Skip to content

Commit

Permalink
fix(@angular/build): synchronize import/export conditions between bun…
Browse files Browse the repository at this point in the history
…dler and TypeScript

To improve type-checking and reduce hard to diagnose errors when using the new
`production`/`development` conditions, the set of conditions used by the bundler
are now also synchronized with those used by TypeScript. This helps ensure
that type mismatches are avoided by type-checking against the actual file that
will be bundled into the output.
  • Loading branch information
clydin authored and jkrems committed Oct 15, 2024
1 parent e448cf6 commit af52fb4
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 2 deletions.
4 changes: 3 additions & 1 deletion modules/testing/builder/src/dev_prod_mode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@ export async function setupConditionImport(harness: BuilderHarness<unknown>) {
// 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;
`,
);

// 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));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
}),
);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -572,6 +577,7 @@ function createCompilerOptionsTransformer(
setupWarnings: PartialMessage[] | undefined,
pluginOptions: CompilerPluginOptions,
preserveSymlinks: boolean | undefined,
customConditions: string[] | undefined,
): Parameters<AngularCompilation['initialize']>[2] {
return (compilerOptions) => {
// target of 9 is ES2022 (using the number avoids an expensive import of typescript just for an enum)
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit af52fb4

Please sign in to comment.