@angular/build
Version:
Official build system for Angular
120 lines (119 loc) • 5.65 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
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.setupJitPluginCallbacks = setupJitPluginCallbacks;
const promises_1 = require("node:fs/promises");
const node_path_1 = require("node:path");
const uri_1 = require("../../angular/uri");
const load_result_cache_1 = require("../load-result-cache");
/**
* Loads/extracts the contents from a load callback Angular JIT entry.
* An Angular JIT entry represents either a file path for a component resource or base64
* encoded data for an inline component resource.
* @param entry The value that represents content to load.
* @param root The absolute path for the root of the build (typically the workspace root).
* @param skipRead If true, do not attempt to read the file; if false, read file content from disk.
* This option has no effect if the entry does not originate from a file. Defaults to false.
* @returns An object containing the absolute path of the contents and optionally the actual contents.
* For inline entries the contents will always be provided.
*/
async function loadEntry(entry, root, skipRead) {
if (entry.startsWith('inline:')) {
const [importer, data] = entry.slice(7).split(';', 2);
return {
path: (0, node_path_1.join)(root, importer),
contents: Buffer.from(data, 'base64').toString(),
};
}
const path = (0, node_path_1.join)(root, entry);
return {
path,
contents: skipRead ? undefined : await (0, promises_1.readFile)(path, 'utf-8'),
};
}
/**
* Sets up esbuild resolve and load callbacks to support Angular JIT mode processing
* for both Component stylesheets and templates. These callbacks work alongside the JIT
* resource TypeScript transformer to convert and then bundle Component resources as
* static imports.
* @param build An esbuild {@link PluginBuild} instance used to add callbacks.
* @param styleOptions The options to use when bundling stylesheets.
* @param additionalResultFiles A Map where stylesheet resources will be added.
*/
function setupJitPluginCallbacks(build, stylesheetBundler, additionalResultFiles, loadCache) {
const root = build.initialOptions.absWorkingDir ?? '';
// Add a resolve callback to capture and parse any JIT URIs that were added by the
// JIT resource TypeScript transformer.
// Resources originating from a file are resolved as relative from the containing file (importer).
build.onResolve({ filter: uri_1.JIT_NAMESPACE_REGEXP }, (args) => {
const parsed = (0, uri_1.parseJitUri)(args.path);
if (!parsed) {
return undefined;
}
const { namespace, origin, specifier } = parsed;
if (origin === 'file') {
return {
// Use a relative path to prevent fully resolved paths in the metafile (JSON stats file).
// This is only necessary for custom namespaces. esbuild will handle the file namespace.
path: (0, node_path_1.relative)(root, (0, node_path_1.join)((0, node_path_1.dirname)(args.importer), specifier)),
namespace,
};
}
else {
// Inline data may need the importer to resolve imports/references within the content
const importer = (0, node_path_1.relative)(root, args.importer);
return {
path: `inline:${importer};${specifier}`,
namespace,
};
}
});
// Add a load callback to handle Component stylesheets (both inline and external)
build.onLoad({ filter: /./, namespace: uri_1.JIT_STYLE_NAMESPACE }, (0, load_result_cache_1.createCachedLoad)(loadCache, async (args) => {
// skipRead is used here because the stylesheet bundling will read a file stylesheet
// directly either via a preprocessor or esbuild itself.
const entry = await loadEntry(args.path, root, true /* skipRead */);
let stylesheetResult;
// Stylesheet contents only exist for internal stylesheets
if (entry.contents === undefined) {
stylesheetResult = await stylesheetBundler.bundleFile(entry.path);
}
else {
stylesheetResult = await stylesheetBundler.bundleInline(entry.contents, entry.path);
}
const { errors, warnings, referencedFiles } = stylesheetResult;
if (stylesheetResult.errors) {
return {
errors,
warnings,
watchFiles: referencedFiles && [...referencedFiles],
};
}
const { contents, outputFiles, metafile } = stylesheetResult;
additionalResultFiles.set(entry.path, { outputFiles, metafile });
return {
errors,
warnings,
contents,
loader: 'text',
watchFiles: referencedFiles && [...referencedFiles],
};
}));
// Add a load callback to handle Component templates
// NOTE: While this callback supports both inline and external templates, the transformer
// currently only supports generating URIs for external templates.
build.onLoad({ filter: /./, namespace: uri_1.JIT_TEMPLATE_NAMESPACE }, (0, load_result_cache_1.createCachedLoad)(loadCache, async (args) => {
const { contents, path } = await loadEntry(args.path, root);
return {
contents,
loader: 'text',
watchFiles: [path],
};
}));
}