Skip to content

Commit e7aac21

Browse files
committed
feat: add beforeTagInsert hook
1 parent 8480bce commit e7aac21

File tree

20 files changed

+206
-7
lines changed

20 files changed

+206
-7
lines changed

.cspell.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"vspace",
3131
"commitlint",
3232
"unreload",
33-
"cnfg"
33+
"cnfg",
34+
"tapable"
3435
],
3536

3637
"ignorePaths": [

README.md

+21
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,27 @@ If you'd like to extract the media queries from the extracted CSS (so mobile use
11941194
- [Media Query Plugin](https://github.com/SassNinja/media-query-plugin)
11951195
- [Media Query Splitting Plugin](https://github.com/mike-diamond/media-query-splitting-plugin)
11961196

1197+
## Hooks
1198+
1199+
The mini-css-extract-plugin provides hooks to extend it to your needs.
1200+
1201+
### beforeTagInsert
1202+
1203+
`SyncWaterfallHook`
1204+
1205+
Called before inject the insert code for link tag. Should return a string
1206+
1207+
```javascript
1208+
MiniCssExtractPlugin.getHooks(compiler).beforeTagInsert.tap(
1209+
"changeHref",
1210+
(source, varNames) =>
1211+
compiler.webpack.Template.asString([
1212+
source,
1213+
`${varNames.tag}.setAttribute("href", "https://github.com/webpack-contrib/mini-css-extract-plugin");`,
1214+
])
1215+
);
1216+
```
1217+
11971218
## Contributing
11981219

11991220
Please take a moment to read our contributing guidelines if you haven't yet done so.

package-lock.json

+1-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@
5252
"webpack": "^5.0.0"
5353
},
5454
"dependencies": {
55-
"schema-utils": "^4.0.0"
55+
"schema-utils": "^4.0.0",
56+
"tapable": "^2.2.1"
5657
},
5758
"devDependencies": {
5859
"@babel/cli": "^7.21.0",

src/hooks.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/** @typedef {import("webpack").Compiler} WebpackCompiler */
2+
/**
3+
* @typedef {Object} VarNames
4+
* @property {string} tag
5+
* @property {string} chunkId
6+
* @property {string} href
7+
* @property {string} resolve
8+
* @property {string} reject
9+
*/
10+
/**
11+
* @typedef {Object} MiniCssExtractHooks
12+
* @property {import("tapable").SyncWaterfallHook<[string, VarNames]>} beforeTagInsert
13+
*/
14+
15+
const { SyncWaterfallHook } = require("tapable");
16+
17+
/**
18+
* @type {WeakMap<WebpackCompiler, MiniCssExtractHooks>}}
19+
*/
20+
const miniCssExtractHooksMap = new WeakMap();
21+
22+
/**
23+
* @param {WebpackCompiler} complier
24+
*/
25+
function getHooks(complier) {
26+
let hooks = miniCssExtractHooksMap.get(complier);
27+
// Setup the hooks only once
28+
if (!hooks) {
29+
hooks = createHooks();
30+
miniCssExtractHooksMap.set(complier, hooks);
31+
}
32+
return hooks;
33+
}
34+
35+
/**
36+
* @returns {MiniCssExtractHooks}
37+
*/
38+
function createHooks() {
39+
return {
40+
beforeTagInsert: new SyncWaterfallHook(["source", "varNames"]),
41+
};
42+
}
43+
44+
module.exports = {
45+
getHooks,
46+
};

src/index.js

+19-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const {
1515
getUndoPath,
1616
BASE_URI,
1717
} = require("./utils");
18+
const { getHooks } = require("./hooks");
1819

1920
/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
2021
/** @typedef {import("webpack").Compiler} Compiler */
@@ -513,6 +514,14 @@ class MiniCssExtractPlugin {
513514
return CssDependency;
514515
}
515516

517+
/**
518+
* Returns all hooks for the given compiler
519+
* @param {Compiler} compiler
520+
*/
521+
static getHooks(compiler) {
522+
return getHooks(compiler);
523+
}
524+
516525
/**
517526
* @param {PluginOptions} [options]
518527
*/
@@ -843,7 +852,6 @@ class MiniCssExtractPlugin {
843852
if (!withLoading && !withHmr) {
844853
return "";
845854
}
846-
847855
return Template.asString([
848856
'if (typeof document === "undefined") return;',
849857
`var createStylesheet = ${runtimeTemplate.basicFunction(
@@ -902,6 +910,16 @@ class MiniCssExtractPlugin {
902910
"}",
903911
])
904912
: "",
913+
MiniCssExtractPlugin.getHooks(compiler).beforeTagInsert.call(
914+
"",
915+
{
916+
tag: "linkTag",
917+
chunkId: "chunkId",
918+
href: "fullhref",
919+
resolve: "resolve",
920+
reject: "reject",
921+
}
922+
) || "",
905923
typeof this.runtimeOptions.insert !== "undefined"
906924
? typeof this.runtimeOptions.insert === "function"
907925
? `(${this.runtimeOptions.insert.toString()})(linkTag)`

test/api.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import path from "path";
2+
13
import webpack from "webpack";
24

35
import MiniCssExtractPlugin from "../src";
46

7+
import { getCompiler } from "./helpers/index";
8+
59
describe("API", () => {
610
it("should return the same CssModule when same webpack instance provided", () => {
711
expect(MiniCssExtractPlugin.getCssModule(webpack)).toEqual(
@@ -14,4 +18,27 @@ describe("API", () => {
1418
MiniCssExtractPlugin.getCssDependency(webpack)
1519
);
1620
});
21+
22+
it("should return the same Hooks when same webpack instance provided", () => {
23+
const compiler = getCompiler(
24+
"insert.js",
25+
{},
26+
{
27+
mode: "none",
28+
output: {
29+
publicPath: "",
30+
path: path.resolve(__dirname, "../outputs"),
31+
filename: "[name].bundle.js",
32+
},
33+
plugins: [
34+
new MiniCssExtractPlugin({
35+
filename: "[name].css",
36+
}),
37+
],
38+
}
39+
);
40+
expect(MiniCssExtractPlugin.getHooks(compiler)).toEqual(
41+
MiniCssExtractPlugin.getHooks(compiler)
42+
);
43+
});
1744
});

test/cases/chunkFilename-fullhash/expected/webpack-5-importModule/main.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ __webpack_require__.r(__webpack_exports__);
7373
/******/
7474
/******/ /* webpack/runtime/getFullHash */
7575
/******/ (() => {
76-
/******/ __webpack_require__.h = () => ("7f0e5fa686a9bb728e64")
76+
/******/ __webpack_require__.h = () => ("04f5273a6b9819ed9e63")
7777
/******/ })();
7878
/******/
7979
/******/ /* webpack/runtime/global */
@@ -201,6 +201,7 @@ __webpack_require__.r(__webpack_exports__);
201201
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
202202
/******/ linkTag.href = fullhref;
203203
/******/
204+
/******/
204205
/******/ if (oldTag) {
205206
/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);
206207
/******/ } else {

test/cases/chunkFilename-fullhash/expected/webpack-5/main.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ __webpack_require__.r(__webpack_exports__);
7373
/******/
7474
/******/ /* webpack/runtime/getFullHash */
7575
/******/ (() => {
76-
/******/ __webpack_require__.h = () => ("100253bb7576627988e6")
76+
/******/ __webpack_require__.h = () => ("04f5273a6b9819ed9e63")
7777
/******/ })();
7878
/******/
7979
/******/ /* webpack/runtime/global */
@@ -201,6 +201,7 @@ __webpack_require__.r(__webpack_exports__);
201201
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
202202
/******/ linkTag.href = fullhref;
203203
/******/
204+
/******/
204205
/******/ if (oldTag) {
205206
/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);
206207
/******/ } else {

test/cases/hmr/expected/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -964,6 +964,7 @@ __webpack_require__.r(__webpack_exports__);
964964
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
965965
/******/ linkTag.href = fullhref;
966966
/******/
967+
/******/
967968
/******/ if (oldTag) {
968969
/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);
969970
/******/ } else {

test/cases/insert-function/expected/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@
185185
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
186186
/******/ linkTag.href = fullhref;
187187
/******/
188+
/******/
188189
/******/ (function (linkTag) {
189190
/******/ const reference = document.querySelector(".hot-reload");
190191
/******/ if (reference) {

test/cases/insert-string/expected/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@
185185
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
186186
/******/ linkTag.href = fullhref;
187187
/******/
188+
/******/
188189
/******/ var target = document.querySelector("script[src='1.js']");
189190
/******/ target.parentNode.insertBefore(linkTag, target.nextSibling);
190191
/******/ return linkTag;

test/cases/insert-undefined/expected/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@
185185
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
186186
/******/ linkTag.href = fullhref;
187187
/******/
188+
/******/
188189
/******/ if (oldTag) {
189190
/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);
190191
/******/ } else {

test/hooks.test.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* eslint-env browser */
2+
import path from "path";
3+
4+
import MiniCssExtractPlugin from "../src";
5+
6+
import { runInJsDom, compile, getCompiler } from "./helpers/index";
7+
8+
describe("hooks", () => {
9+
it(`beforeTagInsert`, async () => {
10+
const webpackCompiler = getCompiler(
11+
"insert.js",
12+
{},
13+
{
14+
mode: "none",
15+
output: {
16+
publicPath: "",
17+
path: path.resolve(__dirname, "../outputs"),
18+
filename: "[name].bundle.js",
19+
},
20+
plugins: [
21+
new MiniCssExtractPlugin({
22+
filename: "[name].css",
23+
}),
24+
{
25+
apply: (compiler) => {
26+
MiniCssExtractPlugin.getHooks(compiler).beforeTagInsert.tap(
27+
"sri",
28+
(source, varNames) =>
29+
compiler.webpack.Template.asString([
30+
source,
31+
`${varNames.tag}.setAttribute("integrity", "sriHashes[${varNames.chunkId}]");`,
32+
])
33+
);
34+
},
35+
},
36+
{
37+
apply: (compiler) => {
38+
MiniCssExtractPlugin.getHooks(compiler).beforeTagInsert.tap(
39+
"changeHref",
40+
(source, varNames) =>
41+
compiler.webpack.Template.asString([
42+
source,
43+
`${varNames.tag}.setAttribute("href", "https://github.com/webpack-contrib/mini-css-extract-plugin");`,
44+
])
45+
);
46+
},
47+
},
48+
],
49+
}
50+
);
51+
const stats = await compile(webpackCompiler);
52+
runInJsDom("main.bundle.js", webpackCompiler, stats, (dom) => {
53+
const [tag] = dom.window.document.head.getElementsByTagName("link");
54+
expect(tag.getAttribute("integrity")).toBe("sriHashes[chunkId]");
55+
expect(tag.getAttribute("href")).toBe(
56+
"https://github.com/webpack-contrib/mini-css-extract-plugin"
57+
);
58+
});
59+
});
60+
});

types/hooks.d.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export type WebpackCompiler = import("webpack").Compiler;
2+
export type VarNames = {
3+
tag: string;
4+
chunkId: string;
5+
href: string;
6+
resolve: string;
7+
reject: string;
8+
};
9+
export type MiniCssExtractHooks = {
10+
beforeTagInsert: import("tapable").SyncWaterfallHook<[string, VarNames]>;
11+
};
12+
/**
13+
* @param {WebpackCompiler} complier
14+
*/
15+
export function getHooks(complier: WebpackCompiler): MiniCssExtractHooks;

types/index.d.ts

+5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ declare class MiniCssExtractPlugin {
1212
static getCssDependency(
1313
webpack: Compiler["webpack"]
1414
): CssDependencyConstructor;
15+
/**
16+
* Returns all hooks for the given compiler
17+
* @param {Compiler} compiler
18+
*/
19+
static getHooks(compiler: Compiler): import("./hooks").MiniCssExtractHooks;
1520
/**
1621
* @param {PluginOptions} [options]
1722
*/

0 commit comments

Comments
 (0)