diff --git a/examples/custom-webpack/full-cycle-app/angular.json b/examples/custom-webpack/full-cycle-app/angular.json index 058c7c3c85..e3b4c8a090 100644 --- a/examples/custom-webpack/full-cycle-app/angular.json +++ b/examples/custom-webpack/full-cycle-app/angular.json @@ -75,6 +75,14 @@ }, "itwcw": { "indexTransform": "./index-html.transform.ts" + }, + "bundler-resolution-ts": { + "customWebpackConfig": { + "path": "./extra-webpack.config.bundler.ts", + "mergeRules": { + "plugins": "prepend" + } + } } } }, @@ -200,4 +208,4 @@ "typeSeparator": "." } } -} +} \ No newline at end of file diff --git a/examples/custom-webpack/full-cycle-app/extra-webpack.config.bundler.ts b/examples/custom-webpack/full-cycle-app/extra-webpack.config.bundler.ts new file mode 100644 index 0000000000..a53373ca97 --- /dev/null +++ b/examples/custom-webpack/full-cycle-app/extra-webpack.config.bundler.ts @@ -0,0 +1,11 @@ +import { Configuration } from 'webpack'; +// This subpath export only resolves with moduleResolution:bundler (no physical file at this path). +// If the builder overrides moduleResolution to 'node', this import fails with TS2307. +// Regression test for: https://github.com/just-jeb/angular-builders/issues/2025 +// We use an empty type import (`import type {}`) to test pure path resolution without +// depending on any specific exported member name. +import type {} from '@angular/core/primitives/di'; + +export default { + plugins: [], +} as Configuration; diff --git a/packages/common/src/load-module.ts b/packages/common/src/load-module.ts index 5bd6e542bd..e15588233d 100644 --- a/packages/common/src/load-module.ts +++ b/packages/common/src/load-module.ts @@ -22,9 +22,16 @@ const _tsNodeRegister = (() => { project: tsConfig, compilerOptions: { module: 'CommonJS', - moduleResolution: 'node', + // We deliberately do NOT override moduleResolution here. + // The user's tsconfig moduleResolution setting (node, bundler, node16, etc.) is preserved. + // TypeScript allows moduleResolution:bundler with module:CommonJS, so type checking + // works correctly for all moduleResolution values — including 'bundler', which supports + // subpath package exports (e.g. '@angular/core/primitives/di'). + // Overriding moduleResolution to 'node' (as was done previously) was the root cause of + // issue https://github.com/just-jeb/angular-builders/issues/2025: it prevented TypeScript + // from resolving subpath exports, causing TS2307 errors at build time. types: [ - 'node', // NOTE: `node` is added because users scripts can also use pure node's packages as webpack or others + 'node', // NOTE: `node` is added so user configs can use Node.js globals (process, __dirname, etc.) ], }, }); @@ -45,7 +52,6 @@ const _tsNodeRegister = (() => { /** * check for TS node registration * @param file: file name or file directory are allowed - * @todo tsNodeRegistration: require ts-node if file extension is TypeScript */ function tsNodeRegister(file: string = '', tsConfig: string, logger: logging.LoggerApi) { if (file?.endsWith('.ts')) { @@ -90,7 +96,7 @@ export async function loadModule( return require(modulePath); case '.ts': try { - // If it's a TS file then there are 2 cases for exporing an object. + // If it's a TS file then there are 2 cases for exporting an object. // The first one is `export blah`, transpiled into `module.exports = { blah} `. // The second is `export default blah`, transpiled into `{ default: { ... } }`. return require(modulePath).default || require(modulePath); diff --git a/packages/custom-webpack/src/transform-factories.spec.ts b/packages/custom-webpack/src/transform-factories.spec.ts index aa94b4ccec..76037e80a6 100644 --- a/packages/custom-webpack/src/transform-factories.spec.ts +++ b/packages/custom-webpack/src/transform-factories.spec.ts @@ -32,6 +32,13 @@ describe('getTransforms', () => { transforms.webpackConfiguration({}); expect(tsNode.register).toHaveBeenCalledTimes(1); + expect(tsNode.register).toHaveBeenCalledWith({ + project: 'test/tsconfig.test.json', + compilerOptions: { + module: 'CommonJS', + types: ['node'], + }, + }); expect(logger.warn).not.toHaveBeenCalled(); const transforms2 = getTransforms( diff --git a/packages/custom-webpack/tests/integration.js b/packages/custom-webpack/tests/integration.js index c14790ff4b..c16f6a56b0 100644 --- a/packages/custom-webpack/tests/integration.js +++ b/packages/custom-webpack/tests/integration.js @@ -97,4 +97,11 @@ module.exports = [ app: 'examples/custom-webpack/sanity-app-esm', command: 'yarn build-ts -c tsEsm', }, + { + id: 'ts-config-bundler-module-resolution', + name: 'custom-webpack: TS config with moduleResolution:bundler imports', + purpose: 'Builder loads TypeScript webpack config that uses subpath exports requiring moduleResolution:bundler (regression for #2025)', + app: 'examples/custom-webpack/full-cycle-app', + command: 'yarn build -c bundler-resolution-ts', + }, ];