Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ts-node should take into account tsconfig paths in esm mode and keep but transpile imports #2023

Open
a-x- opened this issue May 17, 2023 · 7 comments

Comments

@a-x-
Copy link

a-x- commented May 17, 2023

Search Terms

  • node-ts and tsconfig paths
  • tsconfig paths

what I found:

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

  1. create tsconfig.json with at least { "compilerOptions": { "paths": { "foo": "src/bar.ts" } } }
  2. run ts-node-esm --project tsconfig.json src/index.ts
  3. 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

@a-x-
Copy link
Author

a-x- commented May 17, 2023

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";

@a-x-
Copy link
Author

a-x- commented May 18, 2023

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?

@a-x-
Copy link
Author

a-x- commented May 19, 2023

I also found this:

I do use tsconfig-paths/register, but looks it does not work (see my tsconfig.json files)

@a-x-
Copy link
Author

a-x- commented May 20, 2023

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

@aaggarwal-sumo
Copy link

aaggarwal-sumo commented Aug 8, 2023

@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?

@mettini
Copy link

mettini commented Aug 15, 2023

This has done the trick for me: #1450 (comment)

@immotus
Copy link

immotus commented Aug 22, 2024

This helped me and appears to be the recommended solution:
https://typestrong.org/ts-node/docs/paths/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants