refactor(ivy): implement a virtual file-system layer in ngtsc + ngcc (#30921)
To improve cross platform support, all file access (and path manipulation) is now done through a well known interface (`FileSystem`). For testing a number of `MockFileSystem` implementations are provided. These provide an in-memory file-system which emulates operating systems like OS/X, Unix and Windows. The current file system is always available via the static method, `FileSystem.getFileSystem()`. This is also used by a number of static methods on `AbsoluteFsPath` and `PathSegment`, to avoid having to pass `FileSystem` objects around all the time. The result of this is that one must be careful to ensure that the file-system has been initialized before using any of these static methods. To prevent this happening accidentally the current file system always starts out as an instance of `InvalidFileSystem`, which will throw an error if any of its methods are called. You can set the current file-system by calling `FileSystem.setFileSystem()`. During testing you can call the helper function `initMockFileSystem(os)` which takes a string name of the OS to emulate, and will also monkey-patch aspects of the TypeScript library to ensure that TS is also using the current file-system. Finally there is the `NgtscCompilerHost` to be used for any TypeScript compilation, which uses a given file-system. All tests that interact with the file-system should be tested against each of the mock file-systems. A series of helpers have been provided to support such tests: * `runInEachFileSystem()` - wrap your tests in this helper to run all the wrapped tests in each of the mock file-systems. * `addTestFilesToFileSystem()` - use this to add files and their contents to the mock file system for testing. * `loadTestFilesFromDisk()` - use this to load a mirror image of files on disk into the in-memory mock file-system. * `loadFakeCore()` - use this to load a fake version of `@angular/core` into the mock file-system. All ngcc and ngtsc source and tests now use this virtual file-system setup. PR Close #30921
This commit is contained in:

committed by
Kara Erickson

parent
1e7e065423
commit
7186f9c016
@ -7,20 +7,19 @@
|
||||
*/
|
||||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {FileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {CompileResult} from '../../../src/ngtsc/transform';
|
||||
import {translateType, ImportManager} from '../../../src/ngtsc/translator';
|
||||
import {DecorationAnalyses} from '../analysis/decoration_analyzer';
|
||||
import {ModuleWithProvidersInfo, ModuleWithProvidersAnalyses} from '../analysis/module_with_providers_analyzer';
|
||||
import {PrivateDeclarationsAnalyses, ExportInfo} from '../analysis/private_declarations_analyzer';
|
||||
import {IMPORT_PREFIX} from '../constants';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {FileToWrite, getImportRewriter} from './utils';
|
||||
import {RenderingFormatter} from './rendering_formatter';
|
||||
import {extractSourceMap, renderSourceAndMap} from './source_maps';
|
||||
import {CompileResult} from '@angular/compiler-cli/src/ngtsc/transform';
|
||||
|
||||
/**
|
||||
* A structure that captures information about what needs to be rendered
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
import {PathSegment, AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {relative, dirname, AbsoluteFsPath, absoluteFromSourceFile} from '../../../src/ngtsc/file_system';
|
||||
import {Import, ImportManager} from '../../../src/ngtsc/translator';
|
||||
import {isDtsPath} from '../../../src/ngtsc/util/src/typescript';
|
||||
import {CompiledClass} from '../analysis/decoration_analyzer';
|
||||
@ -46,8 +46,7 @@ export class EsmRenderingFormatter implements RenderingFormatter {
|
||||
|
||||
if (from) {
|
||||
const basePath = stripExtension(from);
|
||||
const relativePath =
|
||||
'./' + PathSegment.relative(AbsoluteFsPath.dirname(entryPointBasePath), basePath);
|
||||
const relativePath = './' + relative(dirname(entryPointBasePath), basePath);
|
||||
exportFrom = entryPointBasePath !== basePath ? ` from '${relativePath}'` : '';
|
||||
}
|
||||
|
||||
@ -136,12 +135,11 @@ export class EsmRenderingFormatter implements RenderingFormatter {
|
||||
importManager: ImportManager): void {
|
||||
moduleWithProviders.forEach(info => {
|
||||
const ngModuleName = info.ngModule.node.name.text;
|
||||
const declarationFile = AbsoluteFsPath.fromSourceFile(info.declaration.getSourceFile());
|
||||
const ngModuleFile = AbsoluteFsPath.fromSourceFile(info.ngModule.node.getSourceFile());
|
||||
const declarationFile = absoluteFromSourceFile(info.declaration.getSourceFile());
|
||||
const ngModuleFile = absoluteFromSourceFile(info.ngModule.node.getSourceFile());
|
||||
const importPath = info.ngModule.viaModule ||
|
||||
(declarationFile !== ngModuleFile ?
|
||||
stripExtension(
|
||||
`./${PathSegment.relative(AbsoluteFsPath.dirname(declarationFile), ngModuleFile)}`) :
|
||||
stripExtension(`./${relative(dirname(declarationFile), ngModuleFile)}`) :
|
||||
null);
|
||||
const ngModule = generateImportString(importManager, importPath, ngModuleName);
|
||||
|
||||
|
@ -8,14 +8,13 @@
|
||||
import {ConstantPool, Expression, Statement, WrappedNodeExpr, WritePropExpr} from '@angular/compiler';
|
||||
import MagicString from 'magic-string';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {NOOP_DEFAULT_IMPORT_RECORDER} from '@angular/compiler-cli/src/ngtsc/imports';
|
||||
import {NOOP_DEFAULT_IMPORT_RECORDER} from '../../../src/ngtsc/imports';
|
||||
import {translateStatement, ImportManager} from '../../../src/ngtsc/translator';
|
||||
import {CompiledClass, CompiledFile, DecorationAnalyses} from '../analysis/decoration_analyzer';
|
||||
import {PrivateDeclarationsAnalyses} from '../analysis/private_declarations_analyzer';
|
||||
import {SwitchMarkerAnalyses, SwitchMarkerAnalysis} from '../analysis/switch_marker_analyzer';
|
||||
import {IMPORT_PREFIX} from '../constants';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {FileSystem} from '../../../src/ngtsc/file_system';
|
||||
import {NgccReflectionHost} from '../host/ngcc_host';
|
||||
import {EntryPointBundle} from '../packages/entry_point_bundle';
|
||||
import {Logger} from '../logging/logger';
|
||||
|
@ -9,8 +9,7 @@ import {SourceMapConverter, commentRegex, fromJSON, fromObject, fromSource, gene
|
||||
import MagicString from 'magic-string';
|
||||
import {RawSourceMap, SourceMapConsumer, SourceMapGenerator} from 'source-map';
|
||||
import * as ts from 'typescript';
|
||||
import {AbsoluteFsPath, PathSegment} from '../../../src/ngtsc/path';
|
||||
import {FileSystem} from '../file_system/file_system';
|
||||
import {resolve, FileSystem, absoluteFromSourceFile, dirname, basename, absoluteFrom} from '../../../src/ngtsc/file_system';
|
||||
import {Logger} from '../logging/logger';
|
||||
import {FileToWrite} from './utils';
|
||||
|
||||
@ -39,19 +38,18 @@ export function extractSourceMap(
|
||||
let externalSourceMap: SourceMapConverter|null = null;
|
||||
try {
|
||||
const fileName = external[1] || external[2];
|
||||
const filePath = AbsoluteFsPath.resolve(
|
||||
AbsoluteFsPath.dirname(AbsoluteFsPath.fromSourceFile(file)), fileName);
|
||||
const filePath = resolve(dirname(absoluteFromSourceFile(file)), fileName);
|
||||
const mappingFile = fs.readFile(filePath);
|
||||
externalSourceMap = fromJSON(mappingFile);
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') {
|
||||
logger.warn(
|
||||
`The external map file specified in the source code comment "${e.path}" was not found on the file system.`);
|
||||
const mapPath = AbsoluteFsPath.fromUnchecked(file.fileName + '.map');
|
||||
if (PathSegment.basename(e.path) !== PathSegment.basename(mapPath) && fs.exists(mapPath) &&
|
||||
const mapPath = absoluteFrom(file.fileName + '.map');
|
||||
if (basename(e.path) !== basename(mapPath) && fs.exists(mapPath) &&
|
||||
fs.stat(mapPath).isFile()) {
|
||||
logger.warn(
|
||||
`Guessing the map file name from the source file name: "${PathSegment.basename(mapPath)}"`);
|
||||
`Guessing the map file name from the source file name: "${basename(mapPath)}"`);
|
||||
try {
|
||||
externalSourceMap = fromObject(JSON.parse(fs.readFile(mapPath)));
|
||||
} catch (e) {
|
||||
@ -76,9 +74,9 @@ export function extractSourceMap(
|
||||
*/
|
||||
export function renderSourceAndMap(
|
||||
sourceFile: ts.SourceFile, input: SourceMapInfo, output: MagicString): FileToWrite[] {
|
||||
const outputPath = AbsoluteFsPath.fromSourceFile(sourceFile);
|
||||
const outputMapPath = AbsoluteFsPath.fromUnchecked(`${outputPath}.map`);
|
||||
const relativeSourcePath = PathSegment.basename(outputPath);
|
||||
const outputPath = absoluteFromSourceFile(sourceFile);
|
||||
const outputMapPath = absoluteFrom(`${outputPath}.map`);
|
||||
const relativeSourcePath = basename(outputPath);
|
||||
const relativeMapPath = `${relativeSourcePath}.map`;
|
||||
|
||||
const outputMap = output.generateMap({
|
||||
|
@ -6,8 +6,8 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import * as ts from 'typescript';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/file_system';
|
||||
import {ImportRewriter, NoopImportRewriter, R3SymbolsImportRewriter} from '../../../src/ngtsc/imports';
|
||||
import {AbsoluteFsPath} from '../../../src/ngtsc/path';
|
||||
import {NgccFlatImportRewriter} from './ngcc_import_rewriter';
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user