196 lines
5.5 KiB
JavaScript
196 lines
5.5 KiB
JavaScript
#!/usr/bin/env node
|
|
import { build } from 'esbuild'
|
|
import { existsSync, mkdirSync, writeFileSync, cpSync, readFileSync, readdirSync, statSync, chmodSync } from 'node:fs'
|
|
import { join } from 'node:path'
|
|
|
|
const SRC_DIR = 'src'
|
|
const OUT_DIR = 'dist'
|
|
|
|
function collectEntries(dir, acc = []) {
|
|
const items = readdirSync(dir)
|
|
for (const name of items) {
|
|
const p = join(dir, name)
|
|
const st = statSync(p)
|
|
if (st.isDirectory()) {
|
|
// skip tests and storybook or similar folders if any, adjust as needed
|
|
if (name === 'test' || name === '__tests__') continue
|
|
collectEntries(p, acc)
|
|
} else if (st.isFile()) {
|
|
if (p.endsWith('.ts') || p.endsWith('.tsx')) acc.push(p)
|
|
}
|
|
}
|
|
return acc
|
|
}
|
|
|
|
function fixRelativeImports(dir) {
|
|
const items = readdirSync(dir)
|
|
for (const name of items) {
|
|
const p = join(dir, name)
|
|
const st = statSync(p)
|
|
if (st.isDirectory()) {
|
|
fixRelativeImports(p)
|
|
continue
|
|
}
|
|
if (!p.endsWith('.js')) continue
|
|
let text = readFileSync(p, 'utf8')
|
|
// Handle: from '...'
|
|
text = text.replace(/(from\s+['"])(\.{1,2}\/[^'"\n]+)(['"])/gm, (m, a, spec, c) => {
|
|
if (/\.(js|json|node|mjs|cjs)$/.test(spec)) return m
|
|
return a + spec + '.js' + c
|
|
})
|
|
// Handle: export ... from '...'
|
|
text = text.replace(/(export\s+[^;]*?from\s+['"])(\.{1,2}\/[^'"\n]+)(['"])/gm, (m, a, spec, c) => {
|
|
if (/\.(js|json|node|mjs|cjs)$/.test(spec)) return m
|
|
return a + spec + '.js' + c
|
|
})
|
|
// Handle: dynamic import('...')
|
|
text = text.replace(/(import\(\s*['"])(\.{1,2}\/[^'"\n]+)(['"]\s*\))/gm, (m, a, spec, c) => {
|
|
if (/\.(js|json|node|mjs|cjs)$/.test(spec)) return m
|
|
return a + spec + '.js' + c
|
|
})
|
|
writeFileSync(p, text)
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
console.log('🚀 Building Kode CLI for cross-platform compatibility...')
|
|
|
|
if (!existsSync(OUT_DIR)) mkdirSync(OUT_DIR, { recursive: true })
|
|
|
|
const entries = collectEntries(SRC_DIR)
|
|
|
|
// Build ESM format but ensure Node.js compatibility
|
|
await build({
|
|
entryPoints: entries,
|
|
outdir: OUT_DIR,
|
|
outbase: SRC_DIR,
|
|
bundle: false,
|
|
platform: 'node',
|
|
format: 'esm',
|
|
target: ['node20'],
|
|
sourcemap: true,
|
|
legalComments: 'none',
|
|
logLevel: 'info',
|
|
tsconfig: 'tsconfig.json',
|
|
})
|
|
|
|
// Fix relative import specifiers to include .js extension for ESM
|
|
fixRelativeImports(OUT_DIR)
|
|
|
|
// Mark dist as ES module
|
|
writeFileSync(join(OUT_DIR, 'package.json'), JSON.stringify({
|
|
type: 'module',
|
|
main: './entrypoints/cli.js'
|
|
}, null, 2))
|
|
|
|
// Create a proper entrypoint - ESM with async handling
|
|
const mainEntrypoint = join(OUT_DIR, 'index.js')
|
|
writeFileSync(mainEntrypoint, `#!/usr/bin/env node
|
|
import('./entrypoints/cli.js').catch(err => {
|
|
console.error('❌ Failed to load CLI:', err.message);
|
|
process.exit(1);
|
|
});
|
|
`)
|
|
|
|
// Copy yoga.wasm alongside outputs
|
|
try {
|
|
cpSync('yoga.wasm', join(OUT_DIR, 'yoga.wasm'))
|
|
console.log('✅ yoga.wasm copied to dist')
|
|
} catch (err) {
|
|
console.warn('⚠️ Could not copy yoga.wasm:', err.message)
|
|
}
|
|
|
|
// Create cross-platform CLI wrapper
|
|
const cliWrapper = `#!/usr/bin/env node
|
|
|
|
// Cross-platform CLI wrapper for Kode
|
|
// Prefers Bun but falls back to Node.js with tsx loader
|
|
|
|
const { spawn } = require('child_process');
|
|
const { existsSync } = require('fs');
|
|
const path = require('path');
|
|
|
|
// Get the directory where this CLI script is installed
|
|
const kodeDir = __dirname;
|
|
const distPath = path.join(kodeDir, 'dist', 'index.js');
|
|
|
|
// Check if we have a built version
|
|
if (!existsSync(distPath)) {
|
|
console.error('❌ Built files not found. Run "bun run build" first.');
|
|
process.exit(1);
|
|
}
|
|
|
|
// Try to use Bun first, then fallback to Node.js with tsx
|
|
const runWithBun = () => {
|
|
const proc = spawn('bun', ['run', distPath, ...process.argv.slice(2)], {
|
|
stdio: 'inherit',
|
|
cwd: process.cwd() // Use current working directory, not kode installation directory
|
|
});
|
|
|
|
proc.on('error', (err) => {
|
|
if (err.code === 'ENOENT') {
|
|
// Bun not found, try Node.js
|
|
runWithNode();
|
|
} else {
|
|
console.error('❌ Failed to start with Bun:', err.message);
|
|
process.exit(1);
|
|
}
|
|
});
|
|
|
|
proc.on('close', (code) => {
|
|
process.exit(code);
|
|
});
|
|
};
|
|
|
|
const runWithNode = () => {
|
|
const proc = spawn('node', [distPath, ...process.argv.slice(2)], {
|
|
stdio: 'inherit',
|
|
cwd: process.cwd() // Use current working directory, not kode installation directory
|
|
});
|
|
|
|
proc.on('error', (err) => {
|
|
console.error('❌ Failed to start with Node.js:', err.message);
|
|
process.exit(1);
|
|
});
|
|
|
|
proc.on('close', (code) => {
|
|
process.exit(code);
|
|
});
|
|
};
|
|
|
|
// Start with Bun preference
|
|
runWithBun();
|
|
`;
|
|
|
|
writeFileSync('cli.js', cliWrapper);
|
|
|
|
// Make cli.js executable
|
|
try {
|
|
chmodSync('cli.js', 0o755);
|
|
console.log('✅ cli.js made executable');
|
|
} catch (err) {
|
|
console.warn('⚠️ Could not make cli.js executable:', err.message);
|
|
}
|
|
|
|
// Create .npmrc file
|
|
const npmrcContent = `# Kode npm configuration
|
|
package-lock=false
|
|
save-exact=true
|
|
`;
|
|
|
|
writeFileSync('.npmrc', npmrcContent);
|
|
|
|
console.log('✅ Build completed for cross-platform compatibility!')
|
|
console.log('📋 Generated files:')
|
|
console.log(' - dist/ (ESM modules)')
|
|
console.log(' - dist/index.js (main entrypoint)')
|
|
console.log(' - dist/entrypoints/cli.js (CLI main)')
|
|
console.log(' - cli.js (cross-platform wrapper)')
|
|
console.log(' - .npmrc (npm configuration)')
|
|
}
|
|
|
|
main().catch(err => {
|
|
console.error('❌ Build failed:', err)
|
|
process.exit(1)
|
|
})
|