diff --git a/modules/testing/builder/src/dev_prod_mode.ts b/modules/testing/builder/src/dev_prod_mode.ts index d9bcbb98a845..520de82d771b 100644 --- a/modules/testing/builder/src/dev_prod_mode.ts +++ b/modules/testing/builder/src/dev_prod_mode.ts @@ -16,12 +16,14 @@ export async function setupConditionImport(harness: BuilderHarness) { // Files that can be used as targets for the conditional import. await harness.writeFile('src/good.ts', `export const VALUE = 'good-value';`); await harness.writeFile('src/bad.ts', `export const VALUE = 'bad-value';`); + await harness.writeFile('src/wrong.ts', `export const VALUE = 1;`); // Simple application file that accesses conditional code. await harness.writeFile( 'src/main.ts', `import {VALUE} from '#target'; console.log(VALUE); +console.log(VALUE.length); export default 42 as any; `, ); @@ -29,7 +31,7 @@ export default 42 as any; // Ensure that good/bad can be resolved from tsconfig. const tsconfig = JSON.parse(harness.readFile('src/tsconfig.app.json')) as TypeScriptConfig; tsconfig.compilerOptions.moduleResolution = 'bundler'; - tsconfig.files.push('good.ts', 'bad.ts'); + tsconfig.files.push('good.ts', 'bad.ts', 'wrong.ts'); await harness.writeFile('src/tsconfig.app.json', JSON.stringify(tsconfig)); } diff --git a/packages/angular/build/src/builders/application/tests/behavior/build-conditions_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/build-conditions_spec.ts index 887956661c89..949588aa9d1e 100644 --- a/packages/angular/build/src/builders/application/tests/behavior/build-conditions_spec.ts +++ b/packages/angular/build/src/builders/application/tests/behavior/build-conditions_spec.ts @@ -91,5 +91,26 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { }); }); } + + it('fails type-checking when import contains differing type', async () => { + await setTargetMapping(harness, { + 'development': './src/wrong.ts', + 'default': './src/good.ts', + }); + + harness.useTarget('build', { + ...BASE_OPTIONS, + optimization: false, + }); + + const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false }); + + expect(result?.success).toBeFalse(); + expect(logs).toContain( + jasmine.objectContaining({ + message: jasmine.stringMatching('TS2339'), + }), + ); + }); }); }); diff --git a/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts b/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts index d0e326dae65c..16c4b01fd459 100644 --- a/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts +++ b/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts @@ -286,7 +286,12 @@ export function createCompilerPlugin( const initializationResult = await compilation.initialize( pluginOptions.tsconfig, hostOptions, - createCompilerOptionsTransformer(setupWarnings, pluginOptions, preserveSymlinks), + createCompilerOptionsTransformer( + setupWarnings, + pluginOptions, + preserveSymlinks, + build.initialOptions.conditions, + ), ); shouldTsIgnoreJs = !initializationResult.compilerOptions.allowJs; // Isolated modules option ensures safe non-TypeScript transpilation. @@ -572,6 +577,7 @@ function createCompilerOptionsTransformer( setupWarnings: PartialMessage[] | undefined, pluginOptions: CompilerPluginOptions, preserveSymlinks: boolean | undefined, + customConditions: string[] | undefined, ): Parameters[2] { return (compilerOptions) => { // target of 9 is ES2022 (using the number avoids an expensive import of typescript just for an enum) @@ -631,6 +637,12 @@ function createCompilerOptionsTransformer( }); } + // Synchronize custom resolve conditions. + // Set if using the supported bundler resolution mode (bundler is the default in new projects) + if (compilerOptions.moduleResolution === 100 /* ModuleResolutionKind.Bundler */) { + compilerOptions.customConditions = customConditions; + } + return { ...compilerOptions, noEmitOnError: false,