diff --git a/package-lock.json b/package-lock.json
index 3b902153..c0d92d9b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,11 +5,12 @@
"requires": true,
"packages": {
"": {
- "version": "1.3.9",
+ "version": "1.4.1",
"license": "MIT",
"dependencies": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0",
+ "tapable": "^2.2.0",
"webpack-sources": "^1.1.0"
},
"devDependencies": {
@@ -17000,7 +17001,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
"integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==",
- "dev": true,
"engines": {
"node": ">=6"
}
@@ -32115,8 +32115,7 @@
"tapable": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz",
- "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==",
- "dev": true
+ "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw=="
},
"terminal-link": {
"version": "2.1.1",
diff --git a/package.json b/package.json
index 26abd63e..e295e875 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"dependencies": {
"loader-utils": "^2.0.0",
"schema-utils": "^3.0.0",
+ "tapable": "^2.2.0",
"webpack-sources": "^1.1.0"
},
"devDependencies": {
diff --git a/src/index.js b/src/index.js
index c5483c46..b197c71d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,6 +1,7 @@
/* eslint-disable class-methods-use-this */
import { validate } from 'schema-utils';
+import { SyncWaterfallHook } from 'tapable';
import schema from './plugin-options.json';
import { MODULE_TYPE, compareModulesByIdentifier } from './utils';
@@ -28,6 +29,8 @@ const cssModuleCache = new WeakMap();
*/
const cssDependencyCache = new WeakMap();
+const compilerHookMap = new WeakMap();
+
class MiniCssExtractPlugin {
static getCssModule(webpack) {
/**
@@ -300,6 +303,20 @@ class MiniCssExtractPlugin {
return CssDependency;
}
+ static getCompilerHooks(compiler) {
+ /**
+ * Prevent creation of multiple compiler hook maps to allow other integrations to get the current mapping.
+ */
+ let hooks = compilerHookMap.get(compiler);
+ if (!hooks) {
+ hooks = {
+ customize: new SyncWaterfallHook(['attributes']),
+ };
+ compilerHookMap.set(compiler, hooks);
+ }
+ return hooks;
+ }
+
constructor(options = {}) {
validate(schema, options, {
name: 'Mini CSS Extract Plugin',
@@ -847,30 +864,46 @@ class MiniCssExtractPlugin {
return null;
}
+ const attributes = {
+ href: `${RuntimeGlobals.publicPath} + ${RuntimeGlobals.require}.miniCssF(chunkId)`,
+ rel: JSON.stringify('stylesheet'),
+ onload: 'onLinkComplete',
+ onerror: 'onLinkComplete',
+ };
+
+ // Some attributes cannot be assigned through setAttribute, so we maintain
+ // a list of attributes that can safely be assigned through dot notation
+ const safeAttrs = ['href', 'rel', 'type', 'onload', 'onerror'];
+
+ if (this.runtimeOptions.linkType) {
+ attributes.type = JSON.stringify(this.runtimeOptions.linkType);
+ }
+
+ if (crossOriginLoading) {
+ attributes.crossOrigin = `(linkTag.href.indexOf(window.location.origin + '/') !== 0)
+ ? ${JSON.stringify(crossOriginLoading)}
+ : undefined`;
+ }
+
+ // Append static attributes
+ if (this.runtimeOptions.attributes) {
+ Object.entries(this.runtimeOptions.attributes).forEach(
+ ([key, value]) => {
+ attributes[key] = JSON.stringify(value);
+ }
+ );
+ }
+
+ // Append dynamic attributes
+ MiniCssExtractPlugin.getCompilerHooks(compiler).customize.call(
+ attributes
+ );
+
return Template.asString([
`var createStylesheet = ${runtimeTemplate.basicFunction(
'chunkId, fullhref, resolve, reject',
[
'var linkTag = document.createElement("link");',
- this.runtimeOptions.attributes
- ? Template.asString(
- Object.entries(this.runtimeOptions.attributes).map(
- (entry) => {
- const [key, value] = entry;
-
- return `linkTag.setAttribute(${JSON.stringify(
- key
- )}, ${JSON.stringify(value)});`;
- }
- )
- )
- : '',
- 'linkTag.rel = "stylesheet";',
- this.runtimeOptions.linkType
- ? `linkTag.type = ${JSON.stringify(
- this.runtimeOptions.linkType
- )};`
- : '',
`var onLinkComplete = ${runtimeTemplate.basicFunction(
'event',
[
@@ -892,19 +925,17 @@ class MiniCssExtractPlugin {
'}',
]
)}`,
- '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
- )};`
- ),
- '}',
- ])
- : '',
+ Template.asString(
+ Object.entries(attributes).map(([key, value]) => {
+ if (safeAttrs.includes(key)) {
+ return `linkTag.${key} = ${value};`;
+ }
+
+ return `linkTag.setAttribute(${JSON.stringify(
+ key
+ )}, ${value});`;
+ })
+ ),
typeof this.runtimeOptions.insert !== 'undefined'
? typeof this.runtimeOptions.insert === 'function'
? `(${this.runtimeOptions.insert.toString()})(linkTag)`
@@ -945,7 +976,7 @@ class MiniCssExtractPlugin {
'resolve, reject',
[
`var href = ${RuntimeGlobals.require}.miniCssF(chunkId);`,
- `var fullhref = ${RuntimeGlobals.publicPath} + href;`,
+ `var fullhref = ${attributes.href};`,
'if(findStylesheet(href, fullhref)) return resolve();',
'createStylesheet(chunkId, fullhref, resolve, reject);',
]
@@ -1016,7 +1047,7 @@ class MiniCssExtractPlugin {
'chunkId',
[
`var href = ${RuntimeGlobals.require}.miniCssF(chunkId);`,
- `var fullhref = ${RuntimeGlobals.publicPath} + href;`,
+ `var fullhref = ${attributes.href};`,
'var oldTag = findStylesheet(href, fullhref);',
'if(!oldTag) return;',
`promises.push(new Promise(${runtimeTemplate.basicFunction(
diff --git a/test/__snapshots__/attributes-option.test.js.snap.webpack5 b/test/__snapshots__/attributes-option.test.js.snap.webpack5
index 84007ca0..95d7c9ff 100644
--- a/test/__snapshots__/attributes-option.test.js.snap.webpack5
+++ b/test/__snapshots__/attributes-option.test.js.snap.webpack5
@@ -4,7 +4,7 @@ exports[`attributes option should work with attributes option: DOM 1`] = `
"
style-loader test
-
+
Body
@@ -22,7 +22,7 @@ exports[`attributes option should work without attributes option: DOM 1`] = `
"
style-loader test
-
+
Body
diff --git a/test/__snapshots__/insert-option.test.js.snap.webpack5 b/test/__snapshots__/insert-option.test.js.snap.webpack5
index 1e8530eb..f7e839fd 100644
--- a/test/__snapshots__/insert-option.test.js.snap.webpack5
+++ b/test/__snapshots__/insert-option.test.js.snap.webpack5
@@ -3,7 +3,7 @@
exports[`insert option should work when insert option is function: DOM 1`] = `
"
style-loader test
-
+
Body
@@ -21,7 +21,7 @@ exports[`insert option should work when insert option is function: warnings 1`]
exports[`insert option should work when insert option is string: DOM 1`] = `
"
style-loader test
-
+
Body
@@ -40,7 +40,7 @@ exports[`insert option should work without insert option: DOM 1`] = `
"
style-loader test
-
+
Body
diff --git a/test/__snapshots__/linkTag-option.test.js.snap.webpack5 b/test/__snapshots__/linkTag-option.test.js.snap.webpack5
index 8a4e62d9..1782a725 100644
--- a/test/__snapshots__/linkTag-option.test.js.snap.webpack5
+++ b/test/__snapshots__/linkTag-option.test.js.snap.webpack5
@@ -4,7 +4,7 @@ exports[`linkType option should work when linkType option is "false": DOM 1`] =
"
style-loader test
-
+
Body
@@ -22,7 +22,7 @@ exports[`linkType option should work when linkType option is "text/css": DOM 1`]
"
style-loader test
-
+
Body
@@ -40,7 +40,7 @@ exports[`linkType option should work without linkType option: DOM 1`] = `
"
style-loader test
-
+
Body
diff --git a/test/cases/chunkFilename-fullhash/expected/webpack-5/0.07119853b0d8e8fbe3ca.css b/test/cases/chunkFilename-fullhash/expected/webpack-5/0.5f50282222b6b9c8631c.css
similarity index 100%
rename from test/cases/chunkFilename-fullhash/expected/webpack-5/0.07119853b0d8e8fbe3ca.css
rename to test/cases/chunkFilename-fullhash/expected/webpack-5/0.5f50282222b6b9c8631c.css
diff --git a/test/cases/chunkFilename-fullhash/expected/webpack-5/07119853b0d8e8fbe3ca.css b/test/cases/chunkFilename-fullhash/expected/webpack-5/5f50282222b6b9c8631c.css
similarity index 100%
rename from test/cases/chunkFilename-fullhash/expected/webpack-5/07119853b0d8e8fbe3ca.css
rename to test/cases/chunkFilename-fullhash/expected/webpack-5/5f50282222b6b9c8631c.css
diff --git a/test/cases/chunkFilename-fullhash/expected/webpack-5/main.js b/test/cases/chunkFilename-fullhash/expected/webpack-5/main.js
index 6ec953d4..b9dec09e 100644
--- a/test/cases/chunkFilename-fullhash/expected/webpack-5/main.js
+++ b/test/cases/chunkFilename-fullhash/expected/webpack-5/main.js
@@ -73,7 +73,7 @@ __webpack_require__.r(__webpack_exports__);
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
-/******/ __webpack_require__.h = () => ("07119853b0d8e8fbe3ca")
+/******/ __webpack_require__.h = () => ("5f50282222b6b9c8631c")
/******/ })();
/******/
/******/ /* webpack/runtime/global */
@@ -174,9 +174,6 @@ __webpack_require__.r(__webpack_exports__);
/******/ (() => {
/******/ var createStylesheet = (chunkId, fullhref, resolve, reject) => {
/******/ var linkTag = document.createElement("link");
-/******/
-/******/ linkTag.rel = "stylesheet";
-/******/ linkTag.type = "text/css";
/******/ var onLinkComplete = (event) => {
/******/ // avoid mem leaks.
/******/ linkTag.onerror = linkTag.onload = null;
@@ -193,9 +190,11 @@ __webpack_require__.r(__webpack_exports__);
/******/ reject(err);
/******/ }
/******/ }
-/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
-/******/ linkTag.href = fullhref;
-/******/
+/******/ linkTag.href = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
+/******/ linkTag.rel = "stylesheet";
+/******/ linkTag.onload = onLinkComplete;
+/******/ linkTag.onerror = onLinkComplete;
+/******/ linkTag.type = "text/css";
/******/ document.head.appendChild(linkTag);
/******/ return linkTag;
/******/ };
@@ -216,7 +215,7 @@ __webpack_require__.r(__webpack_exports__);
/******/ var loadStylesheet = (chunkId) => {
/******/ return new Promise((resolve, reject) => {
/******/ var href = __webpack_require__.miniCssF(chunkId);
-/******/ var fullhref = __webpack_require__.p + href;
+/******/ var fullhref = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
/******/ if(findStylesheet(href, fullhref)) return resolve();
/******/ createStylesheet(chunkId, fullhref, resolve, reject);
/******/ });
diff --git a/test/cases/hmr/expected/webpack-5/main.js b/test/cases/hmr/expected/webpack-5/main.js
index ae52744f..cd2ce2ae 100644
--- a/test/cases/hmr/expected/webpack-5/main.js
+++ b/test/cases/hmr/expected/webpack-5/main.js
@@ -846,9 +846,6 @@ module.exports = function (urlString) {
/******/ (() => {
/******/ var createStylesheet = (chunkId, fullhref, resolve, reject) => {
/******/ var linkTag = document.createElement("link");
-/******/
-/******/ linkTag.rel = "stylesheet";
-/******/ linkTag.type = "text/css";
/******/ var onLinkComplete = (event) => {
/******/ // avoid mem leaks.
/******/ linkTag.onerror = linkTag.onload = null;
@@ -865,9 +862,11 @@ module.exports = function (urlString) {
/******/ reject(err);
/******/ }
/******/ }
-/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
-/******/ linkTag.href = fullhref;
-/******/
+/******/ linkTag.href = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
+/******/ linkTag.rel = "stylesheet";
+/******/ linkTag.onload = onLinkComplete;
+/******/ linkTag.onerror = onLinkComplete;
+/******/ linkTag.type = "text/css";
/******/ document.head.appendChild(linkTag);
/******/ return linkTag;
/******/ };
@@ -888,7 +887,7 @@ module.exports = function (urlString) {
/******/ var loadStylesheet = (chunkId) => {
/******/ return new Promise((resolve, reject) => {
/******/ var href = __webpack_require__.miniCssF(chunkId);
-/******/ var fullhref = __webpack_require__.p + href;
+/******/ var fullhref = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
/******/ if(findStylesheet(href, fullhref)) return resolve();
/******/ createStylesheet(chunkId, fullhref, resolve, reject);
/******/ });
@@ -913,7 +912,7 @@ module.exports = function (urlString) {
/******/ applyHandlers.push(applyHandler);
/******/ chunkIds.forEach((chunkId) => {
/******/ var href = __webpack_require__.miniCssF(chunkId);
-/******/ var fullhref = __webpack_require__.p + href;
+/******/ var fullhref = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
/******/ var oldTag = findStylesheet(href, fullhref);
/******/ if(!oldTag) return;
/******/ promises.push(new Promise((resolve, reject) => {
diff --git a/test/cases/insert-function/expected/webpack-5/main.js b/test/cases/insert-function/expected/webpack-5/main.js
index 0dcaf369..6c7e634a 100644
--- a/test/cases/insert-function/expected/webpack-5/main.js
+++ b/test/cases/insert-function/expected/webpack-5/main.js
@@ -158,9 +158,6 @@
/******/ (() => {
/******/ var createStylesheet = (chunkId, fullhref, resolve, reject) => {
/******/ var linkTag = document.createElement("link");
-/******/
-/******/ linkTag.rel = "stylesheet";
-/******/ linkTag.type = "text/css";
/******/ var onLinkComplete = (event) => {
/******/ // avoid mem leaks.
/******/ linkTag.onerror = linkTag.onload = null;
@@ -177,9 +174,11 @@
/******/ reject(err);
/******/ }
/******/ }
-/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
-/******/ linkTag.href = fullhref;
-/******/
+/******/ linkTag.href = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
+/******/ linkTag.rel = "stylesheet";
+/******/ linkTag.onload = onLinkComplete;
+/******/ linkTag.onerror = onLinkComplete;
+/******/ linkTag.type = "text/css";
/******/ (function (linkTag) {
/******/ const reference = document.querySelector('.hot-reload');
/******/
@@ -206,7 +205,7 @@
/******/ var loadStylesheet = (chunkId) => {
/******/ return new Promise((resolve, reject) => {
/******/ var href = __webpack_require__.miniCssF(chunkId);
-/******/ var fullhref = __webpack_require__.p + href;
+/******/ var fullhref = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
/******/ if(findStylesheet(href, fullhref)) return resolve();
/******/ createStylesheet(chunkId, fullhref, resolve, reject);
/******/ });
diff --git a/test/cases/insert-string/expected/webpack-5/main.js b/test/cases/insert-string/expected/webpack-5/main.js
index 4d61b5dc..5a72955a 100644
--- a/test/cases/insert-string/expected/webpack-5/main.js
+++ b/test/cases/insert-string/expected/webpack-5/main.js
@@ -158,9 +158,6 @@
/******/ (() => {
/******/ var createStylesheet = (chunkId, fullhref, resolve, reject) => {
/******/ var linkTag = document.createElement("link");
-/******/
-/******/ linkTag.rel = "stylesheet";
-/******/ linkTag.type = "text/css";
/******/ var onLinkComplete = (event) => {
/******/ // avoid mem leaks.
/******/ linkTag.onerror = linkTag.onload = null;
@@ -177,9 +174,11 @@
/******/ reject(err);
/******/ }
/******/ }
-/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
-/******/ linkTag.href = fullhref;
-/******/
+/******/ linkTag.href = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
+/******/ linkTag.rel = "stylesheet";
+/******/ linkTag.onload = onLinkComplete;
+/******/ linkTag.onerror = onLinkComplete;
+/******/ linkTag.type = "text/css";
/******/ var target = document.querySelector("script[src='1.js']");
/******/ target.parentNode.insertBefore(linkTag, target.nextSibling);
/******/ return linkTag;
@@ -201,7 +200,7 @@
/******/ var loadStylesheet = (chunkId) => {
/******/ return new Promise((resolve, reject) => {
/******/ var href = __webpack_require__.miniCssF(chunkId);
-/******/ var fullhref = __webpack_require__.p + href;
+/******/ var fullhref = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
/******/ if(findStylesheet(href, fullhref)) return resolve();
/******/ createStylesheet(chunkId, fullhref, resolve, reject);
/******/ });
diff --git a/test/cases/insert-undefined/expected/webpack-5/main.js b/test/cases/insert-undefined/expected/webpack-5/main.js
index b998720c..2c6ef386 100644
--- a/test/cases/insert-undefined/expected/webpack-5/main.js
+++ b/test/cases/insert-undefined/expected/webpack-5/main.js
@@ -158,9 +158,6 @@
/******/ (() => {
/******/ var createStylesheet = (chunkId, fullhref, resolve, reject) => {
/******/ var linkTag = document.createElement("link");
-/******/
-/******/ linkTag.rel = "stylesheet";
-/******/ linkTag.type = "text/css";
/******/ var onLinkComplete = (event) => {
/******/ // avoid mem leaks.
/******/ linkTag.onerror = linkTag.onload = null;
@@ -177,9 +174,11 @@
/******/ reject(err);
/******/ }
/******/ }
-/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
-/******/ linkTag.href = fullhref;
-/******/
+/******/ linkTag.href = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
+/******/ linkTag.rel = "stylesheet";
+/******/ linkTag.onload = onLinkComplete;
+/******/ linkTag.onerror = onLinkComplete;
+/******/ linkTag.type = "text/css";
/******/ document.head.appendChild(linkTag);
/******/ return linkTag;
/******/ };
@@ -200,7 +199,7 @@
/******/ var loadStylesheet = (chunkId) => {
/******/ return new Promise((resolve, reject) => {
/******/ var href = __webpack_require__.miniCssF(chunkId);
-/******/ var fullhref = __webpack_require__.p + href;
+/******/ var fullhref = __webpack_require__.p + __webpack_require__.miniCssF(chunkId);
/******/ if(findStylesheet(href, fullhref)) return resolve();
/******/ createStylesheet(chunkId, fullhref, resolve, reject);
/******/ });