#!/usr/bin/env -S deno run --allow-read --allow-write --allow-run --allow-env // vim:ft=typescript:ts=4:et /* * Generates bin/rapydscript.mjs — the deno compilation entry point — by scanning * the filesystem for embeddable assets and prepending their declarations * and the __rapydscript_embedded__ global setup to the contents of bin/rapydscript. * * Usage: * deno run --allow-read --allow-write bin/build.ts # generate bin/rapydscript.mjs only * deno run --allow-read --allow-write --allow-run bin/build.ts --compile --output foo # generate + deno compile * deno run --allow-read --allow-write --allow-run bin/build.ts --compile --output foo --target x86_64-unknown-linux-gnu */ import { dirname, fromFileUrl, join, relative } from "jsr:@std/path"; const bin_dir = dirname(fromFileUrl(import.meta.url)); const repo_root = dirname(bin_dir); const dev_dir = join(repo_root, 'dev'); const src_lib_dir = join(repo_root, 'src', 'lib'); const tools_dir = join(repo_root, 'tools'); const out_path = join(bin_dir, 'rapydscript.mjs'); async function exists(p: string): Promise { try { await Deno.stat(p); return true; } catch { return false; } } // --- Scan assets --- const DEV_TEXT_ASSETS = ['compiler.js', 'baselib-plain-pretty.js', 'baselib-plain-ugly.js']; const DEV_JSON_ASSETS = ['stdlib_modules.json']; const found_dev_text: string[] = []; for (const a of DEV_TEXT_ASSETS) { if (await exists(join(dev_dir, a))) found_dev_text.push(a); } const found_dev_json: string[] = []; for (const a of DEV_JSON_ASSETS) { if (await exists(join(dev_dir, a))) found_dev_json.push(a); } const stdlib_files: string[] = []; for await (const entry of Deno.readDir(src_lib_dir)) { if (entry.isFile && entry.name.endsWith('.pyj')) { stdlib_files.push(entry.name); } } stdlib_files.sort(); // --- Build the generated asset section --- function var_name(prefix: string, filename: string): string { return prefix + filename.replace(/[^a-zA-Z0-9]/g, '_'); } const lines: string[] = []; lines.push('// Generated by bin/build.ts — do not edit manually'); lines.push('// vim:ft=javascript:ts=4:et'); lines.push(''); lines.push('// === Embedded assets (generated by bin/build.ts) ==='); lines.push('// Assets are read at startup via Deno.readTextFileSync so they are embedded'); lines.push('// in compiled binaries when the corresponding files are passed to deno compile --include.'); lines.push(''); for (const a of found_dev_text) { lines.push(`const ${var_name('_dev_', a)} = Deno.readTextFileSync(new URL('../dev/${a}', import.meta.url));`); } for (const a of found_dev_json) { lines.push(`const ${var_name('_dev_', a)} = JSON.parse(Deno.readTextFileSync(new URL('../dev/${a}', import.meta.url)));`); } for (const f of stdlib_files) { lines.push(`const ${var_name('_stdlib_', f)} = Deno.readTextFileSync(new URL('../src/lib/${f}', import.meta.url));`); } lines.push(''); lines.push('globalThis.__rapydscript_embedded__ = {'); for (const a of found_dev_text) { lines.push(` '${a}': ${var_name('_dev_', a)},`); } for (const a of found_dev_json) { // strip extension for the key so consumers use e.g. embedded.stdlib_modules const key = a.replace(/\.json$/, '').replace(/-/g, '_'); lines.push(` ${key}: ${var_name('_dev_', a)},`); } lines.push(' stdlib: {'); for (const f of stdlib_files) { lines.push(` '${f}': ${var_name('_stdlib_', f)},`); } lines.push(' },'); // In the compiled binary the tools/ directory is embedded via --include, so the // lint worker and all its transitive imports are accessible at this URL. lines.push(" 'lint-worker-url': new URL('../tools/lint-worker.mjs', import.meta.url),"); lines.push('};'); lines.push(''); lines.push('// === Entry point (from bin/rapydscript) ==='); lines.push(''); // --- Append bin/rapydscript content (strip the shebang line) --- const entry_src = await Deno.readTextFile(join(bin_dir, 'rapydscript')); const entry_lines = entry_src.split('\n'); // Drop the first line if it's the shebang, and the vim modeline comment right after let start = 0; if (entry_lines[0].startsWith('#!')) start = 1; if (start < entry_lines.length && entry_lines[start].trim().startsWith('// vim:')) start++; lines.push(...entry_lines.slice(start)); // Ensure the file ends with a newline const content = lines.join('\n'); const final_content = content.endsWith('\n') ? content : content + '\n'; await Deno.writeTextFile(out_path, final_content); console.log(`Written ${relative(repo_root, out_path)}`); console.log(` ${found_dev_text.length} dev text assets, ${found_dev_json.length} dev json assets, ${stdlib_files.length} stdlib files`); // --- Optionally run deno compile with passed-through args --- const extra_args = Deno.args.slice(); const compile_idx = extra_args.indexOf('--compile'); if (compile_idx !== -1) { extra_args.splice(compile_idx, 1); // remove --compile; remaining args pass through // Build --include list: dev assets, stdlib .pyj files, and the entire tools/ // directory so lint-worker.mjs and its transitive imports are available. const include_flags: string[] = []; for (const a of found_dev_text) include_flags.push('--include', join(dev_dir, a)); for (const a of found_dev_json) include_flags.push('--include', join(dev_dir, a)); for (const f of stdlib_files) include_flags.push('--include', join(src_lib_dir, f)); include_flags.push('--include', tools_dir); const cmd_args = [ 'compile', '--allow-read', '--allow-write', '--allow-run', '--allow-env', '--allow-sys', ...include_flags, ...extra_args, // e.g. --output foo --target x86_64-unknown-linux-gnu out_path, ]; console.log(`Running: deno ${cmd_args.join(' ')}`); const proc = new Deno.Command('deno', { args: cmd_args, stdout: 'inherit', stderr: 'inherit', stdin: 'inherit', cwd: repo_root, }); const result = await proc.output(); Deno.exit(result.code); }