Updated - some workarounds and incantations using the very latest node at time of writing.
- use node 20.6 or above
- don't use nodemon, ts-node esm/loader etc directly, instead use below
- avoid chalk and other ESM only packages
tsconfig
{
"include": [
"./src/**/*"
],
"exclude": [
"**/*.test.*",
"node_modules"
],
"ts-node": {
// Tell ts-node CLI to install the --loader automatically
"esm": true
},
"compilerOptions": {
"target": "ESNext",
"module": "commonjs",
"sourceMap": true,
"outDir": "dist",
"strict": true,
"lib": [
"esnext"
],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}
package.json
do NOT use type: module
"engines": {
"node": ">=20.6.0"
},
"scripts": {
"dev": "node --watch -r ts-node/register src/server.ts",
---- for reference to my future self ----
All my old node/ts projects seem to throw compiler errors as I update node.
Can someone recommend a reference node+typescript boilerplate that works in 2024?
- it should compile (!!)
- it should hot reload in dev mode
- there should be a test lib that works (jest/ava/anything)
- able to
importother packages
Seems reasonable?
I started with this prisma template (they know TS pretty well) https://github.com/prisma/prisma-examples/tree/latest/typescript/rest-express
but it breaks as soon as I add some packages.
for development
ts-node-esm src/index.ts
> ReferenceError: exports is not defined in ES module scope
so remove "type": "module" from the package.json
Error [ERR_REQUIRE_ESM]: require() ...
ts-node src/index.ts
> TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/dc/dev/hacks/kbxt/api/src/index.ts
maybe the node version broke everything?
$ nvm use --lts
Now using node v20.11.1 (npm v10.2.4)
$ ts-node src/index.ts
TypeError: Unknown file extension ".ts" for ...
npm i -g ts-node@latest
changed 20 packages in 391ms
➜ api git:(dc/vidgraph) ✗ ts-node -v
v10.9.1
$ node -v
v20.11.1
$ npx tsc
node_modules/@types/node/globals.d.ts:6:76 - error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?
/// dozens of similar errors
$ npm run build
> [email protected] build
> tsc
node_modules/@types/node/globals.d.ts:6:76 - error TS2792: Cannot find module 'undici-types'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?
ok let's add that to the tsconfig
"moduleResolution": "node",
bingo, ok now we can at least compile.
npm run dev
> [email protected] dev
> ts-node src/index.ts
TypeError: Unknown file extension ".ts" for /Users/dc/dev/hacks/kbxt/api/src/index.ts
I recall some voodoo way to use a loader, let's try that
$ nodemon --exec 'ts-node-esm' src/index.ts
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/dc/dev/hacks/kbxt/api/src/index.ts
says to remove type: module but that doesn't work.
confused.
npm run build
> [email protected] build
> tsc
$ node dist/index.js
(node:57981) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/dc/dev/hacks/kbxt/api/dist/index.js:1
import express from "express";
^^^^^^
OK so put type: module back.
node dist/index.js
node:internal/modules/esm/resolve:264
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/dc/dev/hacks/kbxt/api/dist/utils/Clog' imported from /Users/dc/dev/hacks/kbxt/api/dist/index.js
OK so I can't even run the dist compiled output now.
try adding some
"moduleDirectories": [
"node_modules",
"src"
]
nope.
$ node --loader ts-node/esm dist/index.js
(node:58752) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
node:internal/process/esm_loader:34
internalBinding('errors').triggerUncaughtException(
OH so maybe chalk is one of those militant ESM packages that will only work with JS and screws up typescript projects? Like cuid and a few others.
npm uninstall chalk
npm i [email protected]
OK got it.
So we're at a point in the JS ecosystem where certain packages completely break typescript, and the maintainers are more focused on pure JS and ESM future?
I guess I'll leave this here as a warning.
Is there a better way to figure this out in future? Does the TS team need to basically fork major packages to maintain versions that work with typescript?
OK so ...
$ npm run build
> [email protected] build
> tsc
$ node dist/index.js
(node:59510) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/dc/dev/hacks/kbxt/api/dist/index.js:1
import express from "express";
^^^^^^
SyntaxError: Cannot use import statement outside a module
at internalCompileFunction (node:internal/vm:77:18)
at wrapSafe (node:internal/modules/cjs/loader:1288:20)
at Module._compile (node:internal/modules/cjs/loader:1340:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1435:10)
at Module.load (node:internal/modules/cjs/loader:1207:32)
at Module._load (node:internal/modules/cjs/loader:1023:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:135:12)
at node:internal/main/run_main_module:28:49
Node.js v20.11.1
so add type: module back and try again.
$ npm run build
> [email protected] build
> tsc
$ node dist/index.js
node:internal/modules/esm/resolve:264
throw new ERR_MODULE_NOT_FOUND(
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/dc/dev/hacks/kbxt/api/dist/utils/Clog' imported from /Users/dc/dev/hacks/kbxt/api/dist/index.js
at finalizeResolution (node:internal/modules/esm/resolve:264:11)
at moduleResolve (node:internal/modules/esm/resolve:917:10)
at defaultResolve (node:internal/modules/esm/resolve:1130:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:396:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:365:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:240:38)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39)
at link (node:internal/modules/esm/module_job:84:36) {
code: 'ERR_MODULE_NOT_FOUND',
url: 'file:///Users/dc/dev/hacks/kbxt/api/dist/utils/Clog'
}
Node.js v20.11.1
seems like we're closer but the mirage of working TS is still on the horizon in 2024
python and fast api beckons...
at point of pausing I had
tsconfig.json
{
"compilerOptions": {
"target": "ESNext", //defines what sort of code ts generates
"sourceMap": true,
"outDir": "dist",
"strict": true,
"lib": [
"esnext",
],
"esModuleInterop": true,
"moduleResolution": "node",
}
}
package.json
{
"name": "ts-test",
"version": "1.0.0",
"license": "MIT",
"type": "module",
"scripts": {
"dev": "ts-node src/index.ts",
"build": "tsc"
},
"dependencies": {
"chalk": "^4.1.2",
"express": "4.18.3"
},
"devDependencies": {
"@types/express": "4.17.21",
"@types/node": "20.11.30",
"nodemon": "^3.1.0",
"ts-node": "^10.9.2",
"typescript": "5.4.3"
},
"moduleDirectories": [
"node_modules",
"src"
]
}
Updated - some workarounds and incantations using the very latest node at time of writing.
use node 20.6 or above don't use nodemon, ts-node esm/loader etc directly, instead use below avoid chalk and other ESM only packages tsconfig