Skip to content

Commit

Permalink
fix(ngcc): report a warning if ngcc tries to use a solution-style tsc…
Browse files Browse the repository at this point in the history
…onfig (angular#38003)

In CLI v10 there was a move to use the new solution-style tsconfig
which became available in TS 3.9.

The result of this is that the standard tsconfig.json no longer contains
important information such as "paths" mappings, which ngcc might need to
correctly compute dependencies.

ngcc (and ngc and tsc) infer the path to tsconfig.json if not given an
explicit tsconfig file-path. But now that means it infers the solution
tsconfig rather than one that contains the useful information it used to
get.

This commit logs a warning in this case to inform the developer
that they might not have meant to load this tsconfig and offer
alternative options.

Fixes angular#36386

PR Close angular#38003
  • Loading branch information
petebacondarwin authored and atscott committed Jul 14, 2020
1 parent f4fac40 commit b358495
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 1 deletion.
21 changes: 21 additions & 0 deletions packages/compiler-cli/ngcc/src/ngcc_options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ export function getSharedSetup(options: NgccOptions): SharedSetup&RequiredNgccOp
errorOnFailedEntryPoint = true;
}

checkForSolutionStyleTsConfig(fileSystem, logger, projectPath, options.tsConfigPath, tsConfig);

return {
basePath,
targetEntryPointPath,
Expand Down Expand Up @@ -233,3 +235,22 @@ export function clearTsConfigCache() {
tsConfigPathCache = null;
tsConfigCache = null;
}

function checkForSolutionStyleTsConfig(
fileSystem: FileSystem, logger: Logger, projectPath: AbsoluteFsPath,
tsConfigPath: string|null|undefined, tsConfig: ParsedConfiguration|null): void {
if (tsConfigPath !== null && !tsConfigPath && tsConfig !== null &&
tsConfig.rootNames.length === 0 && tsConfig.projectReferences !== undefined &&
tsConfig.projectReferences.length > 0) {
logger.warn(
`The inferred tsconfig file "${tsConfig.project}" appears to be "solution-style" ` +
`since it contains no root files but does contain project references.\n` +
`This is probably not wanted, since ngcc is unable to infer settings like "paths" mappings from such a file.\n` +
`Perhaps you should have explicitly specified one of the referenced projects using the --tsconfig option. For example:\n\n` +
tsConfig.projectReferences.map(ref => ` ngcc ... --tsconfig "${ref.originalPath}"\n`)
.join('') +
`\nFind out more about solution-style tsconfig at https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig.\n` +
`If you did intend to use this file, then you can hide this warning by providing it explicitly:\n\n` +
` ngcc ... --tsconfig "${fileSystem.relative(projectPath, tsConfig.project)}"`);
}
}
42 changes: 42 additions & 0 deletions packages/compiler-cli/ngcc/test/ngcc_options_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,48 @@ runInEachFileSystem(() => {
expect(setup.tsConfigPath).toBe(null);
expect(setup.tsConfig).toBe(null);
});

it('should warn about a solution-style tsconfig if the tsConfigPath is inferred', () => {
fs.writeFile(fs.resolve(projectPath, 'tsconfig.app.json'), '{"files": ["src/index.ts"]}');
fs.writeFile(fs.resolve(projectPath, 'tsconfig.test.json'), '{"files": ["src/test.ts"]}');
fs.writeFile(pathToProjectTsConfig, JSON.stringify({
'files': [],
'references': [
{'path': 'tsconfig.app.json'},
{'path': 'tsconfig.test.json'},
]
}));
const setup = getSharedSetup({...createOptions()});
expect(setup.tsConfigPath).toBeUndefined();
expect(setup.tsConfig?.rootNames).toEqual([]);
expect((setup.logger as MockLogger).logs.warn).toEqual([[
`The inferred tsconfig file "${
pathToProjectTsConfig}" appears to be "solution-style" since it contains no root files but does contain project references.\n` +
`This is probably not wanted, since ngcc is unable to infer settings like "paths" mappings from such a file.\n` +
`Perhaps you should have explicitly specified one of the referenced projects using the --tsconfig option. For example:\n\n` +
` ngcc ... --tsconfig "tsconfig.app.json"\n` +
` ngcc ... --tsconfig "tsconfig.test.json"\n` +
`\nFind out more about solution-style tsconfig at https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig.\n` +
`If you did intend to use this file, then you can hide this warning by providing it explicitly:\n\n` +
` ngcc ... --tsconfig "tsconfig.json"`
]]);
});

it('should not warn about a solution-style tsconfig if the tsConfigPath is explicit', () => {
fs.writeFile(fs.resolve(projectPath, 'tsconfig.app.json'), '{"files": ["src/index.ts"]}');
fs.writeFile(fs.resolve(projectPath, 'tsconfig.test.json'), '{"files": ["src/test.ts"]}');
fs.writeFile(pathToProjectTsConfig, JSON.stringify({
'files': [],
'references': [
{'path': 'tsconfig.app.json'},
{'path': 'tsconfig.test.json'},
]
}));
const setup = getSharedSetup({...createOptions(), tsConfigPath: pathToProjectTsConfig});
expect(setup.tsConfigPath).toEqual(pathToProjectTsConfig);
expect(setup.tsConfig?.rootNames).toEqual([]);
expect((setup.logger as MockLogger).logs.warn).toEqual([]);
});
});

/**
Expand Down
11 changes: 10 additions & 1 deletion packages/compiler-cli/src/perform_compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export interface ParsedConfiguration {
project: string;
options: api.CompilerOptions;
rootNames: string[];
projectReferences?: readonly ts.ProjectReference[]|undefined;
emitFlags: api.EmitFlags;
errors: Diagnostics;
}
Expand Down Expand Up @@ -197,6 +198,7 @@ export function readConfiguration(
const parsed = ts.parseJsonConfigFileContent(
config, parseConfigHost, basePath, existingOptions, configFileName);
const rootNames = parsed.fileNames;
const projectReferences = parsed.projectReferences;

const options = createNgCompilerOptions(basePath, config, parsed.options);
let emitFlags = api.EmitFlags.Default;
Expand All @@ -206,7 +208,14 @@ export function readConfiguration(
if (options.skipTemplateCodegen) {
emitFlags = emitFlags & ~api.EmitFlags.Codegen;
}
return {project: projectFile, rootNames, options, errors: parsed.errors, emitFlags};
return {
project: projectFile,
rootNames,
projectReferences,
options,
errors: parsed.errors,
emitFlags
};
} catch (e) {
const errors: Diagnostics = [{
category: ts.DiagnosticCategory.Error,
Expand Down

0 comments on commit b358495

Please sign in to comment.