ts-node should take into account tsconfig paths in esm mode and keep but transpile imports
Search Terms
- node-ts and tsconfig paths
- tsconfig paths
what I found:
- https://github.com/TypeStrong/ts-node/issues/1948
- https://stackoverflow.com/questions/72549407/how-to-use-tsconfig-paths-with-ts-node
- https://typestrong.org/ts-node/docs/imports
Expected Behavior
Take tsconfig.json paths into account even in esm mode. Keep imports, but transpile paths.
Actual Behavior
imports keeping as is in esm mode. One good thing I have: VSCode have not TS errors in this setup now, it was hard to accomplish it too.
Steps to reproduce the problem
- create tsconfig.json with at least
{ "compilerOptions": { "paths": { "foo": "src/bar.ts" } } } - run
ts-node-esm --project tsconfig.json src/index.ts - write src/index.ts like
import foo from 'foo'and create some src/bar.ts file with default export
Minimal reproduction
will be later
Specifications
- ts-node v10.9.1
- node v18.16.0
- compiler v5.0.4
I have complex monorepo setup with 3 tsconfigs and many other things, I'll show later minimal repo if it really needed in this case... It's vitejs front and ts-node BFF for it I trying to implement now with shared utils.
- Operating system and version: macOS
server/tsconfig.json
{
"compilerOptions": {
"composite": true,
// enable latest features
"lib": ["esnext"],
"module": "esnext",
"target": "esnext",
// if TS 5.x+
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#--moduleresolution-bundler
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"noEmit": true,
"moduleDetection": "force",
// "jsx": "react-jsx", // support JSX
"allowJs": true, // allow importing `.js` from `.ts`
"esModuleInterop": true, // allow default imports for CommonJS modules
// best practices
"strict": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
// monorepo
"paths": {
"@date-fns": ["../shared/utils/date.server.ts"],
"shared/*": ["../shared/*"],
"@front/*": ["../src/*"]
},
"baseUrl": ".",
"plugins": [
{ "name": "typescript-styled-plugin", "validate": false },
// Fix import absolute paths
{ "transform": "typescript-transform-paths", "useRootDirs": true }, // Transform paths in output .js files
{ "transform": "typescript-transform-paths", "useRootDirs": true, "afterDeclarations": true } // Transform paths in output .d.ts files (Include this line if you output declarations files)
],
"rootDirs": [".", "../src", "../shared"],
// other
"types": ["jest"],
"resolveJsonModule": true
},
"ts-node": {
// It is faster to skip typechecking.
// Remove if you want ts-node to do typechecking.
"transpileOnly": true,
"esm": true, // import/export instead of require/module.exports
"experimentalSpecifierResolution": "node", // omit .ts in imports
"require": ["typescript-transform-paths/register", "tsconfig-paths/register"]
},
"include": [
"./**/*",
"./.eslintrc.cjs",
"../jest.config.ts",
// Don't include: "../src/utils/**/*", As it have browser specific, like utils/hooks.ts
"../src/**/types.ts",
"../src/**/config.ts",
"../src/**/*.types.ts",
"../src/**/*.d.ts",
"../shared/**/*"
],
"references": [{ "path": "../shared/tsconfig.json" }, { "path": "../src/tsconfig.json" }]
}
shared/tsconfig.json
{
"compilerOptions": {
"composite": true,
// enable latest features
"lib": ["esnext"],
"module": "esnext",
"target": "esnext",
// if TS 5.x+
"moduleResolution": "bundler",
// "allowImportingTsExtensions": true,
// "noEmit": true,
"moduleDetection": "force",
// "jsx": "react-jsx", // support JSX
"allowJs": true,
"esModuleInterop": true,
// best practices
"strict": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"plugins": [
{ "name": "typescript-styled-plugin", "validate": false },
// Fix import absolute paths
{ "transform": "typescript-transform-paths", "useRootDirs": true }, // Transform paths in output .js files
{ "transform": "typescript-transform-paths", "useRootDirs": true, "afterDeclarations": true } // Transform paths in output .d.ts files (Include this line if you output declarations files)
],
"types": ["jest"],
"resolveJsonModule": true,
"paths": {
"@date-fns": ["./utils/date.server.ts", "./utils/date.client.ts"]
}
},
"ts-node": {
"transpileOnly": true,
"require": ["typescript-transform-paths/register", "tsconfig-paths/register"]
},
"include": ["./**/*"],
"exclude": ["./.eslintrc.cjs"]
}
package.json (root)
{
"name": "rbi-ips-toro-ui",
"private": true,
"type": "module",
"scripts": {
"dev": "yarn install && scripts/dev",
"prod-server": "cd server && yarn run start",
"build": "echo type check... && tsc && echo build front with vite... && vite build",
"lint": "eslint src server shared",
"mocks": "nodemon ./dev-server/devServer.js",
"test": "NODE_OPTIONS=--experimental-vm-modules jest"
},
"dependencies": {
"@vitejs/plugin-react": "^3.1.0"
"date-fns": "^2.29.3",
"date-fns-tz": "^2.0.0",
"lodash-es": "^4.17.21",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-router": "^6.2.1",
"react-router-dom": "^6.2.1",
// some deps are omitted
},
"devDependencies": {
"@jest/globals": "^29.5.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"esbuild": "^0.17.10",
"esbuild-css-modules-plugin": "^2.7.1",
"eslint": "^8.39.0",
"eslint-config-react-app": "^7.0.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-unused-imports": "^2.0.0",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.0.4",
"typescript-plugin-css-modules": "^3.4.0",
"typescript-plugin-styled-components": "^2.0.0",
"typescript-transform-paths": "^3.4.6",
"vite": "^4.1.4",
"vite-jest": "^0.1.4"
// some deps are omitted
}
}
shared/package.json
{
"name": "shared",
"type": "module",
"version": "1.0.0",
"main": "index.js",
"license": "ICS",
"private": true
}
src: package.json and tsconfig.json currently are not interesting as they using for client-side/front
my error: ERR_INVALID_MODULE_SPECIFIER
cd server && yarn run start
$ NODE_OPTIONS='--loader=ts-node/esm --experimental-specifier-resolution=node' ts-node -r tsconfig-paths/register --project tsconfig.json ./index.ts
(node:88652) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
(node:88652) ExperimentalWarning: The Node.js specifier resolution flag is experimental. It could change or be removed at any time.
(node:88671) ExperimentalWarning: The Node.js specifier resolution flag is experimental. It could change or be removed at any time.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/wzhalmq/raif/ips-cortex-collateral-ui-application/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:695
throw new ERR_INVALID_MODULE_SPECIFIER(
^
cd server && yarn run start
$ NODE_OPTIONS='--loader=ts-node/esm --experimental-specifier-resolution=node' ts-node -r tsconfig-paths/register --project tsconfig.json ./index.ts
(node:88652) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
(node:88652) ExperimentalWarning: The Node.js specifier resolution flag is experimental. It could change or be removed at any time.
(node:88671) ExperimentalWarning: The Node.js specifier resolution flag is experimental. It could change or be removed at any time.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/wzhalmq/raif/ips-cortex-collateral-ui-application/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:695
throw new ERR_INVALID_MODULE_SPECIFIER(
^
CustomError: ERR_INVALID_MODULE_SPECIFIER @date-fns is not a valid package name /Users/wzhalmq/raif/ips-cortex-collateral-ui-application/shared/utils/date.ts
More context
I have some esm only deps, some cjs only deps and some universal ones. Also I have some shared utils for node and browser.
I need paths in this case for implementing two date-fns adapter variants: for server and client:
shared/utils/date.server.ts:
export * from "date-fns";
shared/utils/date.client.ts:
export * from "date-fns/esm";
Can I implement it myself? Could you help me with a little advice? Where, and how to fix it in the TS node source code?
I also found this:
- https://github.com/TypeStrong/ts-node#paths-and-baseurl, https://typestrong.org/ts-node/docs/paths
- https://github.com/TypeStrong/ts-node#why-is-this-not-built-in-to-ts-node
I do use tsconfig-paths/register, but looks it does not work (see my tsconfig.json files)
I dig into ts-node and tsconfig-paths and realised:
function register(params) {} in tsconfig-paths had not even called before error:
CustomError: ERR_INVALID_MODULE_SPECIFIER @date-fns is not a valid package name
there is on good thing, registering is works: tsconfig-paths/register.js typescript-transform-paths/register.js
also, I tried remove typescript-transform-paths or swap requirement order
@a-x- I am facing similar issue when I am using paths in tsconfig.json with V8 WDIO.
{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"esModuleInterop": true,
"moduleResolution": "node",
"outDir": "dist",
"paths": {
"@Constants/*": [
"test/constants/*"
],
"@DialogBoxes/*": [
"test/pageobjects/dialogboxes/*"
],
"@EncryptedData/*": [
"test/encryptedData/*"
],
"@Locators/*": [
"test/locators/*"
],
"@Component/*": [
"test/pageobjects/components/*"
],
"@PageObject/*": [
"test/pageobjects/*"
],
"@Reporter/*": [
"customReporters/*"
],
"@TestData/*": [
"test/testdata/*"
],
"@Util/*": [
"test/util/*"
]
},
"resolveJsonModule": true,
"target": "es2022",
"module": "esnext",
"types": [
"node",
"@wdio/globals/types",
"webdriverio/async",
"@wdio/jasmine-framework",
"expect-webdriverio"
],
// "strict": true,
},
"exclude": [
"node_modules"
],
"include": [
"customReporters/",
"test/",
"wdio.conf.ts"
]
}
Package.json
{ "dependencies": { "@rpii/wdio-report-events": "^8.0.2", "axios": "^0.27.2", "chrome-har": "^0.13.0", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-typescript": "^3.5.2", "eslint-plugin-prettier": "^4.0.0", "generate-password": "^1.7.0", "heroku-client": "^3.1.0", "log-timestamp": "^0.3.0", "log4js": "^6.4.6", "node-fetch": "^2.6.7", "prettier": "^2.6.2", "randomstring": "^1.2.2", "request": "^2.88.2", "shelljs": "^0.8.5", "simple-statistics": "^7.8.3", "totp-generator": "^0.0.13", "underscore": "^1.13.4", "url": "^0.11.0", "wdio-junit-reporter": "^0.4.4" }, "devDependencies": { "@types/node-fetch": "^2.6.2", "@typescript-eslint/eslint-plugin": "^5.25.0", "@typescript-eslint/parser": "^5.25.0", "@wdio/cli": "^8.14.4", "@wdio/devtools-service": "^8.14.2", "@wdio/dot-reporter": "^8.14.0", "@wdio/jasmine-framework": "^8.1.3", "@wdio/junit-reporter": "^8.1.2", "@wdio/local-runner": "^8.1.3", "@wdio/reporter": "8.14.0", "@wdio/sauce-service": "^8.1.3", "@wdio/selenium-standalone-service": "^8.1.2", "@wdio/spec-reporter": "^8.1.2", "@wdio/sumologic-reporter": "^8.14.0", "aws-sdk": "^2.1354.0", "eslint": "^8.31.0", "eslint-plugin-wdio": "^7.19.4", "mailgun.js": "^8.0.0", "ts-mixer": "^6.0.1", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", "typescript": "^4.9.4", "wdio-edgedriver-service": "^3.0.3", "wdio-html-nice-reporter": "8.1.0", "wdio-json-reporter": "^3.0.0", "wdio-tesults-service": "^1.2.1", "wdio-video-reporter": "^4.0.3" }, "engines": { "node": "^20.5.0" }, "name": "ui-e2e-tests", "type": "module", "private": true, "scripts": { "lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix", "wdio": "wdio run wdio.conf.ts" }, "version": "0.1.0" }
Getting Error:
[2023-08-08T12:15:07.941Z] 2023-08-08T12:15:07.941Z ERROR @wdio/runner: Error: Cannot find package '@Util/GetCustomerLoginData' imported from ./test/util/LoginUtil.ts [0-0] at packageResolve (./node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:757:9) [0-0] at moduleResolve (./node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:798:18) [0-0] at Object.defaultResolve (./node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:912:11) [0-0] at ./node_modules/ts-node/src/esm.ts:218:35 [0-0] at entrypointFallback (./node_modules/ts-node/src/esm.ts:168:34) [0-0] at ./node_modules/ts-node/src/esm.ts:217:14 [0-0] at addShortCircuitFlag (./node_modules/ts-node/src/esm.ts:409:21) [0-0] at resolve (./node_modules/ts-node/src/esm.ts:197:12) [0-0] at nextResolve (node:internal/modules/esm/hooks:733:28) [0-0] at Hooks.resolve (node:internal/modules/esm/hooks:242:30) [2023-08-08T12:15:07.941Z] [0-0] Error: Cannot find package '@Util/GetCustomerLoginData' imported from ./test/util/LoginUtil.ts
How did you resolve it?
This has done the trick for me: https://github.com/TypeStrong/ts-node/discussions/1450#discussioncomment-1806115