Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
"dependencies": {
"@babel/parser": "^7.19.4",
"@commander-js/extra-typings": "^14.0.0",
"@sourceacademy/common-cse-machine": "^0.1.0",
"@sourceacademy/conductor": "https://github.com/source-academy/conductor.git#0.5.0",
"@sourceacademy/runner-cse-machine": "^1.0.0",
"@ts-morph/bootstrap": "^0.18.0",
"acorn": "^8.8.2",
"acorn-class-fields": "^1.0.0",
Expand All @@ -45,6 +48,7 @@
"scripts": {
"build": "yarn docs && yarn build:slang",
"build:slang": "tsc --project tsconfig.prod.json",
"build:evaluators": "node scripts/build-evaluators.mjs",
"eslint": "eslint --concurrency=auto src scripts",
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"format:ci": "prettier --list-different \"src/**/*.{ts,tsx}\"",
Expand All @@ -59,6 +63,12 @@
"prepare": "husky"
},
"devDependencies": {
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-replace": "^6.0.3",
"@rollup/plugin-terser": "^1.0.0",
"@types/estree": "^1.0.5",
"@types/lodash": "^4.14.202",
"@types/node": "^24.12.2",
Expand All @@ -67,13 +77,18 @@
"@vitest/ui": "^4.0.4",
"ace-builds": "~1.17.0",
"coveralls": "^3.1.0",
"esbuild": "^0.25.0",
"eslint": "^9.39.1",
"eslint-plugin-import": "^2.32.0",
"globals": "^17.0.0",
"husky": "^9.0.0",
"jsdoc": "4.0.5",
"jsdom": "^29.0.0",
"path-browserify": "^1.0.1",
"prettier": "^3.6.2",
"rollup": "^4.59.0",
"rollup-plugin-esbuild": "^6.2.1",
"rollup-plugin-polyfill-node": "^0.13.0",
"typescript": "^6.0.2",
"typescript-eslint": "^8.46.3",
"vitest": "^4.0.4"
Expand Down
79 changes: 79 additions & 0 deletions rollup.config.evaluator.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import alias from '@rollup/plugin-alias';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import nodeResolve from '@rollup/plugin-node-resolve';
import replace from '@rollup/plugin-replace';
import terser from '@rollup/plugin-terser';
import { fileURLToPath } from 'node:url';
import esbuild from 'rollup-plugin-esbuild';
import nodePolyfills from 'rollup-plugin-polyfill-node';

const EVALUATOR = process.env.EVALUATOR;
if (!EVALUATOR) {
throw new Error('EVALUATOR env var must be set. Use scripts/build-evaluators.mjs.');
}

const shim = name => fileURLToPath(new URL(`./scripts/evaluator-shims/${name}`, import.meta.url));

// js-slang has a few inline `require('<literal>')` calls inside ES modules that
// @rollup/plugin-commonjs cannot rewrite (it skips modules its acorn parser rejects as TS).
// This transform rewrites those static requires into hoisted ESM imports first.
function rewriteStaticRequires(modules) {
const pattern = new RegExp(`require\\((['"])(${modules.join('|')})\\1\\)`, 'g');
return {
name: 'rewrite-static-requires',
transform(code) {
if (!pattern.test(code)) return null;
pattern.lastIndex = 0;
const imports = new Map();
const replaced = code.replace(pattern, (_match, _quote, name) => {
const local = `__req_${name.replace(/[^a-zA-Z0-9]/g, '_')}`;
imports.set(name, local);
return local;
});
const header = [...imports].map(([name, local]) => `import ${local} from '${name}';`).join('\n');
return { code: `${header}\n${replaced}`, map: null };
},
};
}

function plugins() {
return [
rewriteStaticRequires(['acorn-class-fields']),
replace({
preventAssignment: true,
values: { __EVALUATOR__: EVALUATOR },
}),
alias({
entries: [
{ find: 'path', replacement: shim('path.mjs') },
{ find: 'inspector', replacement: shim('empty.mjs') },
],
}),
esbuild({ target: 'es2020', sourceMap: true }),
commonjs({ transformMixedEsModules: true }),
json(),
nodeResolve({ preferBuiltins: false, browser: true }),
nodePolyfills(),
terser({ compress: { dead_code: true, passes: 2 } }),
];
}

export default {
treeshake: { moduleSideEffects: false },
input: 'src/conductor/initialise.ts',
output: {
file: `dist/${EVALUATOR}.js`,
format: 'iife',
name: 'JsSlangWorker',
sourcemap: true,
banner: [
'var global = typeof globalThis !== "undefined" ? globalThis : self;',
'var process = (typeof globalThis !== "undefined" && globalThis.process) || ' +
'{ env: { NODE_ENV: "production" }, argv: [], platform: "browser", version: "", ' +
'versions: {}, nextTick: function (f) { Promise.resolve().then(f); }, ' +
'cwd: function () { return "/"; }, browser: true };',
].join('\n'),
},
plugins: plugins(),
};
28 changes: 28 additions & 0 deletions scripts/build-evaluators.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process';

// Keep in sync with exports in src/conductor/index.ts.
const targets = ['SourceEvaluator1', 'JSCseEvaluator3', 'JSCseEvaluator4'];

function buildTarget(target) {
console.log(`\nBuilding ${target}...\n`);
return new Promise((resolve, reject) => {
const child = spawn(
process.execPath,
['--max-old-space-size=4096', 'node_modules/rollup/dist/bin/rollup', '-c', 'rollup.config.evaluator.mjs'],
{
env: { ...process.env, EVALUATOR: target },
stdio: 'inherit',
shell: false,
},
);
Comment thread
Akshay-2007-1 marked this conversation as resolved.
child.on('close', code => {
if (code === 0) resolve();
else reject(new Error(`Build failed for ${target} (exit ${code})`));
});
});
}

for (const target of targets) {
await buildTarget(target);
}
1 change: 1 addition & 0 deletions scripts/evaluator-shims/empty.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default {};
8 changes: 8 additions & 0 deletions scripts/evaluator-shims/path.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import pathBrowserify from 'path-browserify';

const path = pathBrowserify.default ?? pathBrowserify;

export default path;
export const posix = path.posix ?? path;
export const win32 = path.win32 ?? path;
export const { basename, delimiter, dirname, extname, format, isAbsolute, join, normalize, parse, relative, resolve, sep } = path;
Loading
Loading