TypeStrong / ts-node

TypeScript execution and REPL for node.js
https://typestrong.org/ts-node
MIT License
12.84k stars 534 forks source link

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

Open a-x- opened 1 year ago

a-x- commented 1 year ago

Search Terms

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

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.

server/tsconfig.json ```js { "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 ```js { "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) ```js { "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 ```js { "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- commented 1 year ago
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- commented 1 year ago

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- commented 1 year ago

I also found this:

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

a-x- commented 1 year ago

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 commented 1 year ago

@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 commented 1 year ago

This has done the trick for me: https://github.com/TypeStrong/ts-node/discussions/1450#discussioncomment-1806115

PavelGolodoniuc commented 3 weeks ago

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