| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162 |
- /*
- MIT License http://www.opensource.org/licenses/mit-license.php
- Author Alexander Akait @alexander-akait
- */
- "use strict";
- const { RawSource, ReplaceSource } = require("webpack-sources");
- const { ASSET_URL_TYPE } = require("../ModuleSourceTypeConstants");
- const createHash = require("../util/createHash");
- const nonNumericOnlyHash = require("../util/nonNumericOnlyHash");
- const { PUBLIC_PATH_AUTO } = require("../util/publicPathPlaceholder");
- const AssetGenerator = require("./AssetGenerator");
- /** @import { Source } from "webpack-sources" */
- /** @import CodeGenerationResults from "../CodeGenerationResults" */
- /** @import { DependencyConstructor } from "../Compilation" */
- /** @import { DependencyTemplateContext } from "../DependencyTemplate" */
- /** @import { GenerateContext } from "../Generator" */
- /** @import { SourceType, SourceTypes } from "../Module" */
- /** @import ModuleGraph from "../ModuleGraph" */
- /** @import NormalModule from "../NormalModule" */
- const WEBMANIFEST_TYPE = "webmanifest";
- const WEBMANIFEST_TYPES = new Set([WEBMANIFEST_TYPE]);
- const WEBMANIFEST_AND_ASSET_URL_TYPES = new Set([
- WEBMANIFEST_TYPE,
- ASSET_URL_TYPE
- ]);
- /**
- * A Web App Manifest is a "typed asset": a variant of `AssetGenerator` (reusing
- * its filename/hash and `ASSET_URL_TYPE` machinery) whose emitted content is the
- * raw JSON with each icon `src` rewritten to its bundled asset URL. The manifest
- * itself is emitted — with its own URL exposed to the referencing
- * `<link rel="manifest">` — by `AssetModulesPlugin`'s `renderManifest`, which
- * resolves the per-file public path (like the CSS/HTML plugins).
- */
- class WebManifestGenerator extends AssetGenerator {
- /**
- * @param {ModuleGraph} moduleGraph the module graph
- */
- constructor(moduleGraph) {
- super(moduleGraph, undefined, undefined, undefined, undefined, true);
- }
- /**
- * Returns the source types available for this module.
- * @param {NormalModule} module fresh module
- * @returns {SourceTypes} available types (do not mutate)
- */
- getTypes(module) {
- // Expose the manifest's own URL only when it is referenced (from HTML).
- const connections = this._moduleGraph.getIncomingConnections(module);
- const referenced = !connections[Symbol.iterator]().next().done;
- return referenced ? WEBMANIFEST_AND_ASSET_URL_TYPES : WEBMANIFEST_TYPES;
- }
- /**
- * Returns the estimated size for the requested source type.
- * @param {NormalModule} module the module
- * @param {SourceType=} type source type
- * @returns {number} estimate size of the module
- */
- getSize(module, type) {
- const originalSource = module.originalSource();
- return originalSource ? originalSource.size() : 0;
- }
- /**
- * Rewrites each icon `src` in the raw manifest to its emitted asset URL.
- * @param {NormalModule} module the manifest module
- * @param {GenerateContext} generateContext context for generate
- * @returns {string} the rewritten manifest content
- */
- _rewrite(module, generateContext) {
- const originalSource = /** @type {Source} */ (module.originalSource());
- const source = new ReplaceSource(originalSource);
- // `HtmlSourceDependency.Template` only reads `moduleGraph`/`runtimeTemplate`/
- // `codeGenerationResults`; the rest is filled to satisfy the context shape.
- /** @type {DependencyTemplateContext} */
- const templateContext = {
- runtimeTemplate: generateContext.runtimeTemplate,
- dependencyTemplates: generateContext.dependencyTemplates,
- moduleGraph: generateContext.moduleGraph,
- chunkGraph: generateContext.chunkGraph,
- module,
- runtime: generateContext.runtime,
- runtimeRequirements: generateContext.runtimeRequirements,
- concatenationScope: generateContext.concatenationScope,
- codeGenerationResults:
- /** @type {CodeGenerationResults} */
- (generateContext.codeGenerationResults),
- initFragments: [],
- chunkInitFragments: []
- };
- for (const dependency of module.dependencies) {
- const constructor = /** @type {DependencyConstructor} */ (
- dependency.constructor
- );
- const template = templateContext.dependencyTemplates.get(constructor);
- if (!template) {
- throw new Error(
- `No template for dependency: ${dependency.constructor.name}`
- );
- }
- template.apply(dependency, source, templateContext);
- }
- return /** @type {string} */ (source.source());
- }
- /**
- * Generates generated code for this runtime module.
- * @param {NormalModule} module module for which the code should be generated
- * @param {GenerateContext} generateContext context for generate
- * @returns {Source | null} generated code
- */
- generate(module, generateContext) {
- if (!module.originalSource()) return new RawSource("");
- const { type, getData } = generateContext;
- const content = this._rewrite(module, generateContext);
- // Hash the *rewritten* content (so the filename tracks the icons) and reuse
- // the asset filename machinery, so a plain (icon-less) manifest emits like a
- // plain asset (`output.assetModuleFilename`, `[name]`, `[ext]`).
- const outputOptions = generateContext.runtimeTemplate.outputOptions;
- const hash = createHash(outputOptions.hashFunction);
- if (outputOptions.hashSalt) hash.update(outputOptions.hashSalt);
- hash.update(content);
- const fullContentHash = /** @type {string} */ (
- hash.digest(outputOptions.hashDigest)
- );
- const contentHash = nonNumericOnlyHash(
- fullContentHash,
- /** @type {number} */ (outputOptions.hashDigestLength)
- );
- const { filename, assetInfo } = AssetGenerator.getFilenameWithInfo(
- module,
- { filename: undefined, outputPath: undefined },
- generateContext,
- contentHash,
- fullContentHash
- );
- const data = getData ? getData() : undefined;
- if (data) {
- data.set("filename", filename);
- data.set("assetInfo", assetInfo);
- if (type === ASSET_URL_TYPE) {
- data.set("url", {
- ...data.get("url"),
- [ASSET_URL_TYPE]: PUBLIC_PATH_AUTO + filename
- });
- }
- }
- return type === ASSET_URL_TYPE ? null : new RawSource(content);
- }
- }
- module.exports = WebManifestGenerator;
|