diff --git a/packages/compiler-cli/ngcc/src/ngcc_options.ts b/packages/compiler-cli/ngcc/src/ngcc_options.ts index abb3578ef6793..7ef38ec650f80 100644 --- a/packages/compiler-cli/ngcc/src/ngcc_options.ts +++ b/packages/compiler-cli/ngcc/src/ngcc_options.ts @@ -186,6 +186,8 @@ export function getSharedSetup(options: NgccOptions): SharedSetup&RequiredNgccOp errorOnFailedEntryPoint = true; } + checkForSolutionStyleTsConfig(fileSystem, logger, projectPath, options.tsConfigPath, tsConfig); + return { basePath, targetEntryPointPath, @@ -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)}"`); + } +} diff --git a/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts b/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts index f044799d9a90f..56fa85b7eb3a7 100644 --- a/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts +++ b/packages/compiler-cli/ngcc/test/ngcc_options_spec.ts @@ -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([]); + }); }); /** diff --git a/packages/compiler-cli/src/perform_compile.ts b/packages/compiler-cli/src/perform_compile.ts index 754ca7f2e3f22..1fc1a42c970d1 100644 --- a/packages/compiler-cli/src/perform_compile.ts +++ b/packages/compiler-cli/src/perform_compile.ts @@ -112,6 +112,7 @@ export interface ParsedConfiguration { project: string; options: api.CompilerOptions; rootNames: string[]; + projectReferences?: readonly ts.ProjectReference[]|undefined; emitFlags: api.EmitFlags; errors: Diagnostics; } @@ -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; @@ -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,