Skip to content

Commit 476e642

Browse files
committed
feat(types): drop Node 20, require Node >= 22
- Bump engines.node from >=20 to >=22 - Remove Node 20 from CI test matrix - Bump native build Node from 20 to 22 - Replace old JS ESM loader hooks with 2 TypeScript hooks (ts-resolve-loader.ts + ts-resolve-hooks.ts) that resolve .js→.ts import specifiers at runtime - Remove canStripTypes helper and version guards from tests - Update src/index.cjs to import('./index.ts') for dev; build step produces dist/index.cjs with ./index.js for published package - Simplify vitest.config.ts to use --experimental-strip-types only - Update child-process test invocations to use --import loader flag - Rewrite scripts/gen-deps.cjs as ESM TypeScript - Rename scripts/build-wasm.js → .ts Impact: 1 functions changed, 1 affected
1 parent 7d77811 commit 476e642

15 files changed

Lines changed: 94 additions & 70 deletions

.github/workflows/build-native.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
- name: Setup Node.js
6161
uses: actions/setup-node@v6
6262
with:
63-
node-version: 20
63+
node-version: 22
6464

6565
- name: Setup Rust
6666
uses: dtolnay/rust-toolchain@stable

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
fail-fast: false
4444
matrix:
4545
os: [ubuntu-latest, macos-latest, windows-latest]
46-
node-version: [20, 22]
46+
node-version: [22]
4747

4848
runs-on: ${{ matrix.os }}
4949
name: Test Node ${{ matrix.node-version }} (${{ matrix.os }})

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ jobs:
142142
- name: Setup Node.js
143143
uses: actions/setup-node@v6
144144
with:
145-
node-version: 20
145+
node-version: 22
146146

147147
- name: Setup Rust
148148
uses: dtolnay/rust-toolchain@stable

package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,23 @@
2626
"README.md"
2727
],
2828
"engines": {
29-
"node": ">=20"
29+
"node": ">=22"
3030
},
3131
"scripts": {
32-
"build": "tsc",
33-
"build:wasm": "node scripts/build-wasm.js",
32+
"build": "tsc && node -e \"require('fs').writeFileSync('dist/index.cjs',require('fs').readFileSync('src/index.cjs','utf8').replace('./index.ts','./index.js'))\"",
33+
"build:wasm": "node --experimental-strip-types scripts/build-wasm.ts",
3434
"typecheck": "tsc --noEmit",
3535
"verify-imports": "node --experimental-strip-types scripts/verify-imports.ts",
36-
"test": "node scripts/test.js run",
37-
"test:watch": "node scripts/test.js",
38-
"test:coverage": "node scripts/test.js run --coverage",
36+
"test": "vitest run",
37+
"test:watch": "vitest",
38+
"test:coverage": "vitest run --coverage",
3939
"lint": "biome check src/ tests/",
4040
"lint:fix": "biome check --write src/ tests/",
4141
"format": "biome format --write src/ tests/",
4242
"prepack": "npm run build",
4343
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true});require('fs').rmSync('.tsbuildinfo',{force:true})\"",
4444
"prepare": "npm run build:wasm && npm run build && husky && npm run deps:tree",
45-
"deps:tree": "node scripts/gen-deps.cjs",
45+
"deps:tree": "node --experimental-strip-types scripts/gen-deps.ts",
4646
"release": "commit-and-tag-version",
4747
"release:dry-run": "commit-and-tag-version --dry-run",
4848
"version": "node --experimental-strip-types scripts/sync-native-versions.ts && git add package.json crates/codegraph-core/Cargo.toml"
Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
const { execSync } = require('child_process');
2-
const fs = require('fs');
3-
const path = require('path');
1+
#!/usr/bin/env node
2+
import { execSync } from 'node:child_process';
3+
import { mkdirSync, writeFileSync } from 'node:fs';
4+
import { dirname, join } from 'node:path';
45

5-
const outFile = path.join('generated', 'DEPENDENCIES.md');
6-
fs.mkdirSync(path.dirname(outFile), { recursive: true });
6+
const outFile = join('generated', 'DEPENDENCIES.md');
7+
mkdirSync(dirname(outFile), { recursive: true });
78

89
try {
910
const tree = execSync('npm ls --all --omit=dev', { encoding: 'utf8' });
10-
fs.writeFileSync(outFile, '# Dependencies\n\n```\n' + tree + '```\n');
11-
} catch (err) {
11+
writeFileSync(outFile, '# Dependencies\n\n```\n' + tree + '```\n');
12+
} catch (err: any) {
1213
// npm ls exits non-zero on ELSPROBLEMS (version mismatches in optional deps).
1314
// If stdout still has content, write it; otherwise skip silently.
1415
if (err.stdout) {
15-
fs.writeFileSync(
16+
writeFileSync(
1617
outFile,
1718
'# Dependencies\n\n```\n' + err.stdout + '```\n',
1819
);

scripts/ts-resolve-hooks.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* ESM resolve hook — rewrites .js specifiers to .ts when the .js file
3+
* does not exist on disk. Needed because TypeScript's moduleResolution
4+
* "nodenext" convention uses .js extensions in imports, but at runtime
5+
* the source files are .ts.
6+
*
7+
* Loaded via module.register() from ts-resolve-loader.ts.
8+
*/
9+
10+
import { existsSync } from 'node:fs';
11+
import { fileURLToPath } from 'node:url';
12+
13+
export async function resolve(
14+
specifier: string,
15+
context: { parentURL?: string; conditions: string[] },
16+
nextResolve: Function,
17+
): Promise<{ url: string; shortCircuit?: boolean }> {
18+
try {
19+
return await nextResolve(specifier, context);
20+
} catch (err: any) {
21+
// Only attempt .js → .ts fallback for file-relative specifiers
22+
if (err?.code === 'ERR_MODULE_NOT_FOUND' && specifier.endsWith('.js')) {
23+
const tsSpecifier = specifier.slice(0, -3) + '.ts';
24+
return nextResolve(tsSpecifier, context);
25+
}
26+
throw err;
27+
}
28+
}

scripts/ts-resolve-loader.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Registers the .js → .ts ESM resolve hook.
3+
*
4+
* Usage: node --experimental-strip-types --import ./scripts/ts-resolve-loader.ts src/cli.ts
5+
*/
6+
7+
import { register } from 'node:module';
8+
9+
register(new URL('./ts-resolve-hooks.ts', import.meta.url));

src/index.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
// Note: if import() rejects (e.g. missing dependency), the rejected Promise is cached
1414
// by the CJS module system and every subsequent require() call will re-surface the same
1515
// rejection without re-attempting the load.
16-
module.exports = import('./index.js');
16+
module.exports = import('./index.ts');

tests/helpers/node-version.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)