fix(compiler-cli): extend `angularCompilerOptions` in tsconfig from node (#40694)
TypeScript supports non rooted extends, we should do the same
b346f5764e/src/compiler/commandLineParser.ts (L2603-L2628)
Closes: #36715
PR Close #40694
This commit is contained in:
parent
719f9ef7ac
commit
5eb195416b
|
@ -9,7 +9,7 @@
|
||||||
import {isSyntaxError, Position} from '@angular/compiler';
|
import {isSyntaxError, Position} from '@angular/compiler';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
import {absoluteFrom, AbsoluteFsPath, getFileSystem, ReadonlyFileSystem, relative, resolve} from '../src/ngtsc/file_system';
|
import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, ReadonlyFileSystem, relative, resolve} from '../src/ngtsc/file_system';
|
||||||
import {NgCompilerOptions} from './ngtsc/core/api';
|
import {NgCompilerOptions} from './ngtsc/core/api';
|
||||||
|
|
||||||
import {replaceTsWithNgInErrors} from './ngtsc/diagnostics';
|
import {replaceTsWithNgInErrors} from './ngtsc/diagnostics';
|
||||||
|
@ -137,6 +137,8 @@ export function readConfiguration(
|
||||||
project: string, existingOptions?: api.CompilerOptions,
|
project: string, existingOptions?: api.CompilerOptions,
|
||||||
host: ConfigurationHost = getFileSystem()): ParsedConfiguration {
|
host: ConfigurationHost = getFileSystem()): ParsedConfiguration {
|
||||||
try {
|
try {
|
||||||
|
const fs = getFileSystem();
|
||||||
|
|
||||||
const readConfigFile = (configFile: string) =>
|
const readConfigFile = (configFile: string) =>
|
||||||
ts.readConfigFile(configFile, file => host.readFile(host.resolve(file)));
|
ts.readConfigFile(configFile, file => host.readFile(host.resolve(file)));
|
||||||
const readAngularCompilerOptions =
|
const readAngularCompilerOptions =
|
||||||
|
@ -150,20 +152,14 @@ export function readConfiguration(
|
||||||
|
|
||||||
// we are only interested into merging 'angularCompilerOptions' as
|
// we are only interested into merging 'angularCompilerOptions' as
|
||||||
// other options like 'compilerOptions' are merged by TS
|
// other options like 'compilerOptions' are merged by TS
|
||||||
let existingNgCompilerOptions: NgCompilerOptions;
|
const existingNgCompilerOptions = {...config.angularCompilerOptions, ...parentOptions};
|
||||||
if (parentOptions && config.angularCompilerOptions) {
|
|
||||||
existingNgCompilerOptions = {...config.angularCompilerOptions, ...parentOptions};
|
|
||||||
} else {
|
|
||||||
existingNgCompilerOptions = parentOptions || config.angularCompilerOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.extends) {
|
if (config.extends && typeof config.extends === 'string') {
|
||||||
let extendedConfigPath = host.resolve(host.dirname(configFile), config.extends);
|
const extendedConfigPath = getExtendedConfigPath(
|
||||||
extendedConfigPath = host.extname(extendedConfigPath) ?
|
configFile, config.extends, host, fs,
|
||||||
extendedConfigPath :
|
);
|
||||||
absoluteFrom(`${extendedConfigPath}.json`);
|
|
||||||
|
|
||||||
if (host.exists(extendedConfigPath)) {
|
if (extendedConfigPath !== null) {
|
||||||
// Call readAngularCompilerOptions recursively to merge NG Compiler options
|
// Call readAngularCompilerOptions recursively to merge NG Compiler options
|
||||||
return readAngularCompilerOptions(extendedConfigPath, existingNgCompilerOptions);
|
return readAngularCompilerOptions(extendedConfigPath, existingNgCompilerOptions);
|
||||||
}
|
}
|
||||||
|
@ -172,13 +168,6 @@ export function readConfiguration(
|
||||||
return existingNgCompilerOptions;
|
return existingNgCompilerOptions;
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseConfigHost = {
|
|
||||||
useCaseSensitiveFileNames: true,
|
|
||||||
fileExists: host.exists.bind(host),
|
|
||||||
readDirectory: ts.sys.readDirectory,
|
|
||||||
readFile: ts.sys.readFile
|
|
||||||
};
|
|
||||||
|
|
||||||
const {projectFile, basePath} = calcProjectFileAndBasePath(project, host);
|
const {projectFile, basePath} = calcProjectFileAndBasePath(project, host);
|
||||||
const configFileName = host.resolve(host.pwd(), projectFile);
|
const configFileName = host.resolve(host.pwd(), projectFile);
|
||||||
const {config, error} = readConfigFile(projectFile);
|
const {config, error} = readConfigFile(projectFile);
|
||||||
|
@ -191,13 +180,14 @@ export function readConfiguration(
|
||||||
emitFlags: api.EmitFlags.Default
|
emitFlags: api.EmitFlags.Default
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const existingCompilerOptions = {
|
const existingCompilerOptions: api.CompilerOptions = {
|
||||||
genDir: basePath,
|
genDir: basePath,
|
||||||
basePath,
|
basePath,
|
||||||
...readAngularCompilerOptions(configFileName),
|
...readAngularCompilerOptions(configFileName),
|
||||||
...existingOptions,
|
...existingOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const parseConfigHost = createParseConfigHost(host, fs);
|
||||||
const {options, errors, fileNames: rootNames, projectReferences} =
|
const {options, errors, fileNames: rootNames, projectReferences} =
|
||||||
ts.parseJsonConfigFileContent(
|
ts.parseJsonConfigFileContent(
|
||||||
config, parseConfigHost, basePath, existingCompilerOptions, configFileName);
|
config, parseConfigHost, basePath, existingCompilerOptions, configFileName);
|
||||||
|
@ -227,6 +217,47 @@ export function readConfiguration(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createParseConfigHost(host: ConfigurationHost, fs = getFileSystem()): ts.ParseConfigHost {
|
||||||
|
return {
|
||||||
|
fileExists: host.exists.bind(host),
|
||||||
|
readDirectory: ts.sys.readDirectory,
|
||||||
|
readFile: host.readFile.bind(host),
|
||||||
|
useCaseSensitiveFileNames: fs.isCaseSensitive(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExtendedConfigPath(
|
||||||
|
configFile: string, extendsValue: string, host: ConfigurationHost,
|
||||||
|
fs: FileSystem): AbsoluteFsPath|null {
|
||||||
|
let extendedConfigPath: AbsoluteFsPath|null = null;
|
||||||
|
|
||||||
|
if (extendsValue.startsWith('.') || fs.isRooted(extendsValue)) {
|
||||||
|
extendedConfigPath = host.resolve(host.dirname(configFile), extendsValue);
|
||||||
|
extendedConfigPath = host.extname(extendedConfigPath) ?
|
||||||
|
extendedConfigPath :
|
||||||
|
absoluteFrom(`${extendedConfigPath}.json`);
|
||||||
|
} else {
|
||||||
|
const parseConfigHost = createParseConfigHost(host, fs);
|
||||||
|
|
||||||
|
// Path isn't a rooted or relative path, resolve like a module.
|
||||||
|
const {
|
||||||
|
resolvedModule,
|
||||||
|
} =
|
||||||
|
ts.nodeModuleNameResolver(
|
||||||
|
extendsValue, configFile,
|
||||||
|
{moduleResolution: ts.ModuleResolutionKind.NodeJs, resolveJsonModule: true},
|
||||||
|
parseConfigHost);
|
||||||
|
if (resolvedModule) {
|
||||||
|
extendedConfigPath = absoluteFrom(resolvedModule.resolvedFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extendedConfigPath !== null && host.exists(extendedConfigPath)) {
|
||||||
|
return extendedConfigPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
export interface PerformCompilationResult {
|
export interface PerformCompilationResult {
|
||||||
diagnostics: Diagnostics;
|
diagnostics: Diagnostics;
|
||||||
program?: api.Program;
|
program?: api.Program;
|
||||||
|
|
|
@ -102,4 +102,38 @@ describe('perform_compile', () => {
|
||||||
annotateForClosureCompiler: false,
|
annotateForClosureCompiler: false,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should merge tsconfig "angularCompilerOptions" when extends point to node package', () => {
|
||||||
|
support.writeFiles({
|
||||||
|
'tsconfig-level-1.json': `{
|
||||||
|
"extends": "@angular-ru/tsconfig",
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"enableIvy": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'node_modules/@angular-ru/tsconfig/tsconfig.json': `{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"skipMetadataEmit": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
'node_modules/@angular-ru/tsconfig/package.json': `{
|
||||||
|
"name": "@angular-ru/tsconfig",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"main": "./tsconfig.json"
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {options} = readConfiguration(path.resolve(basePath, 'tsconfig-level-1.json'));
|
||||||
|
expect(options).toEqual(jasmine.objectContaining({
|
||||||
|
strict: true,
|
||||||
|
skipMetadataEmit: true,
|
||||||
|
enableIvy: false,
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue