Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add beforeTagInsert hook #1054

Merged
merged 1 commit into from Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .cspell.json
Expand Up @@ -30,7 +30,8 @@
"vspace",
"commitlint",
"unreload",
"cnfg"
"cnfg",
"tapable"
],

"ignorePaths": [
Expand Down
21 changes: 21 additions & 0 deletions README.md
Expand Up @@ -1194,6 +1194,27 @@ If you'd like to extract the media queries from the extracted CSS (so mobile use
- [Media Query Plugin](https://github.com/SassNinja/media-query-plugin)
- [Media Query Splitting Plugin](https://github.com/mike-diamond/media-query-splitting-plugin)

## Hooks

The mini-css-extract-plugin provides hooks to extend it to your needs.

### beforeTagInsert

`SyncWaterfallHook`

Called before inject the insert code for link tag. Should return a string

```javascript
MiniCssExtractPlugin.getCompilationHooks(compilation).beforeTagInsert.tap(
"changeHref",
(source, varNames) =>
Template.asString([
source,
`${varNames.tag}.setAttribute("href", "https://github.com/webpack-contrib/mini-css-extract-plugin");`,
])
);
```

## Contributing

Please take a moment to read our contributing guidelines if you haven't yet done so.
Expand Down
3 changes: 1 addition & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -52,7 +52,8 @@
"webpack": "^5.0.0"
},
"dependencies": {
"schema-utils": "^4.0.0"
"schema-utils": "^4.0.0",
"tapable": "^2.2.1"
},
"devDependencies": {
"@babel/cli": "^7.21.0",
Expand Down
35 changes: 35 additions & 0 deletions src/hooks.js
@@ -0,0 +1,35 @@
const { SyncWaterfallHook } = require("tapable");

/** @typedef {import("webpack").Compilation} Compilation */
/**
* @typedef {Object} VarNames
* @property {string} tag
* @property {string} chunkId
* @property {string} href
* @property {string} resolve
* @property {string} reject
*/

/**
* @typedef {Object} MiniCssExtractPluginCompilationHooks
* @property {import("tapable").SyncWaterfallHook<[string, VarNames], string>} beforeTagInsert
*/

/** @type {WeakMap<Compilation, MiniCssExtractPluginCompilationHooks>} */
const compilationHooksMap = new WeakMap();

/**
*
* @param {Compilation} compilation the compilation
* @returns {MiniCssExtractPluginCompilationHooks} the compilation hooks
*/
exports.getCompilationHooks = function getCompilationHooks(compilation) {
let hooks = compilationHooksMap.get(compilation);
if (!hooks) {
hooks = {
beforeTagInsert: new SyncWaterfallHook(["source", "varNames"], "string"),
};
compilationHooksMap.set(compilation, hooks);
}
return hooks;
};
msidolphin marked this conversation as resolved.
Show resolved Hide resolved
19 changes: 18 additions & 1 deletion src/index.js
Expand Up @@ -15,6 +15,7 @@ const {
getUndoPath,
BASE_URI,
} = require("./utils");
const { getCompilationHooks } = require("./hooks");

/** @typedef {import("schema-utils/declarations/validate").Schema} Schema */
/** @typedef {import("webpack").Compiler} Compiler */
Expand Down Expand Up @@ -513,6 +514,14 @@ class MiniCssExtractPlugin {
return CssDependency;
}

/**
* Returns all hooks for the given compilation
* @param {Compilation} compilation
*/
static getCompilationHooks(compilation) {
return getCompilationHooks(compilation);
}

/**
* @param {PluginOptions} [options]
*/
Expand Down Expand Up @@ -843,7 +852,6 @@ class MiniCssExtractPlugin {
if (!withLoading && !withHmr) {
return "";
}

return Template.asString([
'if (typeof document === "undefined") return;',
`var createStylesheet = ${runtimeTemplate.basicFunction(
Expand Down Expand Up @@ -902,6 +910,15 @@ class MiniCssExtractPlugin {
"}",
])
: "",
MiniCssExtractPlugin.getCompilationHooks(
compilation
).beforeTagInsert.call("", {
tag: "linkTag",
chunkId: "chunkId",
href: "fullhref",
resolve: "resolve",
reject: "reject",
}) || "",
typeof this.runtimeOptions.insert !== "undefined"
? typeof this.runtimeOptions.insert === "function"
? `(${this.runtimeOptions.insert.toString()})(linkTag)`
Expand Down
Expand Up @@ -73,7 +73,7 @@ __webpack_require__.r(__webpack_exports__);
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("7f0e5fa686a9bb728e64")
/******/ __webpack_require__.h = () => ("04f5273a6b9819ed9e63")
/******/ })();
/******/
/******/ /* webpack/runtime/global */
Expand Down Expand Up @@ -201,6 +201,7 @@ __webpack_require__.r(__webpack_exports__);
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
/******/ linkTag.href = fullhref;
/******/
/******/
/******/ if (oldTag) {
/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);
/******/ } else {
Expand Down
3 changes: 2 additions & 1 deletion test/cases/chunkFilename-fullhash/expected/webpack-5/main.js
Expand Up @@ -73,7 +73,7 @@ __webpack_require__.r(__webpack_exports__);
/******/
/******/ /* webpack/runtime/getFullHash */
/******/ (() => {
/******/ __webpack_require__.h = () => ("100253bb7576627988e6")
/******/ __webpack_require__.h = () => ("04f5273a6b9819ed9e63")
/******/ })();
/******/
/******/ /* webpack/runtime/global */
Expand Down Expand Up @@ -201,6 +201,7 @@ __webpack_require__.r(__webpack_exports__);
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
/******/ linkTag.href = fullhref;
/******/
/******/
/******/ if (oldTag) {
/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);
/******/ } else {
Expand Down
1 change: 1 addition & 0 deletions test/cases/hmr/expected/main.js
Expand Up @@ -964,6 +964,7 @@ __webpack_require__.r(__webpack_exports__);
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
/******/ linkTag.href = fullhref;
/******/
/******/
/******/ if (oldTag) {
/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);
/******/ } else {
Expand Down
1 change: 1 addition & 0 deletions test/cases/insert-function/expected/main.js
Expand Up @@ -185,6 +185,7 @@
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
/******/ linkTag.href = fullhref;
/******/
/******/
/******/ (function (linkTag) {
/******/ const reference = document.querySelector(".hot-reload");
/******/ if (reference) {
Expand Down
1 change: 1 addition & 0 deletions test/cases/insert-string/expected/main.js
Expand Up @@ -185,6 +185,7 @@
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
/******/ linkTag.href = fullhref;
/******/
/******/
/******/ var target = document.querySelector("script[src='1.js']");
/******/ target.parentNode.insertBefore(linkTag, target.nextSibling);
/******/ return linkTag;
Expand Down
1 change: 1 addition & 0 deletions test/cases/insert-undefined/expected/main.js
Expand Up @@ -185,6 +185,7 @@
/******/ linkTag.onerror = linkTag.onload = onLinkComplete;
/******/ linkTag.href = fullhref;
/******/
/******/
/******/ if (oldTag) {
/******/ oldTag.parentNode.insertBefore(linkTag, oldTag.nextSibling);
/******/ } else {
Expand Down
74 changes: 74 additions & 0 deletions test/hooks.test.js
@@ -0,0 +1,74 @@
/* eslint-env browser */
import path from "path";

import { Template } from "webpack";

import MiniCssExtractPlugin from "../src";

import { runInJsDom, compile, getCompiler } from "./helpers/index";

describe("hooks", () => {
it(`beforeTagInsert`, async () => {
const webpackCompiler = getCompiler(
"insert.js",
{},
{
mode: "none",
output: {
publicPath: "",
path: path.resolve(__dirname, "../outputs"),
filename: "[name].bundle.js",
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].css",
}),
{
/**
*
* @param {import('webpack').Compiler} compiler
*/
apply: (compiler) => {
compiler.hooks.compilation.tap("sri", (compilation) => {
MiniCssExtractPlugin.getCompilationHooks(
compilation
).beforeTagInsert.tap("sri", (source, varNames) =>
Template.asString([
source,
`${varNames.tag}.setAttribute("integrity", "sriHashes[${varNames.chunkId}]");`,
])
);
});
},
},
{
/**
*
* @param {import('webpack').Compiler} compiler
*/
apply: (compiler) => {
compiler.hooks.compilation.tap("href", (compilation) => {
MiniCssExtractPlugin.getCompilationHooks(
compilation
).beforeTagInsert.tap("changeHref", (source, varNames) =>
Template.asString([
source,
`${varNames.tag}.setAttribute("href", "https://github.com/webpack-contrib/mini-css-extract-plugin");`,
])
);
});
},
},
],
}
);
const stats = await compile(webpackCompiler);
runInJsDom("main.bundle.js", webpackCompiler, stats, (dom) => {
const [tag] = dom.window.document.head.getElementsByTagName("link");
expect(tag.getAttribute("integrity")).toBe("sriHashes[chunkId]");
expect(tag.getAttribute("href")).toBe(
"https://github.com/webpack-contrib/mini-css-extract-plugin"
);
});
});
});
17 changes: 17 additions & 0 deletions types/hooks.d.ts
@@ -0,0 +1,17 @@
export function getCompilationHooks(
compilation: Compilation
): MiniCssExtractPluginCompilationHooks;
export type Compilation = import("webpack").Compilation;
export type VarNames = {
tag: string;
chunkId: string;
href: string;
resolve: string;
reject: string;
};
export type MiniCssExtractPluginCompilationHooks = {
beforeTagInsert: import("tapable").SyncWaterfallHook<
[string, VarNames],
string
>;
};
9 changes: 8 additions & 1 deletion types/index.d.ts
Expand Up @@ -12,6 +12,13 @@ declare class MiniCssExtractPlugin {
static getCssDependency(
webpack: Compiler["webpack"]
): CssDependencyConstructor;
/**
* Returns all hooks for the given compilation
* @param {Compilation} compilation
*/
static getCompilationHooks(
compilation: Compilation
): import("./hooks").MiniCssExtractPluginCompilationHooks;
/**
* @param {PluginOptions} [options]
*/
Expand Down Expand Up @@ -103,6 +110,7 @@ type CssDependencyConstructor = new (
context: string | null,
identifierIndex: number
) => CssDependency;
type Compilation = import("webpack").Compilation;
type PluginOptions = {
filename?: Required<Configuration>["output"]["filename"];
chunkFilename?: Required<Configuration>["output"]["chunkFilename"];
Expand Down Expand Up @@ -166,7 +174,6 @@ declare const pluginName: "mini-css-extract-plugin";
declare const pluginSymbol: unique symbol;
declare var loader: string;
type Schema = import("schema-utils/declarations/validate").Schema;
type Compilation = import("webpack").Compilation;
type ChunkGraph = import("webpack").ChunkGraph;
type Chunk = import("webpack").Chunk;
type ChunkGroup = Parameters<import("webpack").Chunk["isInGroup"]>[0];
Expand Down