@angular/build
Version:
Official build system for Angular
136 lines (135 loc) • 5.88 kB
JavaScript
;
/**
* @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.initialize = initialize;
exports.resolve = resolve;
exports.load = load;
const node_assert_1 = __importDefault(require("node:assert"));
const node_crypto_1 = require("node:crypto");
const node_path_1 = require("node:path");
const node_url_1 = require("node:url");
const javascript_transformer_1 = require("../../../tools/esbuild/javascript-transformer");
/**
* @note For some unknown reason, setting `globalThis.ngServerMode = true` does not work when using ESM loader hooks.
*/
const NG_SERVER_MODE_INIT_BYTES = new TextEncoder().encode('var ngServerMode=true;');
/**
* Node.js ESM loader to redirect imports to in memory files.
* @see: https://nodejs.org/api/esm.html#loaders for more information about loaders.
*/
const MEMORY_URL_SCHEME = 'memory://';
let memoryVirtualRootUrl;
let outputFiles;
const javascriptTransformer = new javascript_transformer_1.JavaScriptTransformer(
// Always enable JIT linking to support applications built with and without AOT.
// In a development environment the additional scope information does not
// have a negative effect unlike production where final output size is relevant.
{ sourcemap: true, jit: true }, 1);
function initialize(data) {
// This path does not actually exist but is used to overlay the in memory files with the
// actual filesystem for resolution purposes.
// A custom URL schema (such as `memory://`) cannot be used for the resolve output because
// the in-memory files may use `import.meta.url` in ways that assume a file URL.
// `createRequire` is one example of this usage.
memoryVirtualRootUrl = (0, node_url_1.pathToFileURL)((0, node_path_1.join)(data.workspaceRoot, `.angular/prerender-root/${(0, node_crypto_1.randomUUID)()}/`)).href;
outputFiles = data.outputFiles;
}
function resolve(specifier, context, nextResolve) {
// In-memory files loaded from external code will contain a memory scheme
if (specifier.startsWith(MEMORY_URL_SCHEME)) {
let memoryUrl;
try {
memoryUrl = new URL(specifier);
}
catch {
node_assert_1.default.fail('External code attempted to use malformed memory scheme: ' + specifier);
}
// Resolve with a URL based from the virtual filesystem root
return {
format: 'module',
shortCircuit: true,
url: new URL(memoryUrl.pathname.slice(1), memoryVirtualRootUrl).href,
};
}
// Use next/default resolve if the parent is not from the virtual root
if (!context.parentURL?.startsWith(memoryVirtualRootUrl)) {
return nextResolve(specifier, context);
}
// Check for `./` and `../` relative specifiers
const isRelative = specifier[0] === '.' &&
(specifier[1] === '/' || (specifier[1] === '.' && specifier[2] === '/'));
// Relative specifiers from memory file should be based from the parent memory location
if (isRelative) {
let specifierUrl;
try {
specifierUrl = new URL(specifier, context.parentURL);
}
catch { }
if (specifierUrl?.pathname &&
Object.hasOwn(outputFiles, specifierUrl.href.slice(memoryVirtualRootUrl.length))) {
return {
format: 'module',
shortCircuit: true,
url: specifierUrl.href,
};
}
node_assert_1.default.fail(`In-memory ESM relative file should always exist: '${context.parentURL}' --> '${specifier}'`);
}
// Update the parent URL to allow for module resolution for the workspace.
// This handles bare specifiers (npm packages) and absolute paths.
// Defer to the next hook in the chain, which would be the
// Node.js default resolve if this is the last user-specified loader.
return nextResolve(specifier, {
...context,
parentURL: new URL('index.js', memoryVirtualRootUrl).href,
});
}
async function load(url, context, nextLoad) {
const { format } = context;
// Load the file from memory if the URL is based in the virtual root
if (url.startsWith(memoryVirtualRootUrl)) {
const source = outputFiles[url.slice(memoryVirtualRootUrl.length)];
(0, node_assert_1.default)(source !== undefined, 'Resolved in-memory ESM file should always exist: ' + url);
// In-memory files have already been transformer during bundling and can be returned directly
return {
format,
shortCircuit: true,
source,
};
}
// Only module files potentially require transformation. Angular libraries that would
// need linking are ESM only.
if (format === 'module' && isFileProtocol(url)) {
const filePath = (0, node_url_1.fileURLToPath)(url);
let source = await javascriptTransformer.transformFile(filePath);
if (filePath.includes('@angular/')) {
// Prepend 'var ngServerMode=true;' to the source.
source = Buffer.concat([NG_SERVER_MODE_INIT_BYTES, source]);
}
return {
format,
shortCircuit: true,
source,
};
}
// Let Node.js handle all other URLs.
return nextLoad(url);
}
function isFileProtocol(url) {
return url.startsWith('file://');
}
function handleProcessExit() {
void javascriptTransformer.close();
}
process.once('exit', handleProcessExit);
process.once('SIGINT', handleProcessExit);
process.once('uncaughtException', handleProcessExit);