mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-19 20:52:06 +08:00
refactor(@angular-devkit/build-angular): use native path normalization for angular compiler caching
The path comparisons for the TypeScript emit file lookups now use the native path normalization functions. This removes the special handling previously used and better ensures that cache checks are valid. The separate babel cache Map has also been combined with the already present memory load cache within the Angular plugin. This removes the need for extra handling of another Map and allows it to use the common logic of the load cache. To support this usage, the load cache will also now automatically include the request path in the file dependencies if the request is from the `file` namespace. This removes the need to manually specify the request path file for each load result return value.
This commit is contained in:
parent
3b93df42da
commit
03ce964858
@ -18,10 +18,9 @@ import type {
|
|||||||
import assert from 'node:assert';
|
import assert from 'node:assert';
|
||||||
import { realpath } from 'node:fs/promises';
|
import { realpath } from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { pathToFileURL } from 'node:url';
|
|
||||||
import { maxWorkers } from '../../../utils/environment-options';
|
import { maxWorkers } from '../../../utils/environment-options';
|
||||||
import { JavaScriptTransformer } from '../javascript-transformer';
|
import { JavaScriptTransformer } from '../javascript-transformer';
|
||||||
import { LoadResultCache } from '../load-result-cache';
|
import { LoadResultCache, createCachedLoad } from '../load-result-cache';
|
||||||
import { logCumulativeDurations, profileAsync, resetCumulativeDurations } from '../profiling';
|
import { logCumulativeDurations, profileAsync, resetCumulativeDurations } from '../profiling';
|
||||||
import { BundleStylesheetOptions } from '../stylesheets/bundle-options';
|
import { BundleStylesheetOptions } from '../stylesheets/bundle-options';
|
||||||
import { AngularHostOptions } from './angular-host';
|
import { AngularHostOptions } from './angular-host';
|
||||||
@ -280,7 +279,7 @@ export function createCompilerPlugin(
|
|||||||
try {
|
try {
|
||||||
await profileAsync('NG_EMIT_TS', async () => {
|
await profileAsync('NG_EMIT_TS', async () => {
|
||||||
for (const { filename, contents } of await compilation.emitAffectedFiles()) {
|
for (const { filename, contents } of await compilation.emitAffectedFiles()) {
|
||||||
typeScriptFileCache.set(pathToFileURL(filename).href, contents);
|
typeScriptFileCache.set(path.normalize(filename), contents);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -323,7 +322,9 @@ export function createCompilerPlugin(
|
|||||||
});
|
});
|
||||||
|
|
||||||
build.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async (args) => {
|
build.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, async (args) => {
|
||||||
const request = pluginOptions.fileReplacements?.[args.path] ?? args.path;
|
const request = path.normalize(
|
||||||
|
pluginOptions.fileReplacements?.[path.normalize(args.path)] ?? args.path,
|
||||||
|
);
|
||||||
|
|
||||||
// Skip TS load attempt if JS TypeScript compilation not enabled and file is JS
|
// Skip TS load attempt if JS TypeScript compilation not enabled and file is JS
|
||||||
if (shouldTsIgnoreJs && /\.[cm]?js$/.test(request)) {
|
if (shouldTsIgnoreJs && /\.[cm]?js$/.test(request)) {
|
||||||
@ -334,7 +335,7 @@ export function createCompilerPlugin(
|
|||||||
// the options cannot change and do not need to be represented in the key. If the
|
// the options cannot change and do not need to be represented in the key. If the
|
||||||
// cache is later stored to disk, then the options that affect transform output
|
// cache is later stored to disk, then the options that affect transform output
|
||||||
// would need to be added to the key as well as a check for any change of content.
|
// would need to be added to the key as well as a check for any change of content.
|
||||||
let contents = typeScriptFileCache.get(pathToFileURL(request).href);
|
let contents = typeScriptFileCache.get(request);
|
||||||
|
|
||||||
if (contents === undefined) {
|
if (contents === undefined) {
|
||||||
// If the Angular compilation had errors the file may not have been emitted.
|
// If the Angular compilation had errors the file may not have been emitted.
|
||||||
@ -364,7 +365,7 @@ export function createCompilerPlugin(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Store as the returned Uint8Array to allow caching the fully transformed code
|
// Store as the returned Uint8Array to allow caching the fully transformed code
|
||||||
typeScriptFileCache.set(pathToFileURL(request).href, contents);
|
typeScriptFileCache.set(request, contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -373,27 +374,25 @@ export function createCompilerPlugin(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
build.onLoad({ filter: /\.[cm]?js$/ }, (args) =>
|
build.onLoad(
|
||||||
profileAsync(
|
{ filter: /\.[cm]?js$/ },
|
||||||
'NG_EMIT_JS*',
|
createCachedLoad(pluginOptions.loadResultCache, async (args) => {
|
||||||
async () => {
|
return profileAsync(
|
||||||
// The filename is currently used as a cache key. Since the cache is memory only,
|
'NG_EMIT_JS*',
|
||||||
// the options cannot change and do not need to be represented in the key. If the
|
async () => {
|
||||||
// cache is later stored to disk, then the options that affect transform output
|
const contents = await javascriptTransformer.transformFile(
|
||||||
// would need to be added to the key as well as a check for any change of content.
|
args.path,
|
||||||
let contents = pluginOptions.sourceFileCache?.babelFileCache.get(args.path);
|
pluginOptions.jit,
|
||||||
if (contents === undefined) {
|
);
|
||||||
contents = await javascriptTransformer.transformFile(args.path, pluginOptions.jit);
|
|
||||||
pluginOptions.sourceFileCache?.babelFileCache.set(args.path, contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
contents,
|
contents,
|
||||||
loader: 'js',
|
loader: 'js',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
),
|
);
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Setup bundling of component templates and stylesheets when in JIT mode
|
// Setup bundling of component templates and stylesheets when in JIT mode
|
||||||
@ -531,8 +530,9 @@ function bundleWebWorker(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createMissingFileError(request: string, original: string, root: string): PartialMessage {
|
function createMissingFileError(request: string, original: string, root: string): PartialMessage {
|
||||||
|
const relativeRequest = path.relative(root, request);
|
||||||
const error = {
|
const error = {
|
||||||
text: `File '${path.relative(root, request)}' is missing from the TypeScript compilation.`,
|
text: `File '${relativeRequest}' is missing from the TypeScript compilation.`,
|
||||||
notes: [
|
notes: [
|
||||||
{
|
{
|
||||||
text: `Ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
|
text: `Ensure the file is part of the TypeScript program via the 'files' or 'include' property.`,
|
||||||
@ -540,9 +540,10 @@ function createMissingFileError(request: string, original: string, root: string)
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (request !== original) {
|
const relativeOriginal = path.relative(root, original);
|
||||||
|
if (relativeRequest !== relativeOriginal) {
|
||||||
error.notes.push({
|
error.notes.push({
|
||||||
text: `File is requested from a file replacement of '${path.relative(root, original)}'.`,
|
text: `File is requested from a file replacement of '${relativeOriginal}'.`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
|
|
||||||
import { platform } from 'node:os';
|
import { platform } from 'node:os';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { pathToFileURL } from 'node:url';
|
|
||||||
import type ts from 'typescript';
|
import type ts from 'typescript';
|
||||||
import { MemoryLoadResultCache } from '../load-result-cache';
|
import { MemoryLoadResultCache } from '../load-result-cache';
|
||||||
|
|
||||||
@ -17,7 +16,6 @@ const WINDOWS_SEP_REGEXP = new RegExp(`\\${path.win32.sep}`, 'g');
|
|||||||
|
|
||||||
export class SourceFileCache extends Map<string, ts.SourceFile> {
|
export class SourceFileCache extends Map<string, ts.SourceFile> {
|
||||||
readonly modifiedFiles = new Set<string>();
|
readonly modifiedFiles = new Set<string>();
|
||||||
readonly babelFileCache = new Map<string, Uint8Array>();
|
|
||||||
readonly typeScriptFileCache = new Map<string, string | Uint8Array>();
|
readonly typeScriptFileCache = new Map<string, string | Uint8Array>();
|
||||||
readonly loadResultCache = new MemoryLoadResultCache();
|
readonly loadResultCache = new MemoryLoadResultCache();
|
||||||
|
|
||||||
@ -32,8 +30,8 @@ export class SourceFileCache extends Map<string, ts.SourceFile> {
|
|||||||
this.modifiedFiles.clear();
|
this.modifiedFiles.clear();
|
||||||
}
|
}
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
this.babelFileCache.delete(file);
|
file = path.normalize(file);
|
||||||
this.typeScriptFileCache.delete(pathToFileURL(file).href);
|
this.typeScriptFileCache.delete(file);
|
||||||
this.loadResultCache.invalidate(file);
|
this.loadResultCache.invalidate(file);
|
||||||
|
|
||||||
// Normalize separators to allow matching TypeScript Host paths
|
// Normalize separators to allow matching TypeScript Host paths
|
||||||
|
@ -32,6 +32,11 @@ export function createCachedLoad(
|
|||||||
|
|
||||||
// Do not cache null or undefined
|
// Do not cache null or undefined
|
||||||
if (result) {
|
if (result) {
|
||||||
|
// Ensure requested path is included if it was a resolved file
|
||||||
|
if (args.namespace === 'file') {
|
||||||
|
result.watchFiles ??= [];
|
||||||
|
result.watchFiles.push(args.path);
|
||||||
|
}
|
||||||
await cache.put(loadCacheKey, result);
|
await cache.put(loadCacheKey, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -79,6 +84,8 @@ export class MemoryLoadResultCache implements LoadResultCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get watchFiles(): string[] {
|
get watchFiles(): string[] {
|
||||||
return [...this.#loadResults.keys(), ...this.#fileDependencies.keys()];
|
// this.#loadResults.keys() is not included here because the keys
|
||||||
|
// are namespaced request paths and not disk-based file paths.
|
||||||
|
return [...this.#fileDependencies.keys()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user