UNPKG

@angular-devkit/build-angular

Version:
196 lines (195 loc) 8.68 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; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ScriptsWebpackPlugin = void 0; const loader_utils_1 = require("loader-utils"); const path = __importStar(require("node:path")); const webpack_1 = require("webpack"); const error_1 = require("../../../utils/error"); const webpack_diagnostics_1 = require("../../../utils/webpack-diagnostics"); const Entrypoint = require('webpack/lib/Entrypoint'); /** * The name of the plugin provided to Webpack when tapping Webpack compiler hooks. */ const PLUGIN_NAME = 'scripts-webpack-plugin'; function addDependencies(compilation, scripts) { for (const script of scripts) { compilation.fileDependencies.add(script); } } class ScriptsWebpackPlugin { options; _lastBuildTime; _cachedOutput; constructor(options) { this.options = options; } async shouldSkip(compilation, scripts) { if (this._lastBuildTime == undefined) { this._lastBuildTime = Date.now(); return false; } for (const script of scripts) { const scriptTime = await new Promise((resolve, reject) => { compilation.fileSystemInfo.getFileTimestamp(script, (error, entry) => { if (error) { reject(error); return; } resolve(entry && typeof entry !== 'string' ? entry.safeTime : undefined); }); }); if (!scriptTime || scriptTime > this._lastBuildTime) { this._lastBuildTime = Date.now(); return false; } } return true; } _insertOutput(compilation, { filename, source }, cached = false) { const chunk = new webpack_1.Chunk(this.options.name); chunk.rendered = !cached; chunk.id = this.options.name; chunk.ids = [chunk.id]; chunk.files.add(filename); const entrypoint = new Entrypoint(this.options.name); entrypoint.pushChunk(chunk); chunk.addGroup(entrypoint); compilation.entrypoints.set(this.options.name, entrypoint); compilation.chunks.add(chunk); compilation.assets[filename] = source; compilation.hooks.chunkAsset.call(chunk, filename); } apply(compiler) { if (this.options.scripts.length === 0) { return; } compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => { // Use the resolver from the compilation instead of compiler. // Using the latter will causes a lot of `DescriptionFileUtils.loadDescriptionFile` calls. // See: https://github.com/angular/angular-cli/issues/24634#issuecomment-1425782668 const resolver = compilation.resolverFactory.get('normal', { preferRelative: true, useSyncFileSystemCalls: true, // Caching must be disabled because it causes the resolver to become async after a rebuild. cache: false, }); const scripts = []; for (const script of this.options.scripts) { try { const resolvedPath = resolver.resolveSync({}, this.options.basePath, script); if (resolvedPath) { scripts.push(resolvedPath); } else { (0, webpack_diagnostics_1.addError)(compilation, `Cannot resolve '${script}'.`); } } catch (error) { (0, error_1.assertIsError)(error); (0, webpack_diagnostics_1.addError)(compilation, error.message); } } compilation.hooks.additionalAssets.tapPromise(PLUGIN_NAME, async () => { if (await this.shouldSkip(compilation, scripts)) { if (this._cachedOutput) { this._insertOutput(compilation, this._cachedOutput, true); } addDependencies(compilation, scripts); return; } const sourceGetters = scripts.map((fullPath) => { return new Promise((resolve, reject) => { compilation.inputFileSystem.readFile(fullPath, (err, data) => { if (err) { reject(err); return; } const content = data?.toString() ?? ''; let source; if (this.options.sourceMap) { // TODO: Look for source map file (for '.min' scripts, etc.) let adjustedPath = fullPath; if (this.options.basePath) { adjustedPath = path.relative(this.options.basePath, fullPath); } source = new webpack_1.sources.OriginalSource(content, adjustedPath); } else { source = new webpack_1.sources.RawSource(content); } resolve(source); }); }); }); const sources = await Promise.all(sourceGetters); const concatSource = new webpack_1.sources.ConcatSource(); sources.forEach((source) => { concatSource.add(source); concatSource.add('\n;'); }); const combinedSource = new webpack_1.sources.CachedSource(concatSource); const output = { filename: this.options.filename, source: combinedSource }; this._insertOutput(compilation, output); this._cachedOutput = output; addDependencies(compilation, scripts); }); compilation.hooks.processAssets.tapPromise({ name: PLUGIN_NAME, stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING, }, async () => { const assetName = this.options.filename; const asset = compilation.getAsset(assetName); if (asset) { const interpolatedFilename = (0, loader_utils_1.interpolateName)( // TODO: Revisit. Previously due to lack of type safety, this object // was fine, but in practice it doesn't match the type of the loader context. { resourcePath: 'scripts.js' }, assetName, { content: asset.source.source() }); if (assetName !== interpolatedFilename) { compilation.renameAsset(assetName, interpolatedFilename); } } }); }); } } exports.ScriptsWebpackPlugin = ScriptsWebpackPlugin;