fix(core): avoid migration error when non-existent symbol is imported (#36367)
In rare cases a project with configured `rootDirs` that has imports to non-existent identifiers could fail in the migration. This happens because based on the application code, the migration could end up trying to resolve the `ts.Symbol` of such non-existent identifiers. This isn't a problem usually, but due to a upstream bug in the TypeScript compiler, a runtime error is thrown. This is because TypeScript is unable to compute a relative path from the originating source file to the imported source file which _should_ provide the non-existent identifier. An issue for this has been reported upstream: https://github.com/microsoft/TypeScript/issues/37731. The issue only surfaces since our migrations don't provide an absolute base path that is used for resolving the root directories. To fix this, we ensure that we never use relative paths when parsing tsconfig files. More details can be found in the TS issue. Fixes #36346. PR Close #36367
This commit is contained in:

committed by
Kara Erickson

parent
56af303dc3
commit
dff52ecb11
@ -6,12 +6,39 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {Tree} from '@angular-devkit/schematics';
|
||||
import {relative} from 'path';
|
||||
import {dirname, relative, resolve} from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import {parseTsconfigFile} from './parse_tsconfig';
|
||||
|
||||
export type FakeReadFileFn = (fileName: string) => string|null;
|
||||
|
||||
/**
|
||||
* Creates a TypeScript program instance for a TypeScript project within
|
||||
* the virtual file system tree.
|
||||
* @param tree Virtual file system tree that contains the source files.
|
||||
* @param tsconfigPath Virtual file system path that resolves to the TypeScript project.
|
||||
* @param basePath Base path for the virtual file system tree.
|
||||
* @param fakeFileRead Optional file reader function. Can be used to overwrite files in
|
||||
* the TypeScript program, or to add in-memory files (e.g. to add global types).
|
||||
* @param additionalFiles Additional file paths that should be added to the program.
|
||||
*/
|
||||
export function createMigrationProgram(
|
||||
tree: Tree, tsconfigPath: string, basePath: string, fakeFileRead?: FakeReadFileFn,
|
||||
additionalFiles?: string[]) {
|
||||
// Resolve the tsconfig path to an absolute path. This is needed as TypeScript otherwise
|
||||
// is not able to resolve root directories in the given tsconfig. More details can be found
|
||||
// in the following issue: https://github.com/microsoft/TypeScript/issues/37731.
|
||||
tsconfigPath = resolve(basePath, tsconfigPath);
|
||||
const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath));
|
||||
const host = createMigrationCompilerHost(tree, parsed.options, basePath, fakeFileRead);
|
||||
const program =
|
||||
ts.createProgram(parsed.fileNames.concat(additionalFiles || []), parsed.options, host);
|
||||
return {parsed, host, program};
|
||||
}
|
||||
|
||||
export function createMigrationCompilerHost(
|
||||
tree: Tree, options: ts.CompilerOptions, basePath: string,
|
||||
fakeRead?: (fileName: string) => string | null): ts.CompilerHost {
|
||||
fakeRead?: FakeReadFileFn): ts.CompilerHost {
|
||||
const host = ts.createCompilerHost(options, true);
|
||||
|
||||
// We need to overwrite the host "readFile" method, as we want the TypeScript
|
||||
|
@ -6,6 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as path from 'path';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
export function parseTsconfigFile(tsconfigPath: string, basePath: string): ts.ParsedCommandLine {
|
||||
@ -17,5 +18,12 @@ export function parseTsconfigFile(tsconfigPath: string, basePath: string): ts.Pa
|
||||
readFile: ts.sys.readFile,
|
||||
};
|
||||
|
||||
// Throw if incorrect arguments are passed to this function. Passing relative base paths
|
||||
// results in root directories not being resolved and in later type checking runtime errors.
|
||||
// More details can be found here: https://github.com/microsoft/TypeScript/issues/37731.
|
||||
if (!path.isAbsolute(basePath)) {
|
||||
throw Error('Unexpected relative base path has been specified.');
|
||||
}
|
||||
|
||||
return ts.parseJsonConfigFileContent(config, parseConfigHost, basePath, {});
|
||||
}
|
||||
|
Reference in New Issue
Block a user