UNPKG

@angular-devkit/build-angular

Version:
304 lines (303 loc) 11.6 kB
"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 __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; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getOutputHashFormat = getOutputHashFormat; exports.normalizeExtraEntryPoints = normalizeExtraEntryPoints; exports.assetNameTemplateFactory = assetNameTemplateFactory; exports.getInstrumentationExcludedPaths = getInstrumentationExcludedPaths; exports.normalizeGlobalStyles = normalizeGlobalStyles; exports.getCacheSettings = getCacheSettings; exports.globalScriptsByBundleName = globalScriptsByBundleName; exports.assetPatterns = assetPatterns; exports.getStatsOptions = getStatsOptions; exports.isPackageInstalled = isPackageInstalled; const fast_glob_1 = __importDefault(require("fast-glob")); const node_crypto_1 = require("node:crypto"); const path = __importStar(require("node:path")); const schema_1 = require("../../../builders/browser/schema"); const package_version_1 = require("../../../utils/package-version"); function getOutputHashFormat(outputHashing = schema_1.OutputHashing.None, length = 20) { const hashTemplate = `.[contenthash:${length}]`; switch (outputHashing) { case 'media': return { chunk: '', extract: '', file: hashTemplate, script: '', }; case 'bundles': return { chunk: hashTemplate, extract: hashTemplate, file: '', script: hashTemplate, }; case 'all': return { chunk: hashTemplate, extract: hashTemplate, file: hashTemplate, script: hashTemplate, }; case 'none': default: return { chunk: '', extract: '', file: '', script: '', }; } } function normalizeExtraEntryPoints(extraEntryPoints, defaultBundleName) { return extraEntryPoints.map((entry) => { if (typeof entry === 'string') { return { input: entry, inject: true, bundleName: defaultBundleName }; } const { inject = true, ...newEntry } = entry; let bundleName; if (entry.bundleName) { bundleName = entry.bundleName; } else if (!inject) { // Lazy entry points use the file name as bundle name. bundleName = path.parse(entry.input).name; } else { bundleName = defaultBundleName; } return { ...newEntry, inject, bundleName }; }); } function assetNameTemplateFactory(hashFormat) { const visitedFiles = new Map(); return (resourcePath) => { if (hashFormat.file) { // File names are hashed therefore we don't need to handle files with the same file name. return `[name]${hashFormat.file}.[ext]`; } const filename = path.basename(resourcePath); // Check if the file with the same name has already been processed. const visited = visitedFiles.get(filename); if (!visited) { // Not visited. visitedFiles.set(filename, resourcePath); return filename; } else if (visited === resourcePath) { // Same file. return filename; } // File has the same name but it's in a different location. return '[path][name].[ext]'; }; } function getInstrumentationExcludedPaths(root, excludedPaths) { const excluded = new Set(); for (const excludeGlob of excludedPaths) { const excludePath = excludeGlob[0] === '/' ? excludeGlob.slice(1) : excludeGlob; fast_glob_1.default.sync(excludePath, { cwd: root }).forEach((p) => excluded.add(path.join(root, p))); } return excluded; } function normalizeGlobalStyles(styleEntrypoints) { const entryPoints = {}; const noInjectNames = []; if (styleEntrypoints.length === 0) { return { entryPoints, noInjectNames }; } for (const style of normalizeExtraEntryPoints(styleEntrypoints, 'styles')) { // Add style entry points. entryPoints[style.bundleName] ??= []; entryPoints[style.bundleName].push(style.input); // Add non injected styles to the list. if (!style.inject) { noInjectNames.push(style.bundleName); } } return { entryPoints, noInjectNames }; } function getCacheSettings(wco, angularVersion) { const { enabled, path: cacheDirectory } = wco.buildOptions.cache; if (enabled) { return { type: 'filesystem', profile: wco.buildOptions.verbose, cacheDirectory: path.join(cacheDirectory, 'angular-webpack'), maxMemoryGenerations: 1, // We use the versions and build options as the cache name. The Webpack configurations are too // dynamic and shared among different build types: test, build and serve. // None of which are "named". name: (0, node_crypto_1.createHash)('sha1') .update(angularVersion) .update(package_version_1.VERSION) .update(wco.projectRoot) .update(JSON.stringify(wco.tsConfig)) .update(JSON.stringify({ ...wco.buildOptions, // Needed because outputPath changes on every build when using i18n extraction // https://github.com/angular/angular-cli/blob/736a5f89deaca85f487b78aec9ff66d4118ceb6a/packages/angular_devkit/build_angular/src/utils/i18n-options.ts#L264-L265 outputPath: undefined, })) .digest('hex'), }; } if (wco.buildOptions.watch) { return { type: 'memory', maxGenerations: 1, }; } return false; } function globalScriptsByBundleName(scripts) { return normalizeExtraEntryPoints(scripts, 'scripts').reduce((prev, curr) => { const { bundleName, inject, input } = curr; const existingEntry = prev.find((el) => el.bundleName === bundleName); if (existingEntry) { if (existingEntry.inject && !inject) { // All entries have to be lazy for the bundle to be lazy. throw new Error(`The ${bundleName} bundle is mixing injected and non-injected scripts.`); } existingEntry.paths.push(input); } else { prev.push({ bundleName, inject, paths: [input], }); } return prev; }, []); } function assetPatterns(root, assets) { return assets.map((asset, index) => { // Resolve input paths relative to workspace root and add slash at the end. // eslint-disable-next-line prefer-const let { input, output = '', ignore = [], glob } = asset; input = path.resolve(root, input).replace(/\\/g, '/'); input = input.endsWith('/') ? input : input + '/'; output = output.endsWith('/') ? output : output + '/'; if (output.startsWith('..')) { throw new Error('An asset cannot be written to a location outside of the output path.'); } return { context: input, // Now we remove starting slash to make Webpack place it from the output root. to: output.replace(/^\//, ''), from: glob, noErrorOnMissing: true, force: true, globOptions: { dot: true, followSymbolicLinks: !!asset.followSymlinks, ignore: [ '.gitkeep', '**/.DS_Store', '**/Thumbs.db', // Negate patterns needs to be absolute because copy-webpack-plugin uses absolute globs which // causes negate patterns not to match. // See: https://github.com/webpack-contrib/copy-webpack-plugin/issues/498#issuecomment-639327909 ...ignore, ].map((i) => path.posix.join(input, i)), }, priority: index, }; }); } function getStatsOptions(verbose = false) { const webpackOutputOptions = { all: false, // Fallback value for stats options when an option is not defined. It has precedence over local webpack defaults. colors: true, hash: true, // required by custom stat output timings: true, // required by custom stat output chunks: true, // required by custom stat output builtAt: true, // required by custom stat output warnings: true, errors: true, assets: true, // required by custom stat output cachedAssets: true, // required for bundle size calculators // Needed for markAsyncChunksNonInitial. ids: true, entrypoints: true, }; const verboseWebpackOutputOptions = { // The verbose output will most likely be piped to a file, so colors just mess it up. colors: false, usedExports: true, optimizationBailout: true, reasons: true, children: true, assets: true, version: true, chunkModules: true, errorDetails: true, errorStack: true, moduleTrace: true, logging: 'verbose', modulesSpace: Infinity, }; return verbose ? { ...webpackOutputOptions, ...verboseWebpackOutputOptions } : webpackOutputOptions; } /** * Checks if a specified package is installed in the given workspace. * * @param root - The root directory of the workspace. * @param name - The name of the package to check for. * @returns `true` if the package is installed, `false` otherwise. */ function isPackageInstalled(root, name) { try { require.resolve(name, { paths: [root] }); return true; } catch { return false; } }