Skip to content

Commit e3b8010

Browse files
petebacondarwinatscott
authored andcommitted
fix(ngcc): report a warning if ngcc tries to use a solution-style tsconfig (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
1 parent 6626739 commit e3b8010

File tree

3 files changed

+73
-1
lines changed

3 files changed

+73
-1
lines changed

packages/compiler-cli/ngcc/src/ngcc_options.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,8 @@ export function getSharedSetup(options: NgccOptions): SharedSetup&RequiredNgccOp
187187
errorOnFailedEntryPoint = true;
188188
}
189189

190+
checkForSolutionStyleTsConfig(fileSystem, logger, projectPath, options.tsConfigPath, tsConfig);
191+
190192
return {
191193
basePath,
192194
targetEntryPointPath,
@@ -234,3 +236,22 @@ export function clearTsConfigCache() {
234236
tsConfigPathCache = null;
235237
tsConfigCache = null;
236238
}
239+
240+
function checkForSolutionStyleTsConfig(
241+
fileSystem: FileSystem, logger: Logger, projectPath: AbsoluteFsPath,
242+
tsConfigPath: string|null|undefined, tsConfig: ParsedConfiguration|null): void {
243+
if (tsConfigPath !== null && !tsConfigPath && tsConfig !== null &&
244+
tsConfig.rootNames.length === 0 && tsConfig.projectReferences !== undefined &&
245+
tsConfig.projectReferences.length > 0) {
246+
logger.warn(
247+
`The inferred tsconfig file "${tsConfig.project}" appears to be "solution-style" ` +
248+
`since it contains no root files but does contain project references.\n` +
249+
`This is probably not wanted, since ngcc is unable to infer settings like "paths" mappings from such a file.\n` +
250+
`Perhaps you should have explicitly specified one of the referenced projects using the --tsconfig option. For example:\n\n` +
251+
tsConfig.projectReferences.map(ref => ` ngcc ... --tsconfig "${ref.originalPath}"\n`)
252+
.join('') +
253+
`\nFind out more about solution-style tsconfig at https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig.\n` +
254+
`If you did intend to use this file, then you can hide this warning by providing it explicitly:\n\n` +
255+
` ngcc ... --tsconfig "${fileSystem.relative(projectPath, tsConfig.project)}"`);
256+
}
257+
}

packages/compiler-cli/ngcc/test/ngcc_options_spec.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,48 @@ runInEachFileSystem(() => {
5656
expect(setup.tsConfigPath).toBe(null);
5757
expect(setup.tsConfig).toBe(null);
5858
});
59+
60+
it('should warn about a solution-style tsconfig if the tsConfigPath is inferred', () => {
61+
fs.writeFile(fs.resolve(projectPath, 'tsconfig.app.json'), '{"files": ["src/index.ts"]}');
62+
fs.writeFile(fs.resolve(projectPath, 'tsconfig.test.json'), '{"files": ["src/test.ts"]}');
63+
fs.writeFile(pathToProjectTsConfig, JSON.stringify({
64+
'files': [],
65+
'references': [
66+
{'path': 'tsconfig.app.json'},
67+
{'path': 'tsconfig.test.json'},
68+
]
69+
}));
70+
const setup = getSharedSetup({...createOptions()});
71+
expect(setup.tsConfigPath).toBeUndefined();
72+
expect(setup.tsConfig?.rootNames).toEqual([]);
73+
expect((setup.logger as MockLogger).logs.warn).toEqual([[
74+
`The inferred tsconfig file "${
75+
pathToProjectTsConfig}" appears to be "solution-style" since it contains no root files but does contain project references.\n` +
76+
`This is probably not wanted, since ngcc is unable to infer settings like "paths" mappings from such a file.\n` +
77+
`Perhaps you should have explicitly specified one of the referenced projects using the --tsconfig option. For example:\n\n` +
78+
` ngcc ... --tsconfig "tsconfig.app.json"\n` +
79+
` ngcc ... --tsconfig "tsconfig.test.json"\n` +
80+
`\nFind out more about solution-style tsconfig at https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig.\n` +
81+
`If you did intend to use this file, then you can hide this warning by providing it explicitly:\n\n` +
82+
` ngcc ... --tsconfig "tsconfig.json"`
83+
]]);
84+
});
85+
86+
it('should not warn about a solution-style tsconfig if the tsConfigPath is explicit', () => {
87+
fs.writeFile(fs.resolve(projectPath, 'tsconfig.app.json'), '{"files": ["src/index.ts"]}');
88+
fs.writeFile(fs.resolve(projectPath, 'tsconfig.test.json'), '{"files": ["src/test.ts"]}');
89+
fs.writeFile(pathToProjectTsConfig, JSON.stringify({
90+
'files': [],
91+
'references': [
92+
{'path': 'tsconfig.app.json'},
93+
{'path': 'tsconfig.test.json'},
94+
]
95+
}));
96+
const setup = getSharedSetup({...createOptions(), tsConfigPath: pathToProjectTsConfig});
97+
expect(setup.tsConfigPath).toEqual(pathToProjectTsConfig);
98+
expect(setup.tsConfig?.rootNames).toEqual([]);
99+
expect((setup.logger as MockLogger).logs.warn).toEqual([]);
100+
});
59101
});
60102

61103
/**

packages/compiler-cli/src/perform_compile.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export interface ParsedConfiguration {
112112
project: string;
113113
options: api.CompilerOptions;
114114
rootNames: string[];
115+
projectReferences?: readonly ts.ProjectReference[]|undefined;
115116
emitFlags: api.EmitFlags;
116117
errors: Diagnostics;
117118
}
@@ -197,6 +198,7 @@ export function readConfiguration(
197198
const parsed = ts.parseJsonConfigFileContent(
198199
config, parseConfigHost, basePath, existingOptions, configFileName);
199200
const rootNames = parsed.fileNames;
201+
const projectReferences = parsed.projectReferences;
200202

201203
const options = createNgCompilerOptions(basePath, config, parsed.options);
202204
let emitFlags = api.EmitFlags.Default;
@@ -206,7 +208,14 @@ export function readConfiguration(
206208
if (options.skipTemplateCodegen) {
207209
emitFlags = emitFlags & ~api.EmitFlags.Codegen;
208210
}
209-
return {project: projectFile, rootNames, options, errors: parsed.errors, emitFlags};
211+
return {
212+
project: projectFile,
213+
rootNames,
214+
projectReferences,
215+
options,
216+
errors: parsed.errors,
217+
emitFlags
218+
};
210219
} catch (e) {
211220
const errors: Diagnostics = [{
212221
category: ts.DiagnosticCategory.Error,

0 commit comments

Comments
 (0)