From 2066a59deac20015b0cb9d5f8b0e79189def5a2e Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 24 Mar 2026 15:04:28 +0900 Subject: [PATCH 1/6] Support TS v6 --- package.json | 2 +- projects/core/src/actions/check.ts | 11 ++++++++++- projects/core/src/actions/patch.ts | 16 ++++++++++++++-- projects/core/src/module/ts-module.ts | 18 ++++++++++++++++++ projects/core/tsconfig.json | 1 + projects/patch/src/plugin/register-plugin.ts | 2 ++ projects/patch/tsconfig.json | 2 ++ .../projects/package-config/tsconfig.json | 1 + .../assets/projects/path-mapping/tsconfig.json | 1 + .../projects/path-mapping/tsconfig.plugin.json | 1 + test/assets/projects/webpack/tsconfig.json | 1 + test/tsconfig.json | 3 ++- tsconfig.base.json | 1 + yarn.lock | 8 ++++---- 14 files changed, 59 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index dbf158b..89b7ccf 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "ts-node": "^10.9.1", "ts-patch": "^3.3.0", "tsconfig-paths": "^4.2.0", - "typescript": "5.7.2", + "typescript": "~6.0.2", "ts-next": "npm:typescript@beta", "ts-expose-internals": "npm:ts-expose-internals@5.4.5" }, diff --git a/projects/core/src/actions/check.ts b/projects/core/src/actions/check.ts index 0c42821..b681937 100644 --- a/projects/core/src/actions/check.ts +++ b/projects/core/src/actions/check.ts @@ -2,7 +2,7 @@ import { LogLevel, PatchError } from '../system'; import chalk from 'chalk'; import { getTsPackage } from '../ts-package'; import { PatchDetail } from "../patch/patch-detail"; -import { getTsModule } from "../module"; +import { getTsModule, TsModule } from "../module"; import { getInstallerOptions, InstallerOptions } from "../options"; @@ -47,6 +47,15 @@ export function check(moduleNameOrNames?: string | string[], opts?: Partial { + if (!TsModule.isPatchable(m, tsPackage.majorVer)) { + log([ '~', + `${chalk.blueBright(m)} is not independently patchable in TS ${tsPackage.majorVer}+ ` + + `(delegates to ${chalk.blueBright('typescript.js')})` + ]); + return false; + } + return true; + }); + /* Get modules to patch and patch info */ const moduleFiles: [ string, ModuleFile ][] = - targetModuleNames.map(m => [ m, getModuleFile(tsPackage.getModulePath(m)) ]); + patchableModuleNames.map(m => [ m, getModuleFile(tsPackage.getModulePath(m)) ]); /* Determine files not already patched or outdated */ const patchableFiles = moduleFiles.filter(entry => { diff --git a/projects/core/src/module/ts-module.ts b/projects/core/src/module/ts-module.ts index 74d37b0..1a32758 100644 --- a/projects/core/src/module/ts-module.ts +++ b/projects/core/src/module/ts-module.ts @@ -19,6 +19,24 @@ export namespace TsModule { 'tsserver.js': '_tsserver.js' } satisfies Partial>; + /** + * Modules that are thin wrappers (re-export or import-only) in certain TS major versions. + * These delegate to typescript.js and don't need independent patching. + * + * - TS 6+: tsserverlibrary.js re-exports typescript.js; _tsserver.js is ESM-style (not IIFE) + */ + const nonPatchableModulesByMajorVer: Record = { + 6: [ 'tsserverlibrary.js', 'tsserver.js' ] + }; + + export function isPatchable(moduleName: string, majorVer: number): boolean { + for (let ver = majorVer; ver >= 6; ver--) { + const nonPatchable = nonPatchableModulesByMajorVer[ver]; + if (nonPatchable?.includes(moduleName)) return false; + } + return true; + } + export function getContentFileName(moduleName: typeof names[number]): string { return contentFileMap[moduleName] || moduleName; } diff --git a/projects/core/tsconfig.json b/projects/core/tsconfig.json index fba1f7f..c2cc0e5 100644 --- a/projects/core/tsconfig.json +++ b/projects/core/tsconfig.json @@ -8,6 +8,7 @@ "sourceMap" : true, "composite" : true, "declaration" : true, + "types" : [ "node" ], "plugins" : [ { diff --git a/projects/patch/src/plugin/register-plugin.ts b/projects/patch/src/plugin/register-plugin.ts index 5135ed3..4c28fa0 100644 --- a/projects/patch/src/plugin/register-plugin.ts +++ b/projects/patch/src/plugin/register-plugin.ts @@ -108,6 +108,7 @@ namespace tsp { tsNodeInstance = registerConfig.tsNodeInstance; tsNode.register(tsNodeInstance); } else { + const tsMajor = parseInt(tsShim.versionMajorMinor); tsNodeInstance = tsNode.register({ transpileOnly: true, ...(tsConfig ? { project: tsConfig } : { skipProject: true }), @@ -116,6 +117,7 @@ namespace tsp { jsx: 'react', esModuleInterop: true, module: isEsm ? 'ESNext' : 'commonjs', + ...(tsMajor >= 6 ? { ignoreDeprecations: '6.0' } : {}), } }); } diff --git a/projects/patch/tsconfig.json b/projects/patch/tsconfig.json index e6db858..268726c 100644 --- a/projects/patch/tsconfig.json +++ b/projects/patch/tsconfig.json @@ -5,6 +5,7 @@ "compilerOptions": { "outFile": "../../dist/resources/module-patch.js", + "rootDir": "./src", "declaration": true, "types": [ "@types/node" ], @@ -17,6 +18,7 @@ "target": "ES2020", "downlevelIteration": true, "useUnknownInCatchVariables": false, + "ignoreDeprecations": "6.0", "newLine": "LF", "moduleResolution": "Node", "esModuleInterop": true, diff --git a/test/assets/projects/package-config/tsconfig.json b/test/assets/projects/package-config/tsconfig.json index 82d7fd4..4cb8f62 100644 --- a/test/assets/projects/package-config/tsconfig.json +++ b/test/assets/projects/package-config/tsconfig.json @@ -1,6 +1,7 @@ { "include": [ "src" ], "compilerOptions": { + "rootDir": "src", "outDir": "dist", "module": "commonjs", "target": "esnext", diff --git a/test/assets/projects/path-mapping/tsconfig.json b/test/assets/projects/path-mapping/tsconfig.json index 7b8f8b1..10f0812 100644 --- a/test/assets/projects/path-mapping/tsconfig.json +++ b/test/assets/projects/path-mapping/tsconfig.json @@ -3,6 +3,7 @@ "compilerOptions": { "outDir" : "dist", "moduleResolution" : "node", + "ignoreDeprecations": "6.0", "module": "commonjs", "target": "ES2020", "noEmit": false, diff --git a/test/assets/projects/path-mapping/tsconfig.plugin.json b/test/assets/projects/path-mapping/tsconfig.plugin.json index 2773420..9e0a715 100644 --- a/test/assets/projects/path-mapping/tsconfig.plugin.json +++ b/test/assets/projects/path-mapping/tsconfig.plugin.json @@ -1,6 +1,7 @@ { "include": [ "src" ], "compilerOptions": { + "rootDir": "src", "outDir": "dist", "module": "commonjs", "target": "ES2020", diff --git a/test/assets/projects/webpack/tsconfig.json b/test/assets/projects/webpack/tsconfig.json index 0f4aca5..c6600b3 100644 --- a/test/assets/projects/webpack/tsconfig.json +++ b/test/assets/projects/webpack/tsconfig.json @@ -6,6 +6,7 @@ "target": "ESNext", "declaration": false, "moduleResolution" : "Node", + "ignoreDeprecations": "6.0", "plugins": [ { "transform": "./plugin.ts" } ] diff --git a/test/tsconfig.json b/test/tsconfig.json index e82a65c..8fa9e01 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -6,6 +6,7 @@ "noEmit": true, "target": "ESNext", "skipDefaultLibCheck": true, - "skipLibCheck": true + "skipLibCheck": true, + "types": [ "jest", "node" ] } } diff --git a/tsconfig.base.json b/tsconfig.base.json index ca7ceca..124ad48 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -6,6 +6,7 @@ "noImplicitReturns": true, "forceConsistentCasingInFileNames": true, "useUnknownInCatchVariables": false, + "ignoreDeprecations": "6.0", "lib": [ "es2020", "dom" ], "outDir": "dist", diff --git a/yarn.lock b/yarn.lock index 5e8eee2..2de56f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3309,10 +3309,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@5.7.2: - version "5.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" - integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== +typescript@~6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.2.tgz#0b1bfb15f68c64b97032f3d78abbf98bdbba501f" + integrity sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ== uglify-js@^3.1.4: version "3.19.3" From 58531446fac4a7f1b70a4674ef11ca22dea0154a Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 24 Mar 2026 15:15:12 +0900 Subject: [PATCH 2/6] Also fix every test failures --- projects/patch/src/plugin/esm-intercept.ts | 16 +++++++++++++--- test/tests/webpack.test.ts | 12 ++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/projects/patch/src/plugin/esm-intercept.ts b/projects/patch/src/plugin/esm-intercept.ts index 1cfdc16..08a512e 100644 --- a/projects/patch/src/plugin/esm-intercept.ts +++ b/projects/patch/src/plugin/esm-intercept.ts @@ -113,9 +113,19 @@ namespace tsp { /* Add to cache */ Module._cache[resolvedPath] = newModule; - /* Load with ESM library */ - const res = getEsmLibrary()(newModule)(targetFilePath); - newModule.filename = resolvedPath; + /* Load ESM module — try native require first (Node.js 22.12.0+), fall back to esm library */ + let res; + try { + res = originalRequire.call(this, targetFilePath); + newModule.exports = res; + } catch (loadErr: any) { + if (loadErr?.code === 'ERR_REQUIRE_ESM') { + res = getEsmLibrary()(newModule)(targetFilePath); + newModule.filename = resolvedPath; + } else { + throw loadErr; + } + } return res; } diff --git a/test/tests/webpack.test.ts b/test/tests/webpack.test.ts index 453d0c6..276db49 100644 --- a/test/tests/webpack.test.ts +++ b/test/tests/webpack.test.ts @@ -56,8 +56,16 @@ describe('Webpack', () => { expect(err).toContain('Error: ts-patch worked (esm)'); }); - test(`Compiler with ESM transformer throws if no ESM package`, () => { + test(`Compiler with ESM transformer works without esm package on Node 22.12+ or throws if unavailable`, () => { + const [major, minor] = process.versions.node.split('.').map(Number); + const nativeEsmRequire = major > 22 || (major === 22 && minor >= 12); + const err = execAndGetErr(projectPath, './tsconfig.esm.json', 'esm'); - expect(err).toContain('To enable experimental ESM support, install the \'esm\' package'); + if (nativeEsmRequire) { + // Node.js 22.12.0+ can require ESM modules natively — esm package not needed + expect(err).toContain('Error: ts-patch worked (esm)'); + } else { + expect(err).toContain('To enable experimental ESM support, install the \'esm\' package'); + } }); }); From ccb6587757e6f4ff18d434dc387101de565854fc Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 24 Mar 2026 15:36:32 +0900 Subject: [PATCH 3/6] avoid github actions cache problem? --- .github/workflows/build.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7f49261..fd04ed9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,16 +26,6 @@ jobs: with: node-version: ${{ matrix.node-version }} - - name: Cache dependencies - uses: actions/cache@v2 - with: - path: | - ~/.cache/yarn - node_modules - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - name: Install Packages run: yarn install --frozen-lockfile From b96b8d985b950a256925229737ced4366bf9f89a Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 24 Mar 2026 17:43:59 +0900 Subject: [PATCH 4/6] fix bootstrap --- projects/core/tsconfig.json | 3 ++- projects/patch/src/plugin/register-plugin.ts | 5 ++--- projects/patch/tsconfig.json | 3 ++- test/assets/projects/path-mapping/tsconfig.plugin.json | 1 + tsconfig.plugin.json | 9 +++++++++ 5 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 tsconfig.plugin.json diff --git a/projects/core/tsconfig.json b/projects/core/tsconfig.json index c2cc0e5..447b0f9 100644 --- a/projects/core/tsconfig.json +++ b/projects/core/tsconfig.json @@ -13,7 +13,8 @@ "plugins" : [ { "transform" : "./plugin.ts", - "transformProgram" : true + "transformProgram" : true, + "tsConfig" : "../../tsconfig.plugin.json" } ] } diff --git a/projects/patch/src/plugin/register-plugin.ts b/projects/patch/src/plugin/register-plugin.ts index 4c28fa0..3858fc1 100644 --- a/projects/patch/src/plugin/register-plugin.ts +++ b/projects/patch/src/plugin/register-plugin.ts @@ -108,7 +108,6 @@ namespace tsp { tsNodeInstance = registerConfig.tsNodeInstance; tsNode.register(tsNodeInstance); } else { - const tsMajor = parseInt(tsShim.versionMajorMinor); tsNodeInstance = tsNode.register({ transpileOnly: true, ...(tsConfig ? { project: tsConfig } : { skipProject: true }), @@ -116,8 +115,8 @@ namespace tsp { target: isEsm ? 'ESNext' : 'ES2018', jsx: 'react', esModuleInterop: true, - module: isEsm ? 'ESNext' : 'commonjs', - ...(tsMajor >= 6 ? { ignoreDeprecations: '6.0' } : {}), + module: isEsm ? 'ESNext' : 'node16', + moduleResolution: isEsm ? 'bundler' : 'node16', } }); } diff --git a/projects/patch/tsconfig.json b/projects/patch/tsconfig.json index 268726c..4f349ad 100644 --- a/projects/patch/tsconfig.json +++ b/projects/patch/tsconfig.json @@ -27,7 +27,8 @@ { "transform": "./plugin.ts", "transformProgram": true, - "import": "transformProgram" + "import": "transformProgram", + "tsConfig": "../../tsconfig.plugin.json" } ] } diff --git a/test/assets/projects/path-mapping/tsconfig.plugin.json b/test/assets/projects/path-mapping/tsconfig.plugin.json index 9e0a715..2e5c22b 100644 --- a/test/assets/projects/path-mapping/tsconfig.plugin.json +++ b/test/assets/projects/path-mapping/tsconfig.plugin.json @@ -6,6 +6,7 @@ "module": "commonjs", "target": "ES2020", "noEmit": true, + "ignoreDeprecations": "6.0", "baseUrl" : "src", "paths": { "@a/*": [ "./a/*" ], diff --git a/tsconfig.plugin.json b/tsconfig.plugin.json new file mode 100644 index 0000000..c35edd7 --- /dev/null +++ b/tsconfig.plugin.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "moduleResolution": "bundler", + "esModuleInterop": true, + "rootDir": "." + } +} From 368063c3f01b08170442d1e4840f862df83c0d84 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 24 Mar 2026 17:51:15 +0900 Subject: [PATCH 5/6] give up esm detour way --- projects/patch/src/plugin/esm-intercept.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/patch/src/plugin/esm-intercept.ts b/projects/patch/src/plugin/esm-intercept.ts index 08a512e..2484661 100644 --- a/projects/patch/src/plugin/esm-intercept.ts +++ b/projects/patch/src/plugin/esm-intercept.ts @@ -138,4 +138,4 @@ namespace tsp { } // endregion -} +} \ No newline at end of file From d6d08cf90f3aef71cf770230e9eb2f2e6983e099 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Tue, 24 Mar 2026 18:19:40 +0900 Subject: [PATCH 6/6] clean cache for github actions --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fd04ed9..ed30951 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,9 @@ jobs: - name: Build run: yarn build + - name: Clean yarn cache + run: yarn cache clean + - name: Test run: yarn run test && yarn run perf env: