fix!: replace unmaintained, insecure ts-node with tsx#6903
fix!: replace unmaintained, insecure ts-node with tsx#6903
ts-node with tsx#6903Conversation
ts-node has unresolvable transitive security warnings and has been unmaintained for ~4 years. `tsx` is a drop-in replacement that uses esbuild for fast TypeScript on-the-fly execution. **For [local Build Plugins](https://docs.netlify.com/extend/develop-and-share/develop-build-plugins/#local-plugins) written in TypeScript (local `.ts` files referenced in `netlify.toml`):** | Behavior | Before (ts-node) | After (tsx) | |----------|------------------|-------------| | Transpilation | ✅ | ✅ | | Type checking at load time | ✅ | ❌ | Plugins with type errors will now load successfully and may fail at runtime instead of at load time. The implicit type-checking was an undocumented feature. It is not consistent with the behaviour of Netlify Functions and Netlify Edge Functions `.ts` files. And relatively very few users use local Build Plugins to begin with, let alone `.ts` ones. `ts-node` only supported CommonJS, so the old code used `require()` to load TS plugins: ```js if (tsNodeService !== undefined) { return require(pluginPath) } `tsx` supports both module systems natively, so we now use `import()` for all plugins (TS or not) and register both loaders: ```js registerESM() // hooks into Node's ESM loader for import() registerCJS() // hooks into Node's CJS loader for nested require() calls ``` Registration API differences: | Aspect | ts-node | tsx | |--------|---------|-----| | `register()` returns | Service object with config access | Unregister function (not needed) | | Config introspection | `tsNodeService.config.raw.compilerOptions` | Not available nor needed | | ESM/CJS handling | CJS only via `require()` | Separate `registerESM()` + `registerCJS()` | The service object was used to enrich error messages with the resolved tsconfig. This is no longer possible with tsx, so `addTsErrorInfo()` has been removed. - Replaced `ts-node` with `tsx` in `@netlify/build` - Removed `ts-node` from `@netlify/api` devDependencies (this was unused) - Replaced a single use of `ts-node` in a `@netlify/build-info` test - Removed `addTsErrorInfo()` and `tsNodeService` parameter threading, as this existed only to augment type-checking error details (no longer a thing at all) - Deleted tests that asserted type-checking behavior
|
This pull request adds or modifies JavaScript ( |
| "supports-color": "^10.0.0", | ||
| "terminal-link": "^4.0.0", | ||
| "ts-node": "^10.9.1", | ||
| "tsx": "^4.21.0", |
There was a problem hiding this comment.
compatible with our >=18.14.0 range: https://github.com/privatenumber/tsx/blob/3a3a0071c78eee94b7c73776729389e38056c21a/package.json#L69-L71
| "supports-color": "^10.0.0", | ||
| "terminal-link": "^4.0.0", | ||
| "ts-node": "^10.9.1", | ||
| "tsx": "^4.21.0", |
There was a problem hiding this comment.
This doesn't add any subdependencies, as we already use esbuild@0.27.2 just like tsx: https://node-modules.dev/grid/depth#install=esbuild@0.27.2+tsx@^4.21.0.
It does add ~600 KB for tsx itself.
But we save ~2 MB from removing ts-node: https://node-modules.dev/chart#selected=ts-node@10.9.2&install=ts-node (note: we won't save the 23 MB from typescript because we depend on it elsewhere).
| }, | ||
| "peerDependenciesMeta": { |
There was a problem hiding this comment.
transitive dep with a CVE
ts-node with tsxts-node with tsx
Summary
ts-nodehas unresolvable transitive security warnings and has been unmaintained for ~4 years.tsxis a drop-in replacement that uses esbuild for fast TypeScript on-the-fly execution.What changes
ts-nodewas only used for a single scenario in production code.For local Build Plugins written in TypeScript (local
.tsfiles referenced innetlify.toml):Plugins with type errors will now load successfully and may fail at runtime instead of at load time. Implicit type-checking was an undocumented feature and is removed in this PR. It is not consistent with the behaviour of Netlify Functions and Netlify Edge Functions
.tsfiles. And relatively very few users use local Build Plugins to begin with, let alone.tsones.Module loading strategy
ts-nodeonly supported CommonJS, so the old code usedrequire()to load TS plugins:tsxsupports both module systems natively, so we now useimport()for all plugins (TS or not) andregister both loaders:
Registration API differences:
register()returns:tsNodeService.config.raw.compilerOptionsrequire()registerESM()+registerCJS()The service object was used to enrich error messages with the resolved tsconfig.
This is no longer possible with tsx, so
addTsErrorInfo()has been removed.Technical changes
ts-nodewithtsxin@netlify/buildts-nodefrom@netlify/apidevDependencies (this was unused)ts-nodein a@netlify/build-infotestaddTsErrorInfo()andtsNodeServiceparameter threading, as this existed only toaugment type-checking error details (no longer a thing at all)
npm audit fixthat resolves aglobsecurity warning