From 2aad91daa155a8ff50ea3cd08e73caed1d4c5e3e Mon Sep 17 00:00:00 2001 From: Dor Shtaif Date: Sat, 22 Jun 2024 23:29:15 +0300 Subject: [PATCH] fix: fix ESM build to contain `import` statements with full file extensions (#49) * fix ESM build import statements full paths with file extensions * set a fixed version of \`pnpm\` to install for the reusable CI setup which is compatible with the Node version 16.14.0 that's used in the meantime * rename the main `tsconfig-esm.json` into a more IDE-friendly `tsconfig.json` --- .github/actions/ci-common-setup/action.yaml | 2 +- .mocharc.yml | 2 ++ package.json | 9 +++--- pnpm-lock.yaml | 36 +++++++++------------ spec/iterified.spec.ts | 10 +++--- spec/iterifiedUnwrapped.spec.ts | 6 ++-- spec/testsGlobalSetup.ts | 25 ++++++++++++++ spec/tsconfig-tests.json | 10 ++++++ src/index.ts | 4 +-- src/iterified.ts | 4 +-- src/iterifiedUnwrapped.ts | 4 +-- src/utils/createMulticastChannel.ts | 4 +-- tsconfig-cjs.json | 9 +++--- tsconfig-esm.json => tsconfig.json | 5 +-- 14 files changed, 82 insertions(+), 48 deletions(-) create mode 100644 spec/testsGlobalSetup.ts create mode 100644 spec/tsconfig-tests.json rename tsconfig-esm.json => tsconfig.json (76%) diff --git a/.github/actions/ci-common-setup/action.yaml b/.github/actions/ci-common-setup/action.yaml index 1c527fb..a98ef97 100644 --- a/.github/actions/ci-common-setup/action.yaml +++ b/.github/actions/ci-common-setup/action.yaml @@ -22,7 +22,7 @@ runs: shell: bash - name: Install pnpm - run: npm install -g pnpm + run: npm install -g pnpm@^8 shell: bash - name: Restore possibly cached dependencies diff --git a/.mocharc.yml b/.mocharc.yml index bf74600..5d46a97 100644 --- a/.mocharc.yml +++ b/.mocharc.yml @@ -1,4 +1,6 @@ spec: ./spec/**/*.spec.ts +file: + - ./spec/testsGlobalSetup.ts watch-files: - ./src/**/*.ts - ./spec/**/*.ts diff --git a/package.json b/package.json index 56f51c2..9f3a491 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "typescript", "esm" ], + "type": "commonjs", "engines": { "node": ">=10.21.0" }, @@ -46,9 +47,9 @@ "dist" ], "scripts": { - "test": "ts-mocha -p ./tsconfig-cjs.json", - "build": "rimraf ./dist && tsc -p tsconfig-esm.json && tsc -p tsconfig-cjs.json && ts-node ./scripts/set-module-type-in-dist-builds.ts", - "check-typings": "tsc --noEmit -p ./tsconfig-esm.json", + "test": "ts-mocha -p ./spec/tsconfig-tests.json", + "build": "rimraf ./dist && tsc -p tsconfig.json && tsc -p tsconfig-cjs.json && ts-node -p tsconfig.json ./scripts/set-module-type-in-dist-builds.ts", + "check-typings": "tsc --noEmit -p ./tsconfig.json", "prepublishOnly": "npm run build" }, "devDependencies": { @@ -61,7 +62,7 @@ "eslint-config-prettier": "^8.9.0", "eslint-config-standard": "^17.1.0", "expect": "^29.6.2", - "mocha": "^10.2.0", + "mocha": "^10.4.0", "prettier": "^3.0.2", "rimraf": "^5.0.1", "sinon": "^15.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31b6ea8..0c4bd97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,8 +33,8 @@ devDependencies: specifier: ^29.6.2 version: 29.6.2 mocha: - specifier: ^10.2.0 - version: 10.2.0 + specifier: ^10.4.0 + version: 10.4.0 prettier: specifier: ^3.0.2 version: 3.0.2 @@ -46,7 +46,7 @@ devDependencies: version: 15.2.0 ts-mocha: specifier: ^10.0.0 - version: 10.0.0(mocha@10.2.0) + version: 10.0.0(mocha@10.4.0) ts-node: specifier: ^10.9.1 version: 10.9.1(@types/node@20.4.5)(typescript@5.2.2) @@ -1361,8 +1361,8 @@ packages: path-scurry: 1.10.1 dev: true - /glob@7.2.0: - resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -1372,15 +1372,16 @@ packages: path-is-absolute: 1.0.1 dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.1.2 + minimatch: 5.0.1 once: 1.4.0 - path-is-absolute: 1.0.1 dev: true /globals@13.20.0: @@ -1847,8 +1848,8 @@ packages: minimist: 1.2.8 dev: true - /mocha@10.2.0: - resolution: {integrity: sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==} + /mocha@10.4.0: + resolution: {integrity: sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==} engines: {node: '>= 14.0.0'} hasBin: true dependencies: @@ -1859,13 +1860,12 @@ packages: diff: 5.0.0 escape-string-regexp: 4.0.0 find-up: 5.0.0 - glob: 7.2.0 + glob: 8.1.0 he: 1.2.0 js-yaml: 4.1.0 log-symbols: 4.1.0 minimatch: 5.0.1 ms: 2.1.3 - nanoid: 3.3.3 serialize-javascript: 6.0.0 strip-json-comments: 3.1.1 supports-color: 8.1.1 @@ -1883,12 +1883,6 @@ packages: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} dev: true - /nanoid@3.3.3: - resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - /natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} dev: true @@ -2355,14 +2349,14 @@ packages: typescript: 5.2.2 dev: true - /ts-mocha@10.0.0(mocha@10.2.0): + /ts-mocha@10.0.0(mocha@10.4.0): resolution: {integrity: sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==} engines: {node: '>= 6.X.X'} hasBin: true peerDependencies: mocha: ^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X dependencies: - mocha: 10.2.0 + mocha: 10.4.0 ts-node: 7.0.1 optionalDependencies: tsconfig-paths: 3.14.2 diff --git a/spec/iterified.spec.ts b/spec/iterified.spec.ts index ae40830..054127f 100644 --- a/spec/iterified.spec.ts +++ b/spec/iterified.spec.ts @@ -1,11 +1,11 @@ import 'mocha'; import expect from 'expect'; import * as sinon from 'sinon'; -import { iterified, type ExecutorFn } from '../src'; -import nextTick from './utils/nextTick'; -import getPromiseState from './utils/getPromiseState'; -import collectAsyncIterable from './utils/collectAsyncIterable'; -import sortPromisesByResolutionOrder from './utils/sortPromisesByResolutionOrder'; +import { iterified, type ExecutorFn } from '../src/index.js'; +import nextTick from './utils/nextTick.js'; +import getPromiseState from './utils/getPromiseState.js'; +import collectAsyncIterable from './utils/collectAsyncIterable.js'; +import sortPromisesByResolutionOrder from './utils/sortPromisesByResolutionOrder.js'; describe('`iterified` function', () => { it('generating a single value and consuming via multiple iterators', async () => { diff --git a/spec/iterifiedUnwrapped.spec.ts b/spec/iterifiedUnwrapped.spec.ts index 322b4f8..c347e7a 100644 --- a/spec/iterifiedUnwrapped.spec.ts +++ b/spec/iterifiedUnwrapped.spec.ts @@ -1,8 +1,8 @@ import 'mocha'; import expect from 'expect'; -import { iterifiedUnwrapped } from '../src'; -import getPromiseState from './utils/getPromiseState'; -import collectAsyncIterable from './utils/collectAsyncIterable'; +import { iterifiedUnwrapped } from '../src/index.js'; +import getPromiseState from './utils/getPromiseState.js'; +import collectAsyncIterable from './utils/collectAsyncIterable.js'; describe('`iterifiedUnwrapped` function', () => { it('generating a single value and consuming via multiple iterators', async () => { diff --git a/spec/testsGlobalSetup.ts b/spec/testsGlobalSetup.ts new file mode 100644 index 0000000..f36c274 --- /dev/null +++ b/spec/testsGlobalSetup.ts @@ -0,0 +1,25 @@ +import mod from 'module'; + +(() => { + /* + It appears that `ts-mocha` errors out on import statements with paths having explicit `.js` extension. + However, the `.js` extensions currently present on all imports across the project CANNOT be omitted for the sake of running the tests + since without them, while TypeScript is able to accept extension-less imports as well (having set `"moduleResolution": "NodeNext"` in `tsconfig.json`), + when fed such imports while compiling to an ESM format, it would output them as-are. ESM code with extension-less imports is + INVALID ESM code, so the build output is actually non-executable even though it would complete without any issue from + the TypeScript compilation. + To work around this (only during test run-time) - we're patching Node's file import mechanism to intercept + every `.js`-ending import path and feed it back to Node as itself-minus-the-`.js` extension. This way, + as long as we've set `"module": "commonjs"` in `tsconfig.json` that accompanies our test runs, we can still write + the entire code-base with "fully-qualified" ESM-compatible imports statements AS WELL AS have Mocha be able to + execute over the same code without issues. + Inspired by https://github.com/ReactiveX/rxjs/blob/6d011f0dc67b736adc0979d3bc14d93b49064efa/spec/support/mocha-path-mappings.js + */ + + const origResolveFilename = (mod as any)._resolveFilename; + + (mod as any)._resolveFilename = function (path: string, ...rest: unknown[]) { + const pathPossiblyPatched = path.endsWith('.js') ? path.slice(0, -3) : path; + return origResolveFilename.call(this, pathPossiblyPatched, ...rest); + }; +})(); diff --git a/spec/tsconfig-tests.json b/spec/tsconfig-tests.json new file mode 100644 index 0000000..6148850 --- /dev/null +++ b/spec/tsconfig-tests.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "include": ["./**/*.ts"], + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "module": "NodeNext", + "moduleResolution": "NodeNext" + } +} diff --git a/src/index.ts b/src/index.ts index 366c9a2..d61d984 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,8 +5,8 @@ import { type ExecutorFn, type TeardownFn, type Iterified, -} from './iterified'; -import { iterifiedUnwrapped, type IterifiedUnwrapped } from './iterifiedUnwrapped'; +} from './iterified.js'; +import { iterifiedUnwrapped, type IterifiedUnwrapped } from './iterifiedUnwrapped.js'; export { iterified, diff --git a/src/iterified.ts b/src/iterified.ts index 593c9fb..3328136 100644 --- a/src/iterified.ts +++ b/src/iterified.ts @@ -1,5 +1,5 @@ -import { createMulticastChannel } from './utils/createMulticastChannel'; -import { type MaybePromise } from './utils/types/MaybePromise'; +import { createMulticastChannel } from './utils/createMulticastChannel.js'; +import { type MaybePromise } from './utils/types/MaybePromise.js'; export { iterified, diff --git a/src/iterifiedUnwrapped.ts b/src/iterifiedUnwrapped.ts index 11a3801..06af268 100644 --- a/src/iterifiedUnwrapped.ts +++ b/src/iterifiedUnwrapped.ts @@ -1,5 +1,5 @@ -import { type IterifiedIterable } from './iterified'; -import { createMulticastChannel } from './utils/createMulticastChannel'; +import { type IterifiedIterable } from './iterified.js'; +import { createMulticastChannel } from './utils/createMulticastChannel.js'; export { iterifiedUnwrapped, type IterifiedUnwrapped }; diff --git a/src/utils/createMulticastChannel.ts b/src/utils/createMulticastChannel.ts index 2d603f1..405d6a5 100644 --- a/src/utils/createMulticastChannel.ts +++ b/src/utils/createMulticastChannel.ts @@ -1,5 +1,5 @@ -import { Deferred } from './createDeferred'; -import { type MaybePromise } from './types/MaybePromise'; +import { Deferred } from './createDeferred.js'; +import { type MaybePromise } from './types/MaybePromise.js'; export { createMulticastChannel, type MulticastChannel, type MulticastChannelIterator }; diff --git a/tsconfig-cjs.json b/tsconfig-cjs.json index 0d1e6af..32f81c7 100644 --- a/tsconfig-cjs.json +++ b/tsconfig-cjs.json @@ -1,9 +1,10 @@ { "$schema": "https://json.schemastore.org/tsconfig", - "files": ["./src/index.ts"], - "extends": "./tsconfig-esm.json", + "include": ["./src/**/*.ts"], + "extends": "./tsconfig.json", "compilerOptions": { "outDir": "./dist/cjs", - "module": "CommonJS" + "module": "CommonJS", + "moduleResolution": "Node10" } -} \ No newline at end of file +} diff --git a/tsconfig-esm.json b/tsconfig.json similarity index 76% rename from tsconfig-esm.json rename to tsconfig.json index 54848e3..6c079ee 100644 --- a/tsconfig-esm.json +++ b/tsconfig.json @@ -1,9 +1,10 @@ { "$schema": "https://json.schemastore.org/tsconfig", - "files": ["./src/index.ts"], + "include": ["./src/**/*.ts"], "compilerOptions": { "outDir": "./dist/esm", - "module": "ESNext", + "module": "NodeNext", + "moduleResolution": "NodeNext", "target": "es2019", "strict": true, "noImplicitAny": true,