@angular/build
Version:
Official build system for Angular
250 lines • 13.5 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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeBuild = executeBuild;
const compilation_1 = require("../../tools/angular/compilation");
const source_file_cache_1 = require("../../tools/esbuild/angular/source-file-cache");
const budget_stats_1 = require("../../tools/esbuild/budget-stats");
const bundler_context_1 = require("../../tools/esbuild/bundler-context");
const bundler_execution_result_1 = require("../../tools/esbuild/bundler-execution-result");
const commonjs_checker_1 = require("../../tools/esbuild/commonjs-checker");
const license_extractor_1 = require("../../tools/esbuild/license-extractor");
const profiling_1 = require("../../tools/esbuild/profiling");
const utils_1 = require("../../tools/esbuild/utils");
const bundle_calculator_1 = require("../../utils/bundle-calculator");
const environment_options_1 = require("../../utils/environment-options");
const resolve_assets_1 = require("../../utils/resolve-assets");
const manifest_1 = require("../../utils/server-rendering/manifest");
const supported_browsers_1 = require("../../utils/supported-browsers");
const execute_post_bundle_1 = require("./execute-post-bundle");
const i18n_1 = require("./i18n");
const setup_bundling_1 = require("./setup-bundling");
// eslint-disable-next-line max-lines-per-function
async function executeBuild(options, context, rebuildState) {
const { projectRoot, workspaceRoot, i18nOptions, optimizationOptions, assets, cacheOptions, serverEntryPoint, baseHref, ssrOptions, verbose, colors, jsonLogs, } = options;
// TODO: Consider integrating into watch mode. Would require full rebuild on target changes.
const browsers = (0, supported_browsers_1.getSupportedBrowsers)(projectRoot, context.logger);
// Load active translations if inlining
// TODO: Integrate into watch mode and only load changed translations
if (i18nOptions.shouldInline) {
await (0, i18n_1.loadActiveTranslations)(context, i18nOptions);
}
// Reuse rebuild state or create new bundle contexts for code and global stylesheets
let bundlerContexts;
let componentStyleBundler;
let codeBundleCache;
let bundlingResult;
let templateUpdates;
if (rebuildState) {
bundlerContexts = rebuildState.rebuildContexts;
componentStyleBundler = rebuildState.componentStyleBundler;
codeBundleCache = rebuildState.codeBundleCache;
templateUpdates = rebuildState.templateUpdates;
// Reset template updates for new rebuild
templateUpdates?.clear();
const allFileChanges = rebuildState.fileChanges.all;
// Bundle all contexts that do not require TypeScript changed file checks.
// These will automatically use cached results based on the changed files.
bundlingResult = await bundler_context_1.BundlerContext.bundleAll(bundlerContexts.otherContexts, allFileChanges);
// Check the TypeScript code bundling cache for changes. If invalid, force a rebundle of
// all TypeScript related contexts.
const forceTypeScriptRebuild = codeBundleCache?.invalidate(allFileChanges);
const typescriptResults = [];
for (const typescriptContext of bundlerContexts.typescriptContexts) {
typescriptContext.invalidate(allFileChanges);
const result = await typescriptContext.bundle(forceTypeScriptRebuild);
typescriptResults.push(result);
}
bundlingResult = bundler_context_1.BundlerContext.mergeResults([bundlingResult, ...typescriptResults]);
}
else {
const target = (0, utils_1.transformSupportedBrowsersToTargets)(browsers);
codeBundleCache = new source_file_cache_1.SourceFileCache(cacheOptions.enabled ? cacheOptions.path : undefined);
componentStyleBundler = (0, setup_bundling_1.createComponentStyleBundler)(options, target);
if (options.templateUpdates) {
templateUpdates = new Map();
}
bundlerContexts = (0, setup_bundling_1.setupBundlerContexts)(options, target, codeBundleCache, componentStyleBundler,
// Create new reusable compilation for the appropriate mode based on the `jit` plugin option
await (0, compilation_1.createAngularCompilation)(!!options.jit, !options.serverEntryPoint), templateUpdates);
// Bundle everything on initial build
bundlingResult = await bundler_context_1.BundlerContext.bundleAll([
...bundlerContexts.typescriptContexts,
...bundlerContexts.otherContexts,
]);
}
// Update any external component styles if enabled and rebuilding.
// TODO: Only attempt rebundling of invalidated styles once incremental build results are supported.
if (rebuildState && options.externalRuntimeStyles) {
componentStyleBundler.invalidate(rebuildState.fileChanges.all);
const componentResults = await componentStyleBundler.bundleAllFiles(true, true);
bundlingResult = bundler_context_1.BundlerContext.mergeResults([bundlingResult, ...componentResults]);
}
if (options.optimizationOptions.scripts && environment_options_1.shouldOptimizeChunks) {
const { optimizeChunks } = await Promise.resolve().then(() => __importStar(require('./chunk-optimizer')));
bundlingResult = await (0, profiling_1.profileAsync)('OPTIMIZE_CHUNKS', () => optimizeChunks(bundlingResult, options.sourcemapOptions.scripts ? !options.sourcemapOptions.hidden || 'hidden' : false));
}
const executionResult = new bundler_execution_result_1.ExecutionResult(bundlerContexts, componentStyleBundler, codeBundleCache, templateUpdates);
executionResult.addWarnings(bundlingResult.warnings);
// Add used external component style referenced files to be watched
if (options.externalRuntimeStyles) {
executionResult.extraWatchFiles.push(...componentStyleBundler.collectReferencedFiles());
}
// Return if the bundling has errors
if (bundlingResult.errors) {
executionResult.addErrors(bundlingResult.errors);
return executionResult;
}
// Analyze external imports if external options are enabled
if (options.externalPackages || bundlingResult.externalConfiguration) {
const { externalConfiguration = [], externalImports: { browser = [], server = [] }, } = bundlingResult;
// Similar to esbuild, --external:@foo/bar automatically implies --external:@foo/bar/*,
// which matches import paths like @foo/bar/baz.
// This means all paths within the @foo/bar package are also marked as external.
const exclusionsPrefixes = externalConfiguration.map((exclusion) => exclusion + '/');
const exclusions = new Set(externalConfiguration);
const explicitExternal = new Set();
const isExplicitExternal = (dep) => {
if (exclusions.has(dep)) {
return true;
}
for (const prefix of exclusionsPrefixes) {
if (dep.startsWith(prefix)) {
return true;
}
}
return false;
};
const implicitBrowser = [];
for (const dep of browser) {
if (isExplicitExternal(dep)) {
explicitExternal.add(dep);
}
else {
implicitBrowser.push(dep);
}
}
const implicitServer = [];
for (const dep of server) {
if (isExplicitExternal(dep)) {
explicitExternal.add(dep);
}
else {
implicitServer.push(dep);
}
}
executionResult.setExternalMetadata(implicitBrowser, implicitServer, [...explicitExternal]);
}
const { metafile, initialFiles, outputFiles } = bundlingResult;
executionResult.outputFiles.push(...outputFiles);
// Analyze files for bundle budget failures if present
let budgetFailures;
if (options.budgets) {
const compatStats = (0, budget_stats_1.generateBudgetStats)(metafile, outputFiles, initialFiles);
budgetFailures = [...(0, bundle_calculator_1.checkBudgets)(options.budgets, compatStats, true)];
for (const { message, severity } of budgetFailures) {
if (severity === 'error') {
executionResult.addError(message);
}
else {
executionResult.addWarning(message);
}
}
}
// Calculate estimated transfer size if scripts are optimized
let estimatedTransferSizes;
if (optimizationOptions.scripts || optimizationOptions.styles.minify) {
estimatedTransferSizes = await (0, utils_1.calculateEstimatedTransferSizes)(executionResult.outputFiles);
}
// Check metafile for CommonJS module usage if optimizing scripts
if (optimizationOptions.scripts) {
const messages = (0, commonjs_checker_1.checkCommonJSModules)(metafile, options.allowedCommonJsDependencies);
executionResult.addWarnings(messages);
}
// Copy assets
if (assets) {
executionResult.addAssets(await (0, resolve_assets_1.resolveAssets)(assets, workspaceRoot));
}
// Extract and write licenses for used packages
if (options.extractLicenses) {
executionResult.addOutputFile('3rdpartylicenses.txt', await (0, license_extractor_1.extractLicenses)(metafile, workspaceRoot), bundler_context_1.BuildOutputFileType.Root);
}
// Watch input index HTML file if configured
if (options.indexHtmlOptions) {
executionResult.extraWatchFiles.push(options.indexHtmlOptions.input);
executionResult.htmlIndexPath = options.indexHtmlOptions.output;
executionResult.htmlBaseHref = options.baseHref;
}
// Create server app engine manifest
if (serverEntryPoint) {
executionResult.addOutputFile(manifest_1.SERVER_APP_ENGINE_MANIFEST_FILENAME, (0, manifest_1.generateAngularServerAppEngineManifest)(i18nOptions, baseHref), bundler_context_1.BuildOutputFileType.ServerRoot);
}
// Perform i18n translation inlining if enabled
if (i18nOptions.shouldInline) {
const result = await (0, i18n_1.inlineI18n)(metafile, options, executionResult, initialFiles);
executionResult.addErrors(result.errors);
executionResult.addWarnings(result.warnings);
executionResult.addPrerenderedRoutes(result.prerenderedRoutes);
}
else {
const result = await (0, execute_post_bundle_1.executePostBundleSteps)(metafile, options, executionResult.outputFiles, executionResult.assetFiles, initialFiles,
// Set lang attribute to the defined source locale if present
i18nOptions.hasDefinedSourceLocale ? i18nOptions.sourceLocale : undefined);
// Deduplicate and add errors and warnings
executionResult.addErrors([...new Set(result.errors)]);
executionResult.addWarnings([...new Set(result.warnings)]);
executionResult.addPrerenderedRoutes(result.prerenderedRoutes);
executionResult.outputFiles.push(...result.additionalOutputFiles);
executionResult.assetFiles.push(...result.additionalAssets);
}
executionResult.addOutputFile('prerendered-routes.json', JSON.stringify({ routes: executionResult.prerenderedRoutes }, null, 2), bundler_context_1.BuildOutputFileType.Root);
// Write metafile if stats option is enabled
if (options.stats) {
executionResult.addOutputFile('stats.json', JSON.stringify(metafile, null, 2), bundler_context_1.BuildOutputFileType.Root);
}
if (!jsonLogs) {
const changedFiles = rebuildState && executionResult.findChangedFiles(rebuildState.previousOutputInfo);
executionResult.addLog((0, utils_1.logBuildStats)(metafile, outputFiles, initialFiles, budgetFailures, colors, changedFiles, estimatedTransferSizes, !!ssrOptions, verbose));
}
return executionResult;
}
//# sourceMappingURL=execute-build.js.map