From a7a762a5c5a3a809d2b34e2eb033c6a9294be229 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Wed, 16 Sep 2026 13:35:14 -0700 Subject: [PATCH 1/2] build: share API declarations across module formats Emit the npm package declarations once and point both ESM and CommonJS exports at the shared type tree. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cb57f94f-69e9-4501-a84e-7661b191ceba --- api/package.json | 9 +++++---- api/scripts/test-package.cjs | 12 +++++++++--- api/tsconfig.base.json | 2 +- api/tsconfig.types.json | 10 ++++++++++ 4 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 api/tsconfig.types.json diff --git a/api/package.json b/api/package.json index 021da6dcc..ed2270714 100644 --- a/api/package.json +++ b/api/package.json @@ -12,14 +12,14 @@ "Environments" ], "main": "./out/cjs/main.cjs", - "types": "./out/cjs/main.d.ts", + "types": "./out/types/main.d.ts", "exports": { "import": { - "types": "./out/esm/main.d.ts", + "types": "./out/types/main.d.ts", "default": "./out/esm/main.mjs" }, "require": { - "types": "./out/cjs/main.d.ts", + "types": "./out/types/main.d.ts", "default": "./out/cjs/main.cjs" } }, @@ -41,7 +41,8 @@ "prepack": "npm run all:publish", "all:publish": "git clean -xfd . && npm install && npm run copy:sources && npm run compile", "copy:sources": "node ./scripts/copy-sources.cjs", - "compile": "npm run compile:esm && npm run compile:cjs", + "compile": "npm run clean && npm run compile:types && npm run compile:esm && npm run compile:cjs", + "compile:types": "tsc -b ./tsconfig.types.json", "compile:esm": "tsc -b ./tsconfig.esm.json && mve out/esm/main.js out/esm/main.mjs && node -e \"require('fs').writeFileSync('out/esm/package.json', '{\\\"type\\\":\\\"module\\\"}')\"", "compile:cjs": "tsc -b ./tsconfig.cjs.json && mve out/cjs/main.js out/cjs/main.cjs", "clean": "node -e \"const fs = require('fs'); fs.rmSync('./out', { recursive: true, force: true });\"", diff --git a/api/scripts/test-package.cjs b/api/scripts/test-package.cjs index d6aa287e7..642699de0 100644 --- a/api/scripts/test-package.cjs +++ b/api/scripts/test-package.cjs @@ -97,14 +97,14 @@ try { ); const installedPackageJson = JSON.parse(fs.readFileSync(path.join(installedPackageRoot, 'package.json'), 'utf8')); assert.strictEqual(installedPackageJson.main, './out/cjs/main.cjs'); - assert.strictEqual(installedPackageJson.types, './out/cjs/main.d.ts'); + assert.strictEqual(installedPackageJson.types, './out/types/main.d.ts'); assert.deepStrictEqual(installedPackageJson.exports, { import: { - types: './out/esm/main.d.ts', + types: './out/types/main.d.ts', default: './out/esm/main.mjs', }, require: { - types: './out/cjs/main.d.ts', + types: './out/types/main.d.ts', default: './out/cjs/main.cjs', }, }); @@ -119,6 +119,12 @@ try { ]) { assert.ok(fs.statSync(path.resolve(installedPackageRoot, target)).isFile(), `${target} must be a file`); } + for (const duplicateDeclaration of ['./out/esm/main.d.ts', './out/cjs/main.d.ts']) { + assert.ok( + !fs.existsSync(path.resolve(installedPackageRoot, duplicateDeclaration)), + `${duplicateDeclaration} must not duplicate the shared declaration entry point`, + ); + } const requireFromConsumer = createRequire(path.join(testRoot, 'legacy', 'consumer.cjs')); const commonJsModule = requireFromConsumer('@vscode/python-environments'); diff --git a/api/tsconfig.base.json b/api/tsconfig.base.json index 67c9ca0b1..8d00e11f4 100644 --- a/api/tsconfig.base.json +++ b/api/tsconfig.base.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ES2020", "lib": ["ES2020"], - "declaration": true, + "declaration": false, "strict": true, "rootDir": "src", "esModuleInterop": true, diff --git a/api/tsconfig.types.json b/api/tsconfig.types.json new file mode 100644 index 000000000..9f5dcf23a --- /dev/null +++ b/api/tsconfig.types.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": true, + "module": "esnext", + "moduleResolution": "bundler", + "outDir": "./out/types" + } +} From bfb28633384ac6e2381c4676bb3fea98fd481b2f Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Wed, 16 Sep 2026 13:43:38 -0700 Subject: [PATCH 2/2] test: reject declarations in runtime outputs Scan the complete ESM and CommonJS output trees so supporting declaration files cannot be duplicated unnoticed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cb57f94f-69e9-4501-a84e-7661b191ceba --- api/scripts/test-package.cjs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/api/scripts/test-package.cjs b/api/scripts/test-package.cjs index 642699de0..7feb1180d 100644 --- a/api/scripts/test-package.cjs +++ b/api/scripts/test-package.cjs @@ -119,10 +119,15 @@ try { ]) { assert.ok(fs.statSync(path.resolve(installedPackageRoot, target)).isFile(), `${target} must be a file`); } - for (const duplicateDeclaration of ['./out/esm/main.d.ts', './out/cjs/main.d.ts']) { - assert.ok( - !fs.existsSync(path.resolve(installedPackageRoot, duplicateDeclaration)), - `${duplicateDeclaration} must not duplicate the shared declaration entry point`, + for (const runtimeOutput of ['esm', 'cjs']) { + const runtimeOutputRoot = path.join(installedPackageRoot, 'out', runtimeOutput); + const duplicateDeclarations = fs + .readdirSync(runtimeOutputRoot, { recursive: true }) + .filter((entry) => entry.endsWith('.d.ts')); + assert.deepStrictEqual( + duplicateDeclarations, + [], + `${runtimeOutputRoot} must not contain declaration files: ${duplicateDeclarations.join(', ')}`, ); }