@angular/build
Version:
Official build system for Angular
117 lines (116 loc) • 6.22 kB
JavaScript
"use strict";
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAngularMemoryPlugin = createAngularMemoryPlugin;
const node_assert_1 = __importDefault(require("node:assert"));
const promises_1 = require("node:fs/promises");
const node_path_1 = require("node:path");
const node_url_1 = require("node:url");
const load_esm_1 = require("../../../utils/load-esm");
const ANGULAR_PREFIX = '/@ng/';
const VITE_FS_PREFIX = '/@fs/';
const FILE_PROTOCOL = 'file:';
async function createAngularMemoryPlugin(options) {
const { virtualProjectRoot, outputFiles, external } = options;
const { normalizePath } = await (0, load_esm_1.loadEsmModule)('vite');
return {
name: 'vite:angular-memory',
// Ensures plugin hooks run before built-in Vite hooks
enforce: 'pre',
async resolveId(source, importer, { ssr }) {
if (source.startsWith(VITE_FS_PREFIX)) {
return;
}
// For SSR with component HMR, pass through as a virtual module
if (ssr && source.startsWith(FILE_PROTOCOL) && source.includes(ANGULAR_PREFIX)) {
// Vite will resolve these these files example:
// `file:///@ng/component?c=src%2Fapp%2Fapp.component.ts%40AppComponent&t=1737017253850`
const sourcePath = (0, node_url_1.fileURLToPath)(source);
const sourceWithoutRoot = normalizePath('/' + (0, node_path_1.relative)(virtualProjectRoot, sourcePath));
if (sourceWithoutRoot.startsWith(ANGULAR_PREFIX)) {
const [, query] = source.split('?', 2);
return `\0${sourceWithoutRoot}?${query}`;
}
}
// Prevent vite from resolving an explicit external dependency (`externalDependencies` option)
if (external?.includes(source)) {
// This is still not ideal since Vite will still transform the import specifier to
// `/@id/${source}` but is currently closer to a raw external than a resolved file path.
return source;
}
if (importer && source[0] === '.') {
const normalizedImporter = normalizePath(importer);
if (normalizedImporter.startsWith(virtualProjectRoot)) {
// Remove query if present
const [importerFile] = normalizedImporter.split('?', 1);
source = '/' + (0, node_path_1.join)((0, node_path_1.dirname)((0, node_path_1.relative)(virtualProjectRoot, importerFile)), source);
}
}
const [file] = source.split('?', 1);
if (outputFiles.has(normalizePath(file))) {
return (0, node_path_1.join)(virtualProjectRoot, source);
}
},
load(id, loadOptions) {
// For SSR component updates, return the component update module or empty if none
if (loadOptions?.ssr && id.startsWith(`\0${ANGULAR_PREFIX}`)) {
// Extract component identifier (first character is rollup virtual module null)
const requestUrl = new URL(id.slice(1), 'http://localhost');
const componentId = requestUrl.searchParams.get('c');
return (componentId && options.templateUpdates?.get(encodeURIComponent(componentId))) ?? '';
}
const [file] = id.split('?', 1);
const relativeFile = '/' + normalizePath((0, node_path_1.relative)(virtualProjectRoot, file));
const codeContents = outputFiles.get(relativeFile)?.contents;
if (codeContents === undefined) {
if (relativeFile.endsWith('/node_modules/vite/dist/client/client.mjs')) {
return loadViteClientCode(file, options.disableViteTransport);
}
return undefined;
}
const code = Buffer.from(codeContents).toString('utf-8');
const mapContents = outputFiles.get(relativeFile + '.map')?.contents;
return {
// Remove source map URL comments from the code if a sourcemap is present.
// Vite will inline and add an additional sourcemap URL for the sourcemap.
code: mapContents ? code.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '') : code,
map: mapContents && Buffer.from(mapContents).toString('utf-8'),
};
},
};
}
/**
* Reads the resolved Vite client code from disk and updates the content to remove
* an unactionable suggestion to update the Vite configuration file to disable the
* error overlay. The Vite configuration file is not present when used in the Angular
* CLI.
* @param file The absolute path to the Vite client code.
* @returns
*/
async function loadViteClientCode(file, disableViteTransport = false) {
const originalContents = await (0, promises_1.readFile)(file, 'utf-8');
let updatedContents = originalContents.replace(`"You can also disable this overlay by setting ",
h("code", { part: "config-option-name" }, "server.hmr.overlay"),
" to ",
h("code", { part: "config-option-value" }, "false"),
" in ",
h("code", { part: "config-file-name" }, hmrConfigName),
"."`, '');
(0, node_assert_1.default)(originalContents !== updatedContents, 'Failed to update Vite client error overlay text.');
if (disableViteTransport) {
const previousUpdatedContents = updatedContents;
updatedContents = updatedContents.replace('transport.connect(createHMRHandler(handleMessage));', '');
(0, node_assert_1.default)(previousUpdatedContents !== updatedContents, 'Failed to update Vite client WebSocket disable.');
updatedContents = updatedContents.replace('console.debug("[vite] connecting...")', '');
}
return updatedContents;
}