| 1 | "use strict";
|
|---|
| 2 |
|
|---|
| 3 | const path = require("path");
|
|---|
| 4 | const {
|
|---|
| 5 | validate
|
|---|
| 6 | } = require("schema-utils");
|
|---|
| 7 | const {
|
|---|
| 8 | SyncWaterfallHook
|
|---|
| 9 | } = require("tapable");
|
|---|
| 10 | const schema = require("./plugin-options.json");
|
|---|
| 11 | const {
|
|---|
| 12 | ABSOLUTE_PUBLIC_PATH,
|
|---|
| 13 | AUTO_PUBLIC_PATH,
|
|---|
| 14 | BASE_URI,
|
|---|
| 15 | MODULE_TYPE,
|
|---|
| 16 | SINGLE_DOT_PATH_SEGMENT,
|
|---|
| 17 | compareModulesByIdentifier,
|
|---|
| 18 | compileBooleanMatcher,
|
|---|
| 19 | getUndoPath,
|
|---|
| 20 | trueFn
|
|---|
| 21 | } = require("./utils");
|
|---|
| 22 |
|
|---|
| 23 | /** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
|
|---|
| 24 | /** @typedef {import("webpack").Compiler} Compiler */
|
|---|
| 25 | /** @typedef {import("webpack").Compilation} Compilation */
|
|---|
| 26 | /** @typedef {import("webpack").ChunkGraph} ChunkGraph */
|
|---|
| 27 | /** @typedef {import("webpack").Chunk} Chunk */
|
|---|
| 28 | /** @typedef {import("webpack").ChunkGroup} ChunkGroup */
|
|---|
| 29 | /** @typedef {import("webpack").Module} Module */
|
|---|
| 30 | /** @typedef {import("webpack").Dependency} Dependency */
|
|---|
| 31 | /** @typedef {import("webpack").sources.Source} Source */
|
|---|
| 32 | /** @typedef {import("webpack").Configuration} Configuration */
|
|---|
| 33 | /** @typedef {import("webpack").WebpackError} WebpackError */
|
|---|
| 34 | /** @typedef {import("webpack").AssetInfo} AssetInfo */
|
|---|
| 35 | /** @typedef {import("./loader.js").Dependency} LoaderDependency */
|
|---|
| 36 |
|
|---|
| 37 | /** @typedef {NonNullable<Required<Configuration>['output']['filename']>} Filename */
|
|---|
| 38 | /** @typedef {NonNullable<Required<Configuration>['output']['chunkFilename']>} ChunkFilename */
|
|---|
| 39 |
|
|---|
| 40 | /**
|
|---|
| 41 | * @typedef {object} LoaderOptions
|
|---|
| 42 | * @property {string | ((resourcePath: string, rootContext: string) => string)=} publicPath public path
|
|---|
| 43 | * @property {boolean=} emit true when need to emit, otherwise false
|
|---|
| 44 | * @property {boolean=} esModule need to generate ES module syntax
|
|---|
| 45 | * @property {string=} layer a layer
|
|---|
| 46 | * @property {boolean=} defaultExport true when need to use default export, otherwise false
|
|---|
| 47 | */
|
|---|
| 48 |
|
|---|
| 49 | /**
|
|---|
| 50 | * @typedef {object} PluginOptions
|
|---|
| 51 | * @property {Filename=} filename filename
|
|---|
| 52 | * @property {ChunkFilename=} chunkFilename chunk filename
|
|---|
| 53 | * @property {boolean=} ignoreOrder true when need to ignore order, otherwise false
|
|---|
| 54 | * @property {string | ((linkTag: HTMLLinkElement) => void)=} insert link insert place or a custom insert function
|
|---|
| 55 | * @property {Record<string, string>=} attributes link attributes
|
|---|
| 56 | * @property {string | false | "text/css"=} linkType value of a link type attribute
|
|---|
| 57 | * @property {boolean=} runtime true when need to generate runtime code, otherwise false
|
|---|
| 58 | * @property {boolean=} experimentalUseImportModule true when need to use `experimentalUseImportModule` API, otherwise false
|
|---|
| 59 | */
|
|---|
| 60 |
|
|---|
| 61 | /**
|
|---|
| 62 | * @typedef {object} NormalizedPluginOptions
|
|---|
| 63 | * @property {Filename=} filename filename
|
|---|
| 64 | * @property {ChunkFilename=} chunkFilename chunk filename
|
|---|
| 65 | * @property {boolean} ignoreOrder true when need to ignore order, otherwise false
|
|---|
| 66 | * @property {string | ((linkTag: HTMLLinkElement) => void)=} insert a link insert place or a custom insert function
|
|---|
| 67 | * @property {Record<string, string>=} attributes link attributes
|
|---|
| 68 | * @property {string | false | "text/css"=} linkType value of a link type attribute
|
|---|
| 69 | * @property {boolean} runtime true when need to generate runtime code, otherwise false
|
|---|
| 70 | * @property {boolean=} experimentalUseImportModule true when need to use `experimentalUseImportModule` API, otherwise false
|
|---|
| 71 | */
|
|---|
| 72 |
|
|---|
| 73 | /**
|
|---|
| 74 | * @typedef {object} RuntimeOptions
|
|---|
| 75 | * @property {string | ((linkTag: HTMLLinkElement) => void)=} insert a link insert place or a custom insert function
|
|---|
| 76 | * @property {string | false | "text/css"} linkType value of a link type attribute
|
|---|
| 77 | * @property {Record<string, string>=} attributes link attributes
|
|---|
| 78 | */
|
|---|
| 79 |
|
|---|
| 80 | const pluginName = "mini-css-extract-plugin";
|
|---|
| 81 | const pluginSymbol = Symbol(pluginName);
|
|---|
| 82 | const DEFAULT_FILENAME = "[name].css";
|
|---|
| 83 | /**
|
|---|
| 84 | * @type {Set<string>}
|
|---|
| 85 | */
|
|---|
| 86 | const TYPES = new Set([MODULE_TYPE]);
|
|---|
| 87 | /**
|
|---|
| 88 | * @type {ReturnType<Module["codeGeneration"]>}
|
|---|
| 89 | */
|
|---|
| 90 | const CODE_GENERATION_RESULT = {
|
|---|
| 91 | sources: new Map(),
|
|---|
| 92 | runtimeRequirements: new Set()
|
|---|
| 93 | };
|
|---|
| 94 |
|
|---|
| 95 | // eslint-disable-next-line jsdoc/reject-any-type
|
|---|
| 96 | /** @typedef {{ context: string | null, identifier: string, identifierIndex: number, content: Buffer, sourceMap?: Buffer, media?: string, supports?: string, layer?: any, assetsInfo?: Map<string, AssetInfo>, assets?: { [key: string]: Source } }} CssModuleDependency */
|
|---|
| 97 | /** @typedef {Module & { content: Buffer, media?: string, sourceMap?: Buffer, supports?: string, layer?: string, assets?: { [key: string]: Source }, assetsInfo?: Map<string, AssetInfo> }} CssModule */
|
|---|
| 98 | /** @typedef {{ new (dependency: CssModuleDependency): CssModule }} CssModuleConstructor */
|
|---|
| 99 | /** @typedef {Dependency & CssModuleDependency} CssDependency */
|
|---|
| 100 | /** @typedef {Omit<LoaderDependency, "context">} CssDependencyOptions */
|
|---|
| 101 | /** @typedef {{ new (loaderDependency: CssDependencyOptions, context: string | null, identifierIndex: number): CssDependency }} CssDependencyConstructor */
|
|---|
| 102 |
|
|---|
| 103 | /**
|
|---|
| 104 | * @typedef {object} VarNames
|
|---|
| 105 | * @property {string} tag tag
|
|---|
| 106 | * @property {string} chunkId chunk id
|
|---|
| 107 | * @property {string} href href
|
|---|
| 108 | * @property {string} resolve resolve
|
|---|
| 109 | * @property {string} reject reject
|
|---|
| 110 | */
|
|---|
| 111 |
|
|---|
| 112 | /**
|
|---|
| 113 | * @typedef {object} MiniCssExtractPluginCompilationHooks
|
|---|
| 114 | * @property {import("tapable").SyncWaterfallHook<[string, VarNames], string>} beforeTagInsert before tag insert hook
|
|---|
| 115 | * @property {SyncWaterfallHook<[string, Chunk]>} linkPreload link preload hook
|
|---|
| 116 | * @property {SyncWaterfallHook<[string, Chunk]>} linkPrefetch link prefetch hook
|
|---|
| 117 | */
|
|---|
| 118 |
|
|---|
| 119 | /**
|
|---|
| 120 | * @type {WeakMap<Compiler["webpack"], CssModuleConstructor>}
|
|---|
| 121 | */
|
|---|
| 122 | const cssModuleCache = new WeakMap();
|
|---|
| 123 | /**
|
|---|
| 124 | * @type {WeakMap<Compiler["webpack"], CssDependencyConstructor>}
|
|---|
| 125 | */
|
|---|
| 126 | const cssDependencyCache = new WeakMap();
|
|---|
| 127 | /**
|
|---|
| 128 | * @type {WeakSet<Compiler["webpack"]>}
|
|---|
| 129 | */
|
|---|
| 130 | const registered = new WeakSet();
|
|---|
| 131 |
|
|---|
| 132 | /** @type {WeakMap<Compilation, MiniCssExtractPluginCompilationHooks>} */
|
|---|
| 133 | const compilationHooksMap = new WeakMap();
|
|---|
| 134 | class MiniCssExtractPlugin {
|
|---|
| 135 | /**
|
|---|
| 136 | * @param {Compiler["webpack"]} webpack webpack
|
|---|
| 137 | * @returns {CssModuleConstructor} CSS module constructor
|
|---|
| 138 | */
|
|---|
| 139 | static getCssModule(webpack) {
|
|---|
| 140 | /**
|
|---|
| 141 | * Prevent creation of multiple CssModule classes to allow other integrations to get the current CssModule.
|
|---|
| 142 | */
|
|---|
| 143 | if (cssModuleCache.has(webpack)) {
|
|---|
| 144 | return /** @type {CssModuleConstructor} */cssModuleCache.get(webpack);
|
|---|
| 145 | }
|
|---|
| 146 | class CssModule extends webpack.Module {
|
|---|
| 147 | /**
|
|---|
| 148 | * @param {CssModuleDependency} dependency css module dependency
|
|---|
| 149 | */
|
|---|
| 150 | constructor({
|
|---|
| 151 | context,
|
|---|
| 152 | identifier,
|
|---|
| 153 | identifierIndex,
|
|---|
| 154 | content,
|
|---|
| 155 | layer,
|
|---|
| 156 | supports,
|
|---|
| 157 | media,
|
|---|
| 158 | sourceMap,
|
|---|
| 159 | assets,
|
|---|
| 160 | assetsInfo
|
|---|
| 161 | }) {
|
|---|
| 162 | super(MODULE_TYPE, /** @type {string | undefined} */context);
|
|---|
| 163 | this.id = "";
|
|---|
| 164 | this._context = context;
|
|---|
| 165 | this._identifier = identifier;
|
|---|
| 166 | this._identifierIndex = identifierIndex;
|
|---|
| 167 | this.content = content;
|
|---|
| 168 | this.layer = layer;
|
|---|
| 169 | this.supports = supports;
|
|---|
| 170 | this.media = media;
|
|---|
| 171 | this.sourceMap = sourceMap;
|
|---|
| 172 | this.assets = assets;
|
|---|
| 173 | this.assetsInfo = assetsInfo;
|
|---|
| 174 | this._needBuild = true;
|
|---|
| 175 | }
|
|---|
| 176 |
|
|---|
| 177 | // no source() so webpack 4 doesn't do add stuff to the bundle
|
|---|
| 178 |
|
|---|
| 179 | size() {
|
|---|
| 180 | return this.content.length;
|
|---|
| 181 | }
|
|---|
| 182 | identifier() {
|
|---|
| 183 | return `css|${this._identifier}|${this._identifierIndex}|${this.layer || ""}|${this.supports || ""}|${this.media}}}`;
|
|---|
| 184 | }
|
|---|
| 185 |
|
|---|
| 186 | /**
|
|---|
| 187 | * @param {Parameters<Module["readableIdentifier"]>[0]} requestShortener request shortener
|
|---|
| 188 | * @returns {ReturnType<Module["readableIdentifier"]>} readable identifier
|
|---|
| 189 | */
|
|---|
| 190 | readableIdentifier(requestShortener) {
|
|---|
| 191 | return `css ${requestShortener.shorten(this._identifier)}${this._identifierIndex ? ` (${this._identifierIndex})` : ""}${this.layer ? ` (layer ${this.layer})` : ""}${this.supports ? ` (supports ${this.supports})` : ""}${this.media ? ` (media ${this.media})` : ""}`;
|
|---|
| 192 | }
|
|---|
| 193 | getSourceTypes() {
|
|---|
| 194 | return TYPES;
|
|---|
| 195 | }
|
|---|
| 196 | codeGeneration() {
|
|---|
| 197 | return CODE_GENERATION_RESULT;
|
|---|
| 198 | }
|
|---|
| 199 | nameForCondition() {
|
|---|
| 200 | const resource = /** @type {string} */
|
|---|
| 201 | this._identifier.split("!").pop();
|
|---|
| 202 | const idx = resource.indexOf("?");
|
|---|
| 203 | if (idx >= 0) {
|
|---|
| 204 | return resource.slice(0, Math.max(0, idx));
|
|---|
| 205 | }
|
|---|
| 206 | return resource;
|
|---|
| 207 | }
|
|---|
| 208 |
|
|---|
| 209 | /**
|
|---|
| 210 | * @param {Module} module a module
|
|---|
| 211 | */
|
|---|
| 212 | updateCacheModule(module) {
|
|---|
| 213 | if (!this.content.equals(/** @type {CssModule} */module.content) || this.layer !== /** @type {CssModule} */module.layer || this.supports !== /** @type {CssModule} */module.supports || this.media !== /** @type {CssModule} */module.media || (this.sourceMap ? !this.sourceMap.equals(/** @type {Uint8Array} * */
|
|---|
| 214 | /** @type {CssModule} */module.sourceMap) : false) || this.assets !== /** @type {CssModule} */module.assets || this.assetsInfo !== /** @type {CssModule} */module.assetsInfo) {
|
|---|
| 215 | this._needBuild = true;
|
|---|
| 216 | this.content = /** @type {CssModule} */module.content;
|
|---|
| 217 | this.layer = /** @type {CssModule} */module.layer;
|
|---|
| 218 | this.supports = /** @type {CssModule} */module.supports;
|
|---|
| 219 | this.media = /** @type {CssModule} */module.media;
|
|---|
| 220 | this.sourceMap = /** @type {CssModule} */module.sourceMap;
|
|---|
| 221 | this.assets = /** @type {CssModule} */module.assets;
|
|---|
| 222 | this.assetsInfo = /** @type {CssModule} */module.assetsInfo;
|
|---|
| 223 | }
|
|---|
| 224 | }
|
|---|
| 225 | needRebuild() {
|
|---|
| 226 | return this._needBuild;
|
|---|
| 227 | }
|
|---|
| 228 |
|
|---|
| 229 | /**
|
|---|
| 230 | * @param {Parameters<Module["needBuild"]>[0]} context context info
|
|---|
| 231 | * @param {Parameters<Module["needBuild"]>[1]} callback callback function, returns true, if the module needs a rebuild
|
|---|
| 232 | */
|
|---|
| 233 | needBuild(context, callback) {
|
|---|
| 234 | callback(undefined, this._needBuild);
|
|---|
| 235 | }
|
|---|
| 236 |
|
|---|
| 237 | /**
|
|---|
| 238 | * @param {Parameters<Module["build"]>[0]} options options
|
|---|
| 239 | * @param {Parameters<Module["build"]>[1]} compilation compilation
|
|---|
| 240 | * @param {Parameters<Module["build"]>[2]} resolver resolver
|
|---|
| 241 | * @param {Parameters<Module["build"]>[3]} fileSystem file system
|
|---|
| 242 | * @param {Parameters<Module["build"]>[4]} callback callback
|
|---|
| 243 | */
|
|---|
| 244 | build(options, compilation, resolver, fileSystem, callback) {
|
|---|
| 245 | this.buildInfo = {
|
|---|
| 246 | assets: this.assets,
|
|---|
| 247 | assetsInfo: this.assetsInfo,
|
|---|
| 248 | cacheable: true,
|
|---|
| 249 | hash: (/** @type {string} */
|
|---|
| 250 |
|
|---|
| 251 | this._computeHash(/** @type {string} */
|
|---|
| 252 | compilation.outputOptions.hashFunction))
|
|---|
| 253 | };
|
|---|
| 254 | this.buildMeta = {};
|
|---|
| 255 | this._needBuild = false;
|
|---|
| 256 | callback();
|
|---|
| 257 | }
|
|---|
| 258 |
|
|---|
| 259 | /**
|
|---|
| 260 | * @private
|
|---|
| 261 | * @param {string} hashFunction hash function
|
|---|
| 262 | * @returns {string | Buffer} hash digest
|
|---|
| 263 | */
|
|---|
| 264 | _computeHash(hashFunction) {
|
|---|
| 265 | const hash = webpack.util.createHash(hashFunction);
|
|---|
| 266 | hash.update(this.content);
|
|---|
| 267 | if (this.layer) {
|
|---|
| 268 | hash.update(this.layer);
|
|---|
| 269 | }
|
|---|
| 270 | hash.update(this.supports || "");
|
|---|
| 271 | hash.update(this.media || "");
|
|---|
| 272 | hash.update(this.sourceMap || "");
|
|---|
| 273 | return hash.digest("hex");
|
|---|
| 274 | }
|
|---|
| 275 |
|
|---|
| 276 | /**
|
|---|
| 277 | * @param {Parameters<Module["updateHash"]>[0]} hash hash
|
|---|
| 278 | * @param {Parameters<Module["updateHash"]>[1]} context context
|
|---|
| 279 | */
|
|---|
| 280 | updateHash(hash, context) {
|
|---|
| 281 | super.updateHash(hash, context);
|
|---|
| 282 | hash.update(/** @type {string} */
|
|---|
| 283 | /** @type {NonNullable<Module["buildInfo"]>} */
|
|---|
| 284 | this.buildInfo.hash);
|
|---|
| 285 | }
|
|---|
| 286 |
|
|---|
| 287 | /**
|
|---|
| 288 | * @param {Parameters<Module["serialize"]>[0]} context serializer context
|
|---|
| 289 | */
|
|---|
| 290 | serialize(context) {
|
|---|
| 291 | const {
|
|---|
| 292 | write
|
|---|
| 293 | } = context;
|
|---|
| 294 | write(this._context);
|
|---|
| 295 | write(this._identifier);
|
|---|
| 296 | write(this._identifierIndex);
|
|---|
| 297 | write(this.content);
|
|---|
| 298 | write(this.layer);
|
|---|
| 299 | write(this.supports);
|
|---|
| 300 | write(this.media);
|
|---|
| 301 | write(this.sourceMap);
|
|---|
| 302 | write(this.assets);
|
|---|
| 303 | write(this.assetsInfo);
|
|---|
| 304 | write(this._needBuild);
|
|---|
| 305 | super.serialize(context);
|
|---|
| 306 | }
|
|---|
| 307 |
|
|---|
| 308 | /**
|
|---|
| 309 | * @param {Parameters<Module["deserialize"]>[0]} context deserializer context
|
|---|
| 310 | */
|
|---|
| 311 | deserialize(context) {
|
|---|
| 312 | this._needBuild = context.read();
|
|---|
| 313 | super.deserialize(context);
|
|---|
| 314 | }
|
|---|
| 315 | }
|
|---|
| 316 | cssModuleCache.set(webpack, CssModule);
|
|---|
| 317 | webpack.util.serialization.register(CssModule, path.resolve(__dirname, "CssModule"), null, {
|
|---|
| 318 | serialize(instance, context) {
|
|---|
| 319 | instance.serialize(context);
|
|---|
| 320 | },
|
|---|
| 321 | deserialize(context) {
|
|---|
| 322 | const {
|
|---|
| 323 | read
|
|---|
| 324 | } = context;
|
|---|
| 325 | const contextModule = read();
|
|---|
| 326 | const identifier = read();
|
|---|
| 327 | const identifierIndex = read();
|
|---|
| 328 | const content = read();
|
|---|
| 329 | const layer = read();
|
|---|
| 330 | const supports = read();
|
|---|
| 331 | const media = read();
|
|---|
| 332 | const sourceMap = read();
|
|---|
| 333 | const assets = read();
|
|---|
| 334 | const assetsInfo = read();
|
|---|
| 335 | const dep = new CssModule({
|
|---|
| 336 | context: contextModule,
|
|---|
| 337 | identifier,
|
|---|
| 338 | identifierIndex,
|
|---|
| 339 | content,
|
|---|
| 340 | layer,
|
|---|
| 341 | supports,
|
|---|
| 342 | media,
|
|---|
| 343 | sourceMap,
|
|---|
| 344 | assets,
|
|---|
| 345 | assetsInfo
|
|---|
| 346 | });
|
|---|
| 347 | dep.deserialize(context);
|
|---|
| 348 | return dep;
|
|---|
| 349 | }
|
|---|
| 350 | });
|
|---|
| 351 | return CssModule;
|
|---|
| 352 | }
|
|---|
| 353 |
|
|---|
| 354 | /**
|
|---|
| 355 | * @param {Compiler["webpack"]} webpack webpack
|
|---|
| 356 | * @returns {CssDependencyConstructor} CSS dependency constructor
|
|---|
| 357 | */
|
|---|
| 358 | static getCssDependency(webpack) {
|
|---|
| 359 | /**
|
|---|
| 360 | * Prevent creation of multiple CssDependency classes to allow other integrations to get the current CssDependency.
|
|---|
| 361 | */
|
|---|
| 362 | if (cssDependencyCache.has(webpack)) {
|
|---|
| 363 | return /** @type {CssDependencyConstructor} */cssDependencyCache.get(webpack);
|
|---|
| 364 | }
|
|---|
| 365 | class CssDependency extends webpack.Dependency {
|
|---|
| 366 | /**
|
|---|
| 367 | * @param {CssDependencyOptions} loaderDependency loader dependency
|
|---|
| 368 | * @param {string | null} context context
|
|---|
| 369 | * @param {number} identifierIndex identifier index
|
|---|
| 370 | */
|
|---|
| 371 | constructor({
|
|---|
| 372 | identifier,
|
|---|
| 373 | content,
|
|---|
| 374 | layer,
|
|---|
| 375 | supports,
|
|---|
| 376 | media,
|
|---|
| 377 | sourceMap
|
|---|
| 378 | }, context, identifierIndex) {
|
|---|
| 379 | super();
|
|---|
| 380 | this.identifier = identifier;
|
|---|
| 381 | this.identifierIndex = identifierIndex;
|
|---|
| 382 | this.content = content;
|
|---|
| 383 | this.layer = layer;
|
|---|
| 384 | this.supports = supports;
|
|---|
| 385 | this.media = media;
|
|---|
| 386 | this.sourceMap = sourceMap;
|
|---|
| 387 | this.context = context;
|
|---|
| 388 | /** @type {{ [key: string]: Source } | undefined}} */
|
|---|
| 389 | this.assets = undefined;
|
|---|
| 390 | /** @type {Map<string, AssetInfo> | undefined} */
|
|---|
| 391 | this.assetsInfo = undefined;
|
|---|
| 392 | }
|
|---|
| 393 |
|
|---|
| 394 | /**
|
|---|
| 395 | * @returns {ReturnType<Dependency["getResourceIdentifier"]>} a resource identifier
|
|---|
| 396 | */
|
|---|
| 397 | getResourceIdentifier() {
|
|---|
| 398 | return `css-module-${this.identifier}-${this.identifierIndex}`;
|
|---|
| 399 | }
|
|---|
| 400 |
|
|---|
| 401 | /**
|
|---|
| 402 | * @returns {ReturnType<Dependency["getModuleEvaluationSideEffectsState"]>} side effect state
|
|---|
| 403 | */
|
|---|
| 404 | getModuleEvaluationSideEffectsState() {
|
|---|
| 405 | return webpack.ModuleGraphConnection.TRANSITIVE_ONLY;
|
|---|
| 406 | }
|
|---|
| 407 |
|
|---|
| 408 | /**
|
|---|
| 409 | * @param {Parameters<Dependency["serialize"]>[0]} context serializer context
|
|---|
| 410 | */
|
|---|
| 411 | serialize(context) {
|
|---|
| 412 | const {
|
|---|
| 413 | write
|
|---|
| 414 | } = context;
|
|---|
| 415 | write(this.identifier);
|
|---|
| 416 | write(this.content);
|
|---|
| 417 | write(this.layer);
|
|---|
| 418 | write(this.supports);
|
|---|
| 419 | write(this.media);
|
|---|
| 420 | write(this.sourceMap);
|
|---|
| 421 | write(this.context);
|
|---|
| 422 | write(this.identifierIndex);
|
|---|
| 423 | write(this.assets);
|
|---|
| 424 | write(this.assetsInfo);
|
|---|
| 425 | super.serialize(context);
|
|---|
| 426 | }
|
|---|
| 427 |
|
|---|
| 428 | /**
|
|---|
| 429 | * @param {Parameters<Dependency["deserialize"]>[0]} context deserializer context
|
|---|
| 430 | */
|
|---|
| 431 | deserialize(context) {
|
|---|
| 432 | super.deserialize(context);
|
|---|
| 433 | }
|
|---|
| 434 | }
|
|---|
| 435 | cssDependencyCache.set(webpack, CssDependency);
|
|---|
| 436 | webpack.util.serialization.register(CssDependency, path.resolve(__dirname, "CssDependency"), null, {
|
|---|
| 437 | serialize(instance, context) {
|
|---|
| 438 | instance.serialize(context);
|
|---|
| 439 | },
|
|---|
| 440 | deserialize(context) {
|
|---|
| 441 | const {
|
|---|
| 442 | read
|
|---|
| 443 | } = context;
|
|---|
| 444 | const dep = new CssDependency({
|
|---|
| 445 | identifier: read(),
|
|---|
| 446 | content: read(),
|
|---|
| 447 | layer: read(),
|
|---|
| 448 | supports: read(),
|
|---|
| 449 | media: read(),
|
|---|
| 450 | sourceMap: read()
|
|---|
| 451 | }, read(), read());
|
|---|
| 452 | const assets = read();
|
|---|
| 453 | const assetsInfo = read();
|
|---|
| 454 | dep.assets = assets;
|
|---|
| 455 | dep.assetsInfo = assetsInfo;
|
|---|
| 456 | dep.deserialize(context);
|
|---|
| 457 | return dep;
|
|---|
| 458 | }
|
|---|
| 459 | });
|
|---|
| 460 | return CssDependency;
|
|---|
| 461 | }
|
|---|
| 462 |
|
|---|
| 463 | /**
|
|---|
| 464 | * Returns all hooks for the given compilation
|
|---|
| 465 | * @param {Compilation} compilation the compilation
|
|---|
| 466 | * @returns {MiniCssExtractPluginCompilationHooks} hooks
|
|---|
| 467 | */
|
|---|
| 468 | static getCompilationHooks(compilation) {
|
|---|
| 469 | let hooks = compilationHooksMap.get(compilation);
|
|---|
| 470 | if (!hooks) {
|
|---|
| 471 | hooks = {
|
|---|
| 472 | beforeTagInsert: new SyncWaterfallHook(["source", "varNames"], "string"),
|
|---|
| 473 | linkPreload: new SyncWaterfallHook(["source", "chunk"]),
|
|---|
| 474 | linkPrefetch: new SyncWaterfallHook(["source", "chunk"])
|
|---|
| 475 | };
|
|---|
| 476 | compilationHooksMap.set(compilation, hooks);
|
|---|
| 477 | }
|
|---|
| 478 | return hooks;
|
|---|
| 479 | }
|
|---|
| 480 |
|
|---|
| 481 | /**
|
|---|
| 482 | * @param {PluginOptions=} options options
|
|---|
| 483 | */
|
|---|
| 484 | constructor(options = {}) {
|
|---|
| 485 | validate(/** @type {Schema} */schema, options, {
|
|---|
| 486 | baseDataPath: "options"
|
|---|
| 487 | });
|
|---|
| 488 |
|
|---|
| 489 | /**
|
|---|
| 490 | * @private
|
|---|
| 491 | * @type {WeakMap<Chunk, Set<CssModule>>}
|
|---|
| 492 | */
|
|---|
| 493 | this._sortedModulesCache = new WeakMap();
|
|---|
| 494 |
|
|---|
| 495 | /**
|
|---|
| 496 | * @private
|
|---|
| 497 | * @type {NormalizedPluginOptions}
|
|---|
| 498 | */
|
|---|
| 499 | this.options = {
|
|---|
| 500 | ignoreOrder: false,
|
|---|
| 501 | // TODO remove in the next major release
|
|---|
| 502 | experimentalUseImportModule: undefined,
|
|---|
| 503 | runtime: true,
|
|---|
| 504 | ...options
|
|---|
| 505 | };
|
|---|
| 506 |
|
|---|
| 507 | /**
|
|---|
| 508 | * @private
|
|---|
| 509 | * @type {RuntimeOptions}
|
|---|
| 510 | */
|
|---|
| 511 | this.runtimeOptions = {
|
|---|
| 512 | insert: options.insert,
|
|---|
| 513 | linkType:
|
|---|
| 514 | // Todo in next major release set default to "false"
|
|---|
| 515 | typeof options.linkType === "boolean" && /** @type {boolean} */options.linkType === true || typeof options.linkType === "undefined" ? "text/css" : options.linkType,
|
|---|
| 516 | attributes: options.attributes
|
|---|
| 517 | };
|
|---|
| 518 | }
|
|---|
| 519 |
|
|---|
| 520 | /**
|
|---|
| 521 | * @param {Compiler} compiler compiler
|
|---|
| 522 | */
|
|---|
| 523 | apply(compiler) {
|
|---|
| 524 | // Finally normalize filenames based on compiler options
|
|---|
| 525 | const normalizedFilename = this.options.filename || compiler.options.output.cssFilename || DEFAULT_FILENAME;
|
|---|
| 526 | let normalizedChunkFilename = this.options.chunkFilename || compiler.options.output.cssChunkFilename;
|
|---|
| 527 | if (!normalizedChunkFilename) {
|
|---|
| 528 | if (typeof normalizedFilename !== "function") {
|
|---|
| 529 | const hasName = /** @type {string} */normalizedFilename.includes("[name]");
|
|---|
| 530 | const hasId = /** @type {string} */normalizedFilename.includes("[id]");
|
|---|
| 531 | const hasChunkHash = /** @type {string} */
|
|---|
| 532 | normalizedFilename.includes("[chunkhash]");
|
|---|
| 533 | const hasContentHash = /** @type {string} */
|
|---|
| 534 | normalizedFilename.includes("[contenthash]");
|
|---|
| 535 |
|
|---|
| 536 | // Anything changing depending on chunk is fine
|
|---|
| 537 | if (hasChunkHash || hasContentHash || hasName || hasId) {
|
|---|
| 538 | normalizedChunkFilename = normalizedFilename;
|
|---|
| 539 | } else {
|
|---|
| 540 | // Otherwise prefix "[id]." in front of the basename to make it changing
|
|---|
| 541 | normalizedChunkFilename = /** @type {string} */
|
|---|
| 542 | normalizedFilename.replace(/(^|\/)([^/]*(?:\?|$))/, "$1[id].$2");
|
|---|
| 543 | }
|
|---|
| 544 | } else {
|
|---|
| 545 | normalizedChunkFilename = "[id].css";
|
|---|
| 546 | }
|
|---|
| 547 | }
|
|---|
| 548 | const {
|
|---|
| 549 | webpack
|
|---|
| 550 | } = compiler;
|
|---|
| 551 | if (this.options.experimentalUseImportModule && typeof (/** @type {Compiler["options"]["experiments"] & { executeModule?: boolean }} */
|
|---|
| 552 | compiler.options.experiments.executeModule) === "undefined") {
|
|---|
| 553 | /** @type {Compiler["options"]["experiments"] & { executeModule?: boolean }} */
|
|---|
| 554 |
|
|---|
| 555 | // @ts-expect-error TODO remove in the next major release
|
|---|
| 556 | compiler.options.experiments.executeModule = true;
|
|---|
| 557 | }
|
|---|
| 558 |
|
|---|
| 559 | // TODO bug in webpack, remove it after it will be fixed
|
|---|
| 560 | // webpack tries to `require` loader firstly when serializer doesn't found
|
|---|
| 561 | if (!registered.has(webpack)) {
|
|---|
| 562 | registered.add(webpack);
|
|---|
| 563 | webpack.util.serialization.registerLoader(/^mini-css-extract-plugin\//, trueFn);
|
|---|
| 564 | }
|
|---|
| 565 | const {
|
|---|
| 566 | splitChunks
|
|---|
| 567 | } = compiler.options.optimization;
|
|---|
| 568 | if (splitChunks && /** @type {string[]} */splitChunks.defaultSizeTypes.includes("...")) {
|
|---|
| 569 | /** @type {string[]} */
|
|---|
| 570 | splitChunks.defaultSizeTypes.push(MODULE_TYPE);
|
|---|
| 571 | }
|
|---|
| 572 | const CssModule = MiniCssExtractPlugin.getCssModule(webpack);
|
|---|
| 573 | const CssDependency = MiniCssExtractPlugin.getCssDependency(webpack);
|
|---|
| 574 | const {
|
|---|
| 575 | NormalModule
|
|---|
| 576 | } = compiler.webpack;
|
|---|
| 577 | compiler.hooks.compilation.tap(pluginName, compilation => {
|
|---|
| 578 | const {
|
|---|
| 579 | loader: normalModuleHook
|
|---|
| 580 | } = NormalModule.getCompilationHooks(compilation);
|
|---|
| 581 | normalModuleHook.tap(pluginName,
|
|---|
| 582 | /**
|
|---|
| 583 | * @param {object} loaderContext loader context
|
|---|
| 584 | */
|
|---|
| 585 | loaderContext => {
|
|---|
| 586 | /** @type {object & { [pluginSymbol]: { experimentalUseImportModule: boolean | undefined } }} */
|
|---|
| 587 | loaderContext[pluginSymbol] = {
|
|---|
| 588 | experimentalUseImportModule: this.options.experimentalUseImportModule
|
|---|
| 589 | };
|
|---|
| 590 | });
|
|---|
| 591 | });
|
|---|
| 592 | compiler.hooks.thisCompilation.tap(pluginName, compilation => {
|
|---|
| 593 | class CssModuleFactory {
|
|---|
| 594 | /**
|
|---|
| 595 | * @param {{ dependencies: Dependency[] }} dependencies
|
|---|
| 596 | * @param {(err?: null | Error, result?: CssModule) => void} callback
|
|---|
| 597 | */
|
|---|
| 598 |
|
|---|
| 599 | create({
|
|---|
| 600 | dependencies: [dependency]
|
|---|
| 601 | }, callback) {
|
|---|
| 602 | callback(undefined, new CssModule(/** @type {CssDependency} */dependency));
|
|---|
| 603 | }
|
|---|
| 604 | }
|
|---|
| 605 | compilation.dependencyFactories.set(CssDependency,
|
|---|
| 606 | // @ts-expect-error TODO fix in the next major release and fix using `CssModuleFactory extends webpack.ModuleFactory`
|
|---|
| 607 | new CssModuleFactory());
|
|---|
| 608 | class CssDependencyTemplate {
|
|---|
| 609 | apply() {}
|
|---|
| 610 | }
|
|---|
| 611 | compilation.dependencyTemplates.set(CssDependency, new CssDependencyTemplate());
|
|---|
| 612 | compilation.hooks.renderManifest.tap(pluginName,
|
|---|
| 613 | /**
|
|---|
| 614 | * @param {ReturnType<Compilation["getRenderManifest"]>} result result
|
|---|
| 615 | * @param {Parameters<Compilation["getRenderManifest"]>[0]} chunk chunk
|
|---|
| 616 | * @returns {ReturnType<Compilation["getRenderManifest"]>} a rendered manifest
|
|---|
| 617 | */
|
|---|
| 618 | (result, {
|
|---|
| 619 | chunk
|
|---|
| 620 | }) => {
|
|---|
| 621 | const {
|
|---|
| 622 | chunkGraph
|
|---|
| 623 | } = compilation;
|
|---|
| 624 | const {
|
|---|
| 625 | HotUpdateChunk
|
|---|
| 626 | } = webpack;
|
|---|
| 627 |
|
|---|
| 628 | // We don't need hot update chunks for css
|
|---|
| 629 | // We will use the real asset instead to update
|
|---|
| 630 | if (chunk instanceof HotUpdateChunk) {
|
|---|
| 631 | return result;
|
|---|
| 632 | }
|
|---|
| 633 | const renderedModules = /** @type {CssModule[]} */
|
|---|
| 634 |
|
|---|
| 635 | [...this.getChunkModules(chunk, chunkGraph)].filter(module => module.type === MODULE_TYPE);
|
|---|
| 636 | const filenameTemplate = /** @type {string} */
|
|---|
| 637 |
|
|---|
| 638 | chunk.canBeInitial() ? normalizedFilename : normalizedChunkFilename;
|
|---|
| 639 | if (renderedModules.length > 0) {
|
|---|
| 640 | result.push({
|
|---|
| 641 | render: () => this.renderContentAsset(compiler, compilation, chunk, renderedModules, compilation.runtimeTemplate.requestShortener, filenameTemplate, {
|
|---|
| 642 | contentHashType: MODULE_TYPE,
|
|---|
| 643 | chunk
|
|---|
| 644 | }),
|
|---|
| 645 | filenameTemplate,
|
|---|
| 646 | pathOptions: {
|
|---|
| 647 | chunk,
|
|---|
| 648 | contentHashType: MODULE_TYPE
|
|---|
| 649 | },
|
|---|
| 650 | identifier: `${pluginName}.${chunk.id}`,
|
|---|
| 651 | hash: chunk.contentHash[MODULE_TYPE]
|
|---|
| 652 | });
|
|---|
| 653 | }
|
|---|
| 654 | return result;
|
|---|
| 655 | });
|
|---|
| 656 | compilation.hooks.contentHash.tap(pluginName, chunk => {
|
|---|
| 657 | const {
|
|---|
| 658 | outputOptions,
|
|---|
| 659 | chunkGraph
|
|---|
| 660 | } = compilation;
|
|---|
| 661 | const modules = this.sortModules(compilation, chunk, /** @type {CssModule[]} */
|
|---|
| 662 | chunkGraph.getChunkModulesIterableBySourceType(chunk, MODULE_TYPE), compilation.runtimeTemplate.requestShortener);
|
|---|
| 663 | if (modules && modules.size > 0) {
|
|---|
| 664 | const {
|
|---|
| 665 | hashFunction,
|
|---|
| 666 | hashDigest,
|
|---|
| 667 | hashDigestLength
|
|---|
| 668 | } = outputOptions;
|
|---|
| 669 | const {
|
|---|
| 670 | createHash
|
|---|
| 671 | } = compiler.webpack.util;
|
|---|
| 672 | const hash = createHash(/** @type {string} */hashFunction);
|
|---|
| 673 | for (const m of modules) {
|
|---|
| 674 | hash.update(chunkGraph.getModuleHash(m, chunk.runtime));
|
|---|
| 675 | }
|
|---|
| 676 | chunk.contentHash[MODULE_TYPE] = /** @type {string} */
|
|---|
| 677 | hash.digest(hashDigest).slice(0, Math.max(0, /** @type {number} */hashDigestLength));
|
|---|
| 678 | }
|
|---|
| 679 | });
|
|---|
| 680 |
|
|---|
| 681 | // All the code below is dedicated to the runtime and can be skipped when the `runtime` option is `false`
|
|---|
| 682 | if (!this.options.runtime) {
|
|---|
| 683 | return;
|
|---|
| 684 | }
|
|---|
| 685 | const {
|
|---|
| 686 | Template,
|
|---|
| 687 | RuntimeGlobals,
|
|---|
| 688 | RuntimeModule,
|
|---|
| 689 | runtime
|
|---|
| 690 | } = webpack;
|
|---|
| 691 |
|
|---|
| 692 | /**
|
|---|
| 693 | * @param {Chunk} mainChunk
|
|---|
| 694 | * @param {Compilation} compilation
|
|---|
| 695 | * @returns {Record<string, number>}
|
|---|
| 696 | */
|
|---|
| 697 |
|
|---|
| 698 | const getCssChunkObject = (mainChunk, compilation) => {
|
|---|
| 699 | /** @type {Record<string, number>} */
|
|---|
| 700 | const obj = {};
|
|---|
| 701 | const {
|
|---|
| 702 | chunkGraph
|
|---|
| 703 | } = compilation;
|
|---|
| 704 | for (const chunk of mainChunk.getAllAsyncChunks()) {
|
|---|
| 705 | const modules = chunkGraph.getOrderedChunkModulesIterable(chunk, compareModulesByIdentifier);
|
|---|
| 706 | for (const module of modules) {
|
|---|
| 707 | if (module.type === MODULE_TYPE) {
|
|---|
| 708 | obj[(/** @type {string} */chunk.id)] = 1;
|
|---|
| 709 | break;
|
|---|
| 710 | }
|
|---|
| 711 | }
|
|---|
| 712 | }
|
|---|
| 713 | return obj;
|
|---|
| 714 | };
|
|---|
| 715 |
|
|---|
| 716 | /**
|
|---|
| 717 | * @param {Chunk} chunk chunk
|
|---|
| 718 | * @param {ChunkGraph} chunkGraph chunk graph
|
|---|
| 719 | * @returns {boolean} true, when the chunk has css
|
|---|
| 720 | */
|
|---|
| 721 | function chunkHasCss(chunk, chunkGraph) {
|
|---|
| 722 | // this function replace:
|
|---|
| 723 | // const chunkHasCss = require("webpack/lib/css/CssModulesPlugin").chunkHasCss;
|
|---|
| 724 | return Boolean(chunkGraph.getChunkModulesIterableBySourceType(chunk, "css/mini-extract"));
|
|---|
| 725 | }
|
|---|
| 726 | class CssLoadingRuntimeModule extends RuntimeModule {
|
|---|
| 727 | /**
|
|---|
| 728 | * @param {Set<string>} runtimeRequirements runtime Requirements
|
|---|
| 729 | * @param {RuntimeOptions} runtimeOptions runtime options
|
|---|
| 730 | */
|
|---|
| 731 | constructor(runtimeRequirements, runtimeOptions) {
|
|---|
| 732 | super("css loading", 10);
|
|---|
| 733 | this.runtimeRequirements = runtimeRequirements;
|
|---|
| 734 | this.runtimeOptions = runtimeOptions;
|
|---|
| 735 | }
|
|---|
| 736 | generate() {
|
|---|
| 737 | const {
|
|---|
| 738 | chunkGraph,
|
|---|
| 739 | chunk,
|
|---|
| 740 | runtimeRequirements
|
|---|
| 741 | } = this;
|
|---|
| 742 | const {
|
|---|
| 743 | runtimeTemplate,
|
|---|
| 744 | outputOptions: {
|
|---|
| 745 | crossOriginLoading
|
|---|
| 746 | }
|
|---|
| 747 | } = /** @type {Compilation} */this.compilation;
|
|---|
| 748 | const chunkMap = getCssChunkObject(/** @type {Chunk} */chunk, /** @type {Compilation} */this.compilation);
|
|---|
| 749 | const withLoading = runtimeRequirements.has(RuntimeGlobals.ensureChunkHandlers) && Object.keys(chunkMap).length > 0;
|
|---|
| 750 | const withHmr = runtimeRequirements.has(RuntimeGlobals.hmrDownloadUpdateHandlers);
|
|---|
| 751 | if (!withLoading && !withHmr) {
|
|---|
| 752 | return "";
|
|---|
| 753 | }
|
|---|
| 754 | const conditionMap = /** @type {ChunkGraph} */chunkGraph.getChunkConditionMap(/** @type {Chunk} */chunk, chunkHasCss);
|
|---|
| 755 | const hasCssMatcher = compileBooleanMatcher(conditionMap);
|
|---|
| 756 | const withPrefetch = runtimeRequirements.has(RuntimeGlobals.prefetchChunkHandlers);
|
|---|
| 757 | const withPreload = runtimeRequirements.has(RuntimeGlobals.preloadChunkHandlers);
|
|---|
| 758 | const {
|
|---|
| 759 | linkPreload,
|
|---|
| 760 | linkPrefetch
|
|---|
| 761 | } = MiniCssExtractPlugin.getCompilationHooks(compilation);
|
|---|
| 762 | return Template.asString(['if (typeof document === "undefined") return;', `var createStylesheet = ${runtimeTemplate.basicFunction("chunkId, fullhref, oldTag, resolve, reject", ['var linkTag = document.createElement("link");', this.runtimeOptions.attributes ? Template.asString(Object.entries(this.runtimeOptions.attributes).map(entry => {
|
|---|
| 763 | const [key, value] = entry;
|
|---|
| 764 | return `linkTag.setAttribute(${JSON.stringify(key)}, ${JSON.stringify(value)});`;
|
|---|
| 765 | })) : "", 'linkTag.rel = "stylesheet";', this.runtimeOptions.linkType ? `linkTag.type = ${JSON.stringify(this.runtimeOptions.linkType)};` : "", `if (${RuntimeGlobals.scriptNonce}) {`, Template.indent(`linkTag.nonce = ${RuntimeGlobals.scriptNonce};`), "}", `var onLinkComplete = ${runtimeTemplate.basicFunction("event", ["// avoid mem leaks.", "linkTag.onerror = linkTag.onload = null;", "if (event.type === 'load') {", Template.indent(["resolve();"]), "} else {", Template.indent(["var errorType = event && event.type;", "var realHref = event && event.target && event.target.href || fullhref;", 'var err = new Error("Loading CSS chunk " + chunkId + " failed.\\n(" + errorType + ": " + realHref + ")");', 'err.name = "ChunkLoadError";',
|
|---|
| 766 | // TODO remove `code` in the future major release to align with webpack
|
|---|
| 767 | 'err.code = "CSS_CHUNK_LOAD_FAILED";', "err.type = errorType;", "err.request = realHref;", "if (linkTag.parentNode) linkTag.parentNode.removeChild(linkTag)", "reject(err);"]), "}"])}`, "linkTag.onerror = linkTag.onload = onLinkComplete;", "linkTag.href = fullhref;", crossOriginLoading ? Template.asString(["if (linkTag.href.indexOf(window.location.origin + '/') !== 0) {", Template.indent(`linkTag.crossOrigin = ${JSON.stringify(crossOriginLoading)};`), "}"]) : "", MiniCssExtractPlugin.getCompilationHooks(compilation).beforeTagInsert.call("", {
|
|---|
| 768 | tag: "linkTag",
|
|---|
| 769 | chunkId: "chunkId",
|
|---|
| 770 | href: "fullhref",
|
|---|
| 771 | resolve: "resolve",
|
|---|
| 772 | reject: "reject"
|
|---|
| 773 | }) || "", typeof this.runtimeOptions.insert !== "undefined" ? typeof this.runtimeOptions.insert === "function" ? `(${this.runtimeOptions.insert.toString()})(linkTag)` : Template.asString([`var target = document.querySelector("${this.runtimeOptions.insert}");`, "target.parentNode.insertBefore(linkTag, target.nextSibling);"]) : Template.asString(["if (oldTag) {", Template.indent(["oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);"]), "} else {", Template.indent(["document.head.appendChild(linkTag);"]), "}"]), "return linkTag;"])};`, `var findStylesheet = ${runtimeTemplate.basicFunction("href, fullhref", ['var existingLinkTags = document.getElementsByTagName("link");', "for(var i = 0; i < existingLinkTags.length; i++) {", Template.indent(["var tag = existingLinkTags[i];", 'var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href");', 'if(tag.rel === "stylesheet" && (dataHref === href || dataHref === fullhref)) return tag;']), "}", 'var existingStyleTags = document.getElementsByTagName("style");', "for(var i = 0; i < existingStyleTags.length; i++) {", Template.indent(["var tag = existingStyleTags[i];", 'var dataHref = tag.getAttribute("data-href");', "if(dataHref === href || dataHref === fullhref) return tag;"]), "}"])};`, `var loadStylesheet = ${runtimeTemplate.basicFunction("chunkId", `return new Promise(${runtimeTemplate.basicFunction("resolve, reject", [`var href = ${RuntimeGlobals.require}.miniCssF(chunkId);`, `var fullhref = ${RuntimeGlobals.publicPath} + href;`, "if(findStylesheet(href, fullhref)) return resolve();", "createStylesheet(chunkId, fullhref, null, resolve, reject);"])});`)}`, withLoading ? Template.asString(["// object to store loaded CSS chunks", "var installedCssChunks = {", Template.indent(/** @type {string[]} */
|
|---|
| 774 | (/** @type {Chunk} */chunk.ids).map(id => `${JSON.stringify(id)}: 0`).join(",\n")), "};", "", `${RuntimeGlobals.ensureChunkHandlers}.miniCss = ${runtimeTemplate.basicFunction("chunkId, promises", [`var cssChunks = ${JSON.stringify(chunkMap)};`, "if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]);", "else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) {", Template.indent([`promises.push(installedCssChunks[chunkId] = loadStylesheet(chunkId).then(${runtimeTemplate.basicFunction("", "installedCssChunks[chunkId] = 0;")}, ${runtimeTemplate.basicFunction("e", ["delete installedCssChunks[chunkId];", "throw e;"])}));`]), "}"])};`]) : "// no chunk loading", "", withHmr ? Template.asString(["var oldTags = [];", "var newTags = [];", `var applyHandler = ${runtimeTemplate.basicFunction("options", [`return { dispose: ${runtimeTemplate.basicFunction("", ["for(var i = 0; i < oldTags.length; i++) {", Template.indent(["var oldTag = oldTags[i];", "if(oldTag.parentNode) oldTag.parentNode.removeChild(oldTag);"]), "}", "oldTags.length = 0;"])}, apply: ${runtimeTemplate.basicFunction("", ['for(var i = 0; i < newTags.length; i++) newTags[i].rel = "stylesheet";', "newTags.length = 0;"])} };`])}`, `${RuntimeGlobals.hmrDownloadUpdateHandlers}.miniCss = ${runtimeTemplate.basicFunction("chunkIds, removedChunks, removedModules, promises, applyHandlers, updatedModulesList", ["applyHandlers.push(applyHandler);", `chunkIds.forEach(${runtimeTemplate.basicFunction("chunkId", [`var href = ${RuntimeGlobals.require}.miniCssF(chunkId);`, `var fullhref = ${RuntimeGlobals.publicPath} + href;`, "var oldTag = findStylesheet(href, fullhref);", "if(!oldTag) return;", `promises.push(new Promise(${runtimeTemplate.basicFunction("resolve, reject", [`var tag = createStylesheet(chunkId, fullhref, oldTag, ${runtimeTemplate.basicFunction("", ['tag.as = "style";', 'tag.rel = "preload";', "resolve();"])}, reject);`, "oldTags.push(oldTag);", "newTags.push(tag);"])}));`])});`])}`]) : "// no hmr", "", withPrefetch && withLoading && hasCssMatcher !== false ? `${RuntimeGlobals.prefetchChunkHandlers}.miniCss = ${runtimeTemplate.basicFunction("chunkId", [`if((!${RuntimeGlobals.hasOwnProperty}(installedCssChunks, chunkId) || installedCssChunks[chunkId] === undefined) && ${hasCssMatcher === true ? "true" : hasCssMatcher("chunkId")}) {`, Template.indent(["installedCssChunks[chunkId] = null;", linkPrefetch.call(Template.asString(["var link = document.createElement('link');", crossOriginLoading ? `link.crossOrigin = ${JSON.stringify(crossOriginLoading)};` : "", `if (${RuntimeGlobals.scriptNonce}) {`, Template.indent(`link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`), "}", 'link.rel = "prefetch";', 'link.as = "style";', `link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.require}.miniCssF(chunkId);`]), /** @type {Chunk} */chunk), "document.head.appendChild(link);"]), "}"])};` : "// no prefetching", "", withPreload && withLoading && hasCssMatcher !== false ? `${RuntimeGlobals.preloadChunkHandlers}.miniCss = ${runtimeTemplate.basicFunction("chunkId", [`if((!${RuntimeGlobals.hasOwnProperty}(installedCssChunks, chunkId) || installedCssChunks[chunkId] === undefined) && ${hasCssMatcher === true ? "true" : hasCssMatcher("chunkId")}) {`, Template.indent(["installedCssChunks[chunkId] = null;", linkPreload.call(Template.asString(["var link = document.createElement('link');", "link.charset = 'utf-8';", `if (${RuntimeGlobals.scriptNonce}) {`, Template.indent(`link.setAttribute("nonce", ${RuntimeGlobals.scriptNonce});`), "}", 'link.rel = "preload";', 'link.as = "style";', `link.href = ${RuntimeGlobals.publicPath} + ${RuntimeGlobals.require}.miniCssF(chunkId);`, crossOriginLoading ? crossOriginLoading === "use-credentials" ? 'link.crossOrigin = "use-credentials";' : Template.asString(["if (link.href.indexOf(window.location.origin + '/') !== 0) {", Template.indent(`link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`), "}"]) : ""]), /** @type {Chunk} */chunk), "document.head.appendChild(link);"]), "}"])};` : "// no preloaded"]);
|
|---|
| 775 | }
|
|---|
| 776 | }
|
|---|
| 777 | const enabledChunks = new WeakSet();
|
|---|
| 778 |
|
|---|
| 779 | /**
|
|---|
| 780 | * @param {Chunk} chunk chunk
|
|---|
| 781 | * @param {Set<string>} set set with runtime requirement
|
|---|
| 782 | */
|
|---|
| 783 | const handler = (chunk, set) => {
|
|---|
| 784 | if (enabledChunks.has(chunk)) {
|
|---|
| 785 | return;
|
|---|
| 786 | }
|
|---|
| 787 | enabledChunks.add(chunk);
|
|---|
| 788 | if (typeof normalizedChunkFilename === "string" && /\[(full)?hash(:\d+)?\]/.test(normalizedChunkFilename)) {
|
|---|
| 789 | set.add(RuntimeGlobals.getFullHash);
|
|---|
| 790 | }
|
|---|
| 791 | set.add(RuntimeGlobals.publicPath);
|
|---|
| 792 | compilation.addRuntimeModule(chunk, new runtime.GetChunkFilenameRuntimeModule(MODULE_TYPE, "mini-css", `${RuntimeGlobals.require}.miniCssF`,
|
|---|
| 793 | /**
|
|---|
| 794 | * @param {Chunk} referencedChunk a referenced chunk
|
|---|
| 795 | * @returns {ReturnType<import("webpack").runtime.GetChunkFilenameRuntimeModule["getFilenameForChunk"]>} a template value
|
|---|
| 796 | */
|
|---|
| 797 | referencedChunk => {
|
|---|
| 798 | if (!referencedChunk.contentHash[MODULE_TYPE]) {
|
|---|
| 799 | return false;
|
|---|
| 800 | }
|
|---|
| 801 | return referencedChunk.canBeInitial() ? (/** @type {Filename} */normalizedFilename) : (/** @type {ChunkFilename} */normalizedChunkFilename);
|
|---|
| 802 | }, set.has(RuntimeGlobals.hmrDownloadUpdateHandlers)));
|
|---|
| 803 | compilation.addRuntimeModule(chunk, new CssLoadingRuntimeModule(set, this.runtimeOptions));
|
|---|
| 804 | };
|
|---|
| 805 | compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.ensureChunkHandlers).tap(pluginName, handler);
|
|---|
| 806 | compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.hmrDownloadUpdateHandlers).tap(pluginName, handler);
|
|---|
| 807 | compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.prefetchChunkHandlers).tap(pluginName, handler);
|
|---|
| 808 | compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.preloadChunkHandlers).tap(pluginName, handler);
|
|---|
| 809 | });
|
|---|
| 810 | }
|
|---|
| 811 |
|
|---|
| 812 | /**
|
|---|
| 813 | * @private
|
|---|
| 814 | * @param {Chunk} chunk chunk
|
|---|
| 815 | * @param {ChunkGraph} chunkGraph chunk graph
|
|---|
| 816 | * @returns {Iterable<Module>} modules
|
|---|
| 817 | */
|
|---|
| 818 | getChunkModules(chunk, chunkGraph) {
|
|---|
| 819 | return typeof chunkGraph !== "undefined" ? chunkGraph.getOrderedChunkModulesIterable(chunk, compareModulesByIdentifier) : chunk.modulesIterable;
|
|---|
| 820 | }
|
|---|
| 821 |
|
|---|
| 822 | /**
|
|---|
| 823 | * @private
|
|---|
| 824 | * @param {Compilation} compilation compilation
|
|---|
| 825 | * @param {Chunk} chunk chunk
|
|---|
| 826 | * @param {CssModule[]} modules modules
|
|---|
| 827 | * @param {Compilation["requestShortener"]} requestShortener request shortener
|
|---|
| 828 | * @returns {Set<CssModule>} css modules
|
|---|
| 829 | */
|
|---|
| 830 | sortModules(compilation, chunk, modules, requestShortener) {
|
|---|
| 831 | let usedModules = this._sortedModulesCache.get(chunk);
|
|---|
| 832 | if (usedModules || !modules) {
|
|---|
| 833 | return /** @type {Set<CssModule>} */usedModules;
|
|---|
| 834 | }
|
|---|
| 835 |
|
|---|
| 836 | /** @type {CssModule[]} */
|
|---|
| 837 | const modulesList = [...modules];
|
|---|
| 838 | // Store dependencies for modules
|
|---|
| 839 | /** @type {Map<CssModule, Set<CssModule>>} */
|
|---|
| 840 | const moduleDependencies = new Map(modulesList.map(m => [m, (/** @type {Set<CssModule>} */
|
|---|
| 841 | new Set())]));
|
|---|
| 842 | /** @type {Map<CssModule, Map<CssModule, Set<ChunkGroup>>>} */
|
|---|
| 843 | const moduleDependenciesReasons = new Map(modulesList.map(m => [m, new Map()]));
|
|---|
| 844 | // Get ordered list of modules per chunk group
|
|---|
| 845 | // This loop also gathers dependencies from the ordered lists
|
|---|
| 846 | // Lists are in reverse order to allow to use Array.pop()
|
|---|
| 847 | /** @type {CssModule[][]} */
|
|---|
| 848 | const modulesByChunkGroup = Array.from(chunk.groupsIterable, chunkGroup => {
|
|---|
| 849 | const sortedModules = modulesList.map(module => ({
|
|---|
| 850 | module,
|
|---|
| 851 | index: chunkGroup.getModulePostOrderIndex(module)
|
|---|
| 852 | })).filter(item => item.index !== undefined).sort((a, b) => /** @type {number} */b.index - (/** @type {number} */a.index)).map(item => item.module);
|
|---|
| 853 | for (let i = 0; i < sortedModules.length; i++) {
|
|---|
| 854 | const set = moduleDependencies.get(sortedModules[i]);
|
|---|
| 855 | const reasons = /** @type {Map<CssModule, Set<ChunkGroup>>} */
|
|---|
| 856 | moduleDependenciesReasons.get(sortedModules[i]);
|
|---|
| 857 | for (let j = i + 1; j < sortedModules.length; j++) {
|
|---|
| 858 | const module = sortedModules[j];
|
|---|
| 859 |
|
|---|
| 860 | /** @type {Set<CssModule>} */
|
|---|
| 861 | set.add(module);
|
|---|
| 862 | const reason = reasons.get(module) || (/** @type {Set<ChunkGroup>} */new Set());
|
|---|
| 863 | reason.add(chunkGroup);
|
|---|
| 864 | reasons.set(module, reason);
|
|---|
| 865 | }
|
|---|
| 866 | }
|
|---|
| 867 | return sortedModules;
|
|---|
| 868 | });
|
|---|
| 869 |
|
|---|
| 870 | // set with already included modules in correct order
|
|---|
| 871 | usedModules = new Set();
|
|---|
| 872 |
|
|---|
| 873 | /**
|
|---|
| 874 | * @param {CssModule} m a css module
|
|---|
| 875 | * @returns {boolean} true when module unused, otherwise false
|
|---|
| 876 | */
|
|---|
| 877 | const unusedModulesFilter = m => !(/** @type {Set<CssModule>} */usedModules.has(m));
|
|---|
| 878 | while (usedModules.size < modulesList.length) {
|
|---|
| 879 | let success = false;
|
|---|
| 880 | let bestMatch;
|
|---|
| 881 | let bestMatchDeps;
|
|---|
| 882 |
|
|---|
| 883 | // get first module where dependencies are fulfilled
|
|---|
| 884 | for (const list of modulesByChunkGroup) {
|
|---|
| 885 | // skip and remove already added modules
|
|---|
| 886 | while (list.length > 0 && usedModules.has(list[list.length - 1])) {
|
|---|
| 887 | list.pop();
|
|---|
| 888 | }
|
|---|
| 889 |
|
|---|
| 890 | // skip empty lists
|
|---|
| 891 | if (list.length !== 0) {
|
|---|
| 892 | const module = list[list.length - 1];
|
|---|
| 893 | const deps = /** @type {Set<CssModule>} */
|
|---|
| 894 | moduleDependencies.get(module);
|
|---|
| 895 | // determine dependencies that are not yet included
|
|---|
| 896 | const failedDeps = [...deps].filter(unusedModulesFilter);
|
|---|
| 897 |
|
|---|
| 898 | // store best match for fallback behavior
|
|---|
| 899 | if (!bestMatchDeps || bestMatchDeps.length > failedDeps.length) {
|
|---|
| 900 | bestMatch = list;
|
|---|
| 901 | bestMatchDeps = failedDeps;
|
|---|
| 902 | }
|
|---|
| 903 | if (failedDeps.length === 0) {
|
|---|
| 904 | // use this module and remove it from list
|
|---|
| 905 | usedModules.add(/** @type {CssModule} */list.pop());
|
|---|
| 906 | success = true;
|
|---|
| 907 | break;
|
|---|
| 908 | }
|
|---|
| 909 | }
|
|---|
| 910 | }
|
|---|
| 911 | if (!success) {
|
|---|
| 912 | // no module found => there is a conflict
|
|---|
| 913 | // use list with fewest failed deps
|
|---|
| 914 | // and emit a warning
|
|---|
| 915 | const fallbackModule = /** @type {CssModule[]} */bestMatch.pop();
|
|---|
| 916 | if (!this.options.ignoreOrder) {
|
|---|
| 917 | const reasons = moduleDependenciesReasons.get(/** @type {CssModule} */fallbackModule);
|
|---|
| 918 | compilation.warnings.push(/** @type {WebpackError} */
|
|---|
| 919 |
|
|---|
| 920 | new Error([`chunk ${chunk.name || chunk.id} [${pluginName}]`, "Conflicting order. Following module has been added:", ` * ${ /** @type {CssModule} */fallbackModule.readableIdentifier(requestShortener)}`, "despite it was not able to fulfill desired ordering with these modules:", ... /** @type {CssModule[]} */bestMatchDeps.map(m => {
|
|---|
| 921 | const goodReasonsMap = moduleDependenciesReasons.get(m);
|
|---|
| 922 | const goodReasons = goodReasonsMap && goodReasonsMap.get(/** @type {CssModule} */fallbackModule);
|
|---|
| 923 | const failedChunkGroups = Array.from(/** @type {Set<ChunkGroup>} */
|
|---|
| 924 |
|
|---|
| 925 | /** @type {Map<CssModule, Set<ChunkGroup>>} */
|
|---|
| 926 | reasons.get(m), cg => cg.name).join(", ");
|
|---|
| 927 | const goodChunkGroups = goodReasons && Array.from(goodReasons, cg => cg.name).join(", ");
|
|---|
| 928 | return [` * ${m.readableIdentifier(requestShortener)}`, ` - couldn't fulfill desired order of chunk group(s) ${failedChunkGroups}`, goodChunkGroups && ` - while fulfilling desired order of chunk group(s) ${goodChunkGroups}`].filter(Boolean).join("\n");
|
|---|
| 929 | })].join("\n")));
|
|---|
| 930 | }
|
|---|
| 931 | usedModules.add(/** @type {CssModule} */fallbackModule);
|
|---|
| 932 | }
|
|---|
| 933 | }
|
|---|
| 934 | this._sortedModulesCache.set(chunk, usedModules);
|
|---|
| 935 | return usedModules;
|
|---|
| 936 | }
|
|---|
| 937 |
|
|---|
| 938 | /**
|
|---|
| 939 | * @private
|
|---|
| 940 | * @param {Compiler} compiler compiler
|
|---|
| 941 | * @param {Compilation} compilation compilation
|
|---|
| 942 | * @param {Chunk} chunk chunk
|
|---|
| 943 | * @param {CssModule[]} modules modules
|
|---|
| 944 | * @param {Compiler["requestShortener"]} requestShortener request shortener
|
|---|
| 945 | * @param {string} filenameTemplate filename template
|
|---|
| 946 | * @param {Parameters<Exclude<Required<Configuration>['output']['filename'], string | undefined>>[0]} pathData path data
|
|---|
| 947 | * @returns {Source} source
|
|---|
| 948 | */
|
|---|
| 949 | renderContentAsset(compiler, compilation, chunk, modules, requestShortener, filenameTemplate, pathData) {
|
|---|
| 950 | const usedModules = this.sortModules(compilation, chunk, modules, requestShortener);
|
|---|
| 951 | const {
|
|---|
| 952 | ConcatSource,
|
|---|
| 953 | SourceMapSource,
|
|---|
| 954 | RawSource
|
|---|
| 955 | } = compiler.webpack.sources;
|
|---|
| 956 | const source = new ConcatSource();
|
|---|
| 957 | const externalsSource = new ConcatSource();
|
|---|
| 958 | for (const module of usedModules) {
|
|---|
| 959 | let content = module.content.toString();
|
|---|
| 960 | const readableIdentifier = module.readableIdentifier(requestShortener);
|
|---|
| 961 | const startsWithAtRuleImport = content.startsWith("@import url");
|
|---|
| 962 | let header;
|
|---|
| 963 | if (compilation.outputOptions.pathinfo) {
|
|---|
| 964 | // From https://github.com/webpack/webpack/blob/29eff8a74ecc2f87517b627dee451c2af9ed3f3f/lib/ModuleInfoHeaderPlugin.js#L191-L194
|
|---|
| 965 | const reqStr = readableIdentifier.replace(/\*\//g, "*_/");
|
|---|
| 966 | const reqStrStar = "*".repeat(reqStr.length);
|
|---|
| 967 | const headerStr = `/*!****${reqStrStar}****!*\\\n !*** ${reqStr} ***!\n \\****${reqStrStar}****/\n`;
|
|---|
| 968 | header = new RawSource(headerStr);
|
|---|
| 969 | }
|
|---|
| 970 | if (startsWithAtRuleImport) {
|
|---|
| 971 | if (typeof header !== "undefined") {
|
|---|
| 972 | externalsSource.add(header);
|
|---|
| 973 | }
|
|---|
| 974 |
|
|---|
| 975 | // HACK for IE
|
|---|
| 976 | // http://stackoverflow.com/a/14676665/1458162
|
|---|
| 977 | if (module.media || module.supports || typeof module.layer === "string") {
|
|---|
| 978 | let atImportExtra = "";
|
|---|
| 979 | const needLayer = typeof module.layer === "string";
|
|---|
| 980 | if (needLayer) {
|
|---|
| 981 | atImportExtra += module.layer.length > 0 ? ` layer(${module.layer})` : " layer";
|
|---|
| 982 | }
|
|---|
| 983 | if (module.supports) {
|
|---|
| 984 | atImportExtra += ` supports(${module.supports})`;
|
|---|
| 985 | }
|
|---|
| 986 | if (module.media) {
|
|---|
| 987 | atImportExtra += ` ${module.media}`;
|
|---|
| 988 | }
|
|---|
| 989 |
|
|---|
| 990 | // insert media into the @import
|
|---|
| 991 | // this is rar
|
|---|
| 992 | // TODO improve this and parse the CSS to support multiple medias
|
|---|
| 993 | content = content.replace(/;|\s*$/, `${atImportExtra};`);
|
|---|
| 994 | }
|
|---|
| 995 | externalsSource.add(content);
|
|---|
| 996 | externalsSource.add("\n");
|
|---|
| 997 | } else {
|
|---|
| 998 | if (typeof header !== "undefined") {
|
|---|
| 999 | source.add(header);
|
|---|
| 1000 | }
|
|---|
| 1001 | if (module.supports) {
|
|---|
| 1002 | source.add(`@supports (${module.supports}) {\n`);
|
|---|
| 1003 | }
|
|---|
| 1004 | if (module.media) {
|
|---|
| 1005 | source.add(`@media ${module.media} {\n`);
|
|---|
| 1006 | }
|
|---|
| 1007 | const needLayer = typeof module.layer === "string";
|
|---|
| 1008 | if (needLayer) {
|
|---|
| 1009 | source.add(`@layer${module.layer.length > 0 ? ` ${module.layer}` : ""} {\n`);
|
|---|
| 1010 | }
|
|---|
| 1011 | const {
|
|---|
| 1012 | path: filename
|
|---|
| 1013 | } = compilation.getPathWithInfo(filenameTemplate, pathData);
|
|---|
| 1014 | const undoPath = getUndoPath(filename, compiler.outputPath, false);
|
|---|
| 1015 |
|
|---|
| 1016 | // replacements
|
|---|
| 1017 | content = content.replace(new RegExp(ABSOLUTE_PUBLIC_PATH, "g"), "");
|
|---|
| 1018 | content = content.replace(new RegExp(SINGLE_DOT_PATH_SEGMENT, "g"), ".");
|
|---|
| 1019 | content = content.replace(new RegExp(AUTO_PUBLIC_PATH, "g"), undoPath);
|
|---|
| 1020 | const entryOptions = chunk.getEntryOptions();
|
|---|
| 1021 | const baseUriReplacement = entryOptions && entryOptions.baseUri || undoPath;
|
|---|
| 1022 | content = content.replace(new RegExp(BASE_URI, "g"), baseUriReplacement);
|
|---|
| 1023 | if (module.sourceMap) {
|
|---|
| 1024 | source.add(new SourceMapSource(content, readableIdentifier, module.sourceMap.toString()));
|
|---|
| 1025 | } else {
|
|---|
| 1026 | source.add(new RawSource(content));
|
|---|
| 1027 | }
|
|---|
| 1028 | source.add("\n");
|
|---|
| 1029 | if (needLayer) {
|
|---|
| 1030 | source.add("}\n");
|
|---|
| 1031 | }
|
|---|
| 1032 | if (module.media) {
|
|---|
| 1033 | source.add("}\n");
|
|---|
| 1034 | }
|
|---|
| 1035 | if (module.supports) {
|
|---|
| 1036 | source.add("}\n");
|
|---|
| 1037 | }
|
|---|
| 1038 | }
|
|---|
| 1039 | }
|
|---|
| 1040 | return new ConcatSource(externalsSource, source);
|
|---|
| 1041 | }
|
|---|
| 1042 | }
|
|---|
| 1043 | MiniCssExtractPlugin.pluginName = pluginName;
|
|---|
| 1044 | MiniCssExtractPlugin.pluginSymbol = pluginSymbol;
|
|---|
| 1045 | MiniCssExtractPlugin.loader = require.resolve("./loader");
|
|---|
| 1046 | module.exports = MiniCssExtractPlugin; |
|---|