Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@
"typecheck:bench": "pnpm bench:setup && pnpm --filter @codexo/exojs-bench typecheck",
"typecheck:site": "pnpm --filter @codexo/exojs-examples check-ts",
"typecheck:site-scripts": "pnpm --filter @codexo/exojs-examples check-ts:scripts",
"lint": "eslint --max-warnings=0 \"src/**/*.ts\" \"test/**/*.ts\" \"examples/**/*.ts\" \"scripts/**/*.{ts,mts,cts,mjs}\" \"*.config.ts\" \"packages/exojs-*/src/**/*.{ts,tsx}\" \"packages/exojs-*/test/**/*.{ts,tsx}\" \"packages/exojs-bench/competitors/*.ts\" \"packages/create-exo-app/src/**/*.ts\"",
"lint": "tsx ./scripts/lint.ts",
"lint:fast": "pnpm lint --cache --cache-location .cache/eslintcache",
"lint:site": "pnpm --filter @codexo/exojs-examples lint",
"lint:all": "pnpm lint && pnpm lint:site",
Expand Down
62 changes: 62 additions & 0 deletions scripts/lint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/**
* Runs ESLint over the repository in passes, one process each.
*
* The rule set is type-aware, so a pass holds a TypeScript program for every
* file it lints plus ESLint's own AST for each. Linting the engine, its tests,
* the examples, the scripts and every package in ONE process put all of that in
* one heap at once and reached V8's 4 GB ceiling, which fails the push gate with
* an out-of-memory crash rather than a lint error - and does so intermittently,
* depending on what else the machine is doing.
*
* Splitting costs a second startup and one more program build; it does not
* lint anything twice, and each pass's memory is returned when its process
* exits. That is the trade this file exists to make. Raising the heap ceiling
* instead would hide the growth and move the failure onto whichever machine has
* least memory, which for a push gate is the wrong direction.
*
* Arguments are forwarded to every pass, so `pnpm lint --fix` and
* `pnpm lint --cache` behave as they would against a single invocation.
*/
import { spawnSync } from 'node:child_process';
import { pathToFileURL } from 'node:url';

interface LintPass {
/** What the pass covers, for the console. */
readonly name: string;
/** Globs handed to ESLint, quoted by the shell call below. */
readonly globs: readonly string[];
}

export const LINT_PASSES: readonly LintPass[] = [
{ name: 'engine', globs: ['src/**/*.ts', 'test/**/*.ts', 'examples/**/*.ts', 'scripts/**/*.{ts,mts,cts,mjs}', '*.config.ts'] },
{
name: 'packages',
globs: [
'packages/exojs-*/src/**/*.{ts,tsx}',
'packages/exojs-*/test/**/*.{ts,tsx}',
'packages/exojs-bench/competitors/*.ts',
'packages/create-exo-app/src/**/*.ts',
],
},
];

const main = (): void => {
const args = process.argv.slice(2);

for (const pass of LINT_PASSES) {
console.log(`\n=== lint: ${pass.name} ===\n`);

const quoted = pass.globs.map(glob => `"${glob}"`);
// A shell so the eslint shim resolves on Windows as well.
const result = spawnSync(['eslint', '--max-warnings=0', ...quoted, ...args].join(' '), { stdio: 'inherit', shell: true });

if (result.status !== 0) {
console.error(`\nlint: the ${pass.name} pass failed (exit ${result.status ?? 'signal'}).`);
process.exit(result.status ?? 1);
}
}
};

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
main();
}
Loading