From 93ec94905e715c860c4a6cb3f4db526f3dd64831 Mon Sep 17 00:00:00 2001
From: LingyuCoder
Date: Wed, 14 Aug 2024 13:09:58 +0800
Subject: [PATCH 1/7] test: test html plugin with basic cases of
html-webpack-plugin
---
.gitignore | 2 +
.prettierignore | 3 +-
biome.jsonc | 1 -
crates/node_binding/binding.d.ts | 15 -
.../rspack_plugin_html/src/visitors/asset.rs | 12 +-
package.json | 2 +-
pnpm-lock.yaml | 50 +
tests/plugin-test/README.md | 2 +
tests/plugin-test/html-plugin/README.md | 9 +
tests/plugin-test/html-plugin/basic.test.js | 3847 +++++++++++++++++
.../plugin-test/html-plugin/fixtures/async.js | 2 +
.../html-plugin/fixtures/common.js | 3 +
.../html-plugin/fixtures/empty_html.html | 0
.../html-plugin/fixtures/favicon.ico | Bin 0 -> 766 bytes
.../fixtures/html-template-with-image.html | 13 +
.../plugin-test/html-plugin/fixtures/index.js | 9 +
.../html-plugin/fixtures/interpolation.html | 10 +
.../html-plugin/fixtures/invalid.html | 11 +
.../html-plugin/fixtures/logo.html | 11 +
.../plugin-test/html-plugin/fixtures/logo.png | Bin 0 -> 54055 bytes
.../plugin-test/html-plugin/fixtures/main.css | 3 +
.../html-plugin/fixtures/plain.html | 9 +
.../html-plugin/fixtures/spaced_plain.html | 7 +
.../html-plugin/fixtures/src/index.ejs | 10 +
.../html-plugin/fixtures/template.pug | 9 +
.../html-plugin/fixtures/templateParam.cjs | 21 +
.../html-plugin/fixtures/templateParam.js | 21 +
.../html-plugin/fixtures/test.html | 11 +
.../plugin-test/html-plugin/fixtures/theme.js | 4 +
.../plugin-test/html-plugin/fixtures/util.js | 4 +
.../html-plugin/fixtures/webpackconfig.html | 11 +
tests/plugin-test/package.json | 2 +
32 files changed, 4090 insertions(+), 24 deletions(-)
create mode 100644 tests/plugin-test/html-plugin/README.md
create mode 100644 tests/plugin-test/html-plugin/basic.test.js
create mode 100644 tests/plugin-test/html-plugin/fixtures/async.js
create mode 100644 tests/plugin-test/html-plugin/fixtures/common.js
create mode 100644 tests/plugin-test/html-plugin/fixtures/empty_html.html
create mode 100755 tests/plugin-test/html-plugin/fixtures/favicon.ico
create mode 100644 tests/plugin-test/html-plugin/fixtures/html-template-with-image.html
create mode 100644 tests/plugin-test/html-plugin/fixtures/index.js
create mode 100644 tests/plugin-test/html-plugin/fixtures/interpolation.html
create mode 100644 tests/plugin-test/html-plugin/fixtures/invalid.html
create mode 100644 tests/plugin-test/html-plugin/fixtures/logo.html
create mode 100644 tests/plugin-test/html-plugin/fixtures/logo.png
create mode 100644 tests/plugin-test/html-plugin/fixtures/main.css
create mode 100644 tests/plugin-test/html-plugin/fixtures/plain.html
create mode 100644 tests/plugin-test/html-plugin/fixtures/spaced_plain.html
create mode 100644 tests/plugin-test/html-plugin/fixtures/src/index.ejs
create mode 100644 tests/plugin-test/html-plugin/fixtures/template.pug
create mode 100644 tests/plugin-test/html-plugin/fixtures/templateParam.cjs
create mode 100644 tests/plugin-test/html-plugin/fixtures/templateParam.js
create mode 100644 tests/plugin-test/html-plugin/fixtures/test.html
create mode 100644 tests/plugin-test/html-plugin/fixtures/theme.js
create mode 100644 tests/plugin-test/html-plugin/fixtures/util.js
create mode 100644 tests/plugin-test/html-plugin/fixtures/webpackconfig.html
diff --git a/.gitignore b/.gitignore
index bc78f263784..78d24875a40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -221,6 +221,8 @@ tests/webpack-test/**/dev-defaults.webpack.lock
tests/plugin-test/css-extract/js
!tests/plugin-test/css-extract/**/node_modules
+tests/plugin-test/html-plugin/js
+
/webpack-examples/**/dist
smoke-example
diff --git a/.prettierignore b/.prettierignore
index f4827610165..203645a95fc 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -13,9 +13,8 @@ packages/**/etc/**/*
packages/rspack-test-tools/template/**/*
packages/rspack-test-tools/src/helper/legacy/**/*
packages/rspack-test-tools/tests/**/*
-packages/rspack-plugin-mini-css-extract/test/cases/**/*
crates/**/*
target/**/*
-tests/**/*.*
+tests/**/*
!tests/**/test.filter.js
diff --git a/biome.jsonc b/biome.jsonc
index b201eaf8d51..b2908ab3e6f 100644
--- a/biome.jsonc
+++ b/biome.jsonc
@@ -19,7 +19,6 @@
"packages/rspack-test-tools/template/**/*",
"packages/rspack-test-tools/tests/**/*",
"packages/rspack-test-tools/src/helper/legacy/**/*",
- "packages/rspack-plugin-mini-css-extract/test/cases/**/*",
"packages/playground/**/*",
// --- ignore runtime code in browser
"packages/rspack/hot",
diff --git a/crates/node_binding/binding.d.ts b/crates/node_binding/binding.d.ts
index 883f67afbaf..2128bf7c555 100644
--- a/crates/node_binding/binding.d.ts
+++ b/crates/node_binding/binding.d.ts
@@ -907,13 +907,6 @@ export interface JsTap {
stage: number
}
-export interface NodeFS {
- writeFile: (...args: any[]) => any
- removeFile: (...args: any[]) => any
- mkdir: (...args: any[]) => any
- mkdirp: (...args: any[]) => any
-}
-
export interface PathWithInfo {
path: string
info: JsAssetInfo
@@ -1821,11 +1814,3 @@ export interface RegisterJsTaps {
registerJavascriptModulesChunkHashTaps: (stages: Array) => Array<{ function: ((arg: JsChunk) => Buffer); stage: number; }>
}
-export interface ThreadsafeNodeFS {
- writeFile: (name: string, content: Buffer) => Promise | void
- removeFile: (name: string) => Promise | void
- mkdir: (name: string) => Promise | void
- mkdirp: (name: string) => Promise | string | void
- removeDirAll: (name: string) => Promise | string | void
-}
-
diff --git a/crates/rspack_plugin_html/src/visitors/asset.rs b/crates/rspack_plugin_html/src/visitors/asset.rs
index 79044ade299..a90ae6ba4a1 100644
--- a/crates/rspack_plugin_html/src/visitors/asset.rs
+++ b/crates/rspack_plugin_html/src/visitors/asset.rs
@@ -45,15 +45,12 @@ impl HTMLPluginTag {
append_to: HtmlInject,
script_loading: &HtmlScriptLoading,
) -> HTMLPluginTag {
- let mut attributes = vec![HtmlPluginAttribute {
- attr_name: "src".to_string(),
- attr_value: Some(src.to_string()),
- }];
+ let mut attributes = vec![];
match script_loading {
HtmlScriptLoading::Defer => {
attributes.push(HtmlPluginAttribute {
attr_name: "defer".to_string(),
- attr_value: None,
+ attr_value: Some("defer".to_string()),
});
}
HtmlScriptLoading::Module => {
@@ -65,6 +62,11 @@ impl HTMLPluginTag {
_ => {}
}
+ attributes.push(HtmlPluginAttribute {
+ attr_name: "src".to_string(),
+ attr_value: Some(src.to_string()),
+ });
+
HTMLPluginTag {
tag_name: "script".to_string(),
append_to,
diff --git a/package.json b/package.json
index f2f52956a33..1377fd41e11 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"x": "zx x.mjs",
"dev": "pnpm --filter @rspack/cli run dev",
"clean": "pnpm --filter @rspack/cli run clean",
- "check-dependency-version": "check-dependency-version-consistency . --ignore-dep typescript --ignore-dep @napi-rs/cli --ignore-dep chalk --ignore-package webpack-test --ignore-package webpack-examples",
+ "check-dependency-version": "check-dependency-version-consistency . --ignore-dep typescript --ignore-dep @napi-rs/cli --ignore-dep chalk --ignore-package webpack-test --ignore-package webpack-examples --ignore-package plugin-test",
"build:js": "pnpm --filter \"@rspack/core\" build:force && pnpm --filter \"@rspack/*\" --filter \"!@rspack/core\" build",
"build:cli:debug": "npm run build:binding:debug && npm run build:js",
"build:cli:release": "npm run build:binding:release && npm run build:js",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e7166cbd8c7..de42454b398 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -929,12 +929,18 @@ importers:
file-loader:
specifier: ^6.2.0
version: 6.2.0(webpack@5.92.0(webpack-cli@5.1.4(webpack@5.92.0)))
+ html-loader:
+ specifier: 2.1.1
+ version: 2.1.1(webpack@5.92.0(webpack-cli@5.1.4(webpack@5.92.0)))
jsdom:
specifier: ^24.0.0
version: 24.0.0
memfs:
specifier: 4.8.1
version: 4.8.1
+ pug-loader:
+ specifier: 2.4.0
+ version: 2.4.0(pug@2.0.4)
sass-embedded:
specifier: ^1.77.8
version: 1.77.8
@@ -5678,12 +5684,23 @@ packages:
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+ html-loader@2.1.1:
+ resolution: {integrity: sha512-L6HUVgr2aeGMyiool2raoBQb8Lue2UTAhUsHvsMLyNxfBHMtOz1NJL1GyE4VbyAoYBK5PJp1I9Aw+FqS3d2eTA==}
+ engines: {node: '>= 10.13.0'}
+ peerDependencies:
+ webpack: ^5.0.0
+
html-loader@5.0.0:
resolution: {integrity: sha512-puaGKdjdVVIFRtgIC2n5dt5bt0N5j6heXlAQZ4Do1MLjHmOT1gCE1Ogg7XZNeJlnOVHHsrZKGs5dfh+XwZ3XPw==}
engines: {node: '>= 18.12.0'}
peerDependencies:
webpack: ^5.0.0
+ html-minifier-terser@5.1.1:
+ resolution: {integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
html-minifier-terser@6.1.0:
resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==}
engines: {node: '>=12'}
@@ -7100,6 +7117,9 @@ packages:
parse-svg-path@0.1.2:
resolution: {integrity: sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ==}
+ parse5@6.0.1:
+ resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
+
parse5@7.1.2:
resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
@@ -8552,6 +8572,11 @@ packages:
uglify-js:
optional: true
+ terser@4.8.1:
+ resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
terser@5.27.2:
resolution: {integrity: sha512-sHXmLSkImesJ4p5apTeT63DsV4Obe1s37qT8qvwHRmVxKTBH7Rv9Wr26VcAMmLbmk9UliiwK8z+657NyJHHy/w==}
engines: {node: '>=10'}
@@ -15387,12 +15412,28 @@ snapshots:
html-escaper@2.0.2: {}
+ html-loader@2.1.1(webpack@5.92.0(webpack-cli@5.1.4(webpack@5.92.0))):
+ dependencies:
+ html-minifier-terser: 5.1.1
+ parse5: 6.0.1
+ webpack: 5.92.0(webpack-cli@5.1.4(webpack@5.92.0))
+
html-loader@5.0.0(webpack@5.92.0(webpack-cli@5.1.4(webpack@5.92.0))):
dependencies:
html-minifier-terser: 7.2.0
parse5: 7.1.2
webpack: 5.92.0(webpack-cli@5.1.4(webpack@5.92.0))
+ html-minifier-terser@5.1.1:
+ dependencies:
+ camel-case: 4.1.2
+ clean-css: 4.2.4
+ commander: 4.1.1
+ he: 1.2.0
+ param-case: 3.0.4
+ relateurl: 0.2.7
+ terser: 4.8.1
+
html-minifier-terser@6.1.0:
dependencies:
camel-case: 4.1.2
@@ -17034,6 +17075,8 @@ snapshots:
parse-svg-path@0.1.2: {}
+ parse5@6.0.1: {}
+
parse5@7.1.2:
dependencies:
entities: 4.5.0
@@ -18678,6 +18721,13 @@ snapshots:
terser: 5.27.2
webpack: 5.92.0(webpack-cli@5.1.4)
+ terser@4.8.1:
+ dependencies:
+ acorn: 8.11.3
+ commander: 2.20.3
+ source-map: 0.6.1
+ source-map-support: 0.5.21
+
terser@5.27.2:
dependencies:
'@jridgewell/source-map': 0.3.6
diff --git a/tests/plugin-test/README.md b/tests/plugin-test/README.md
index bccb83047e8..b45beab57ae 100644
--- a/tests/plugin-test/README.md
+++ b/tests/plugin-test/README.md
@@ -3,6 +3,7 @@
>
> - [mini-css-extract-plugin/test](https://github.com/webpack-contrib/mini-css-extract-plugin/tree/master/test)
> - [copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin/tree/master/test)
+> - [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin/tree/main/spec)
## Credits
@@ -10,3 +11,4 @@ Thanks to:
- The [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) project created by [@sokra](https://github.com/sokra)
- The [copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin) project created by [@kevlened](https://github.com/kevlened)
+- The [html-webpack-plugin](https://github.com/jantimon/html-webpack-plugin) project created by [@jantimon](https://github.com/jantimon)
diff --git a/tests/plugin-test/html-plugin/README.md b/tests/plugin-test/html-plugin/README.md
new file mode 100644
index 00000000000..33252bb7f27
--- /dev/null
+++ b/tests/plugin-test/html-plugin/README.md
@@ -0,0 +1,9 @@
+/**
+ * The test code is modified based on
+ * https://github.com/jantimon/html-webpack-plugin/tree/main/spec
+ *
+ * MIT Licensed
+ * Author Jan Nicklas @jantimon
+ * Copyright JS Foundation and other contributors
+ * https://github.com/jantimon/html-webpack-plugin/blob/main/LICENSE
+ */
\ No newline at end of file
diff --git a/tests/plugin-test/html-plugin/basic.test.js b/tests/plugin-test/html-plugin/basic.test.js
new file mode 100644
index 00000000000..405bcce2f09
--- /dev/null
+++ b/tests/plugin-test/html-plugin/basic.test.js
@@ -0,0 +1,3847 @@
+/*
+ * Integration and unit tests for all features but caching
+ */
+
+/* eslint-env jest */
+"use strict";
+
+const path = require("path");
+const fs = require("fs");
+/// DIFF const webpack = require("webpack");
+const webpack = require("@rspack/core");
+const rimraf = require("rimraf");
+const _ = require("lodash");
+/// DIFF const MiniCssExtractPlugin = require("mini-css-extract-plugin");
+const MiniCssExtractPlugin = webpack.CssExtractRspackPlugin;
+
+const webpackMajorVersion = Number(
+ /// DIFF: require("webpack/package.json")
+ require("@rspack/core/package.json").webpackVersion.split(".")[0],
+);
+const itUnixOnly =
+ process.platform === "win32" || process.platform === "win64" ? it.skip : it;
+
+if (isNaN(webpackMajorVersion)) {
+ throw new Error("Cannot parse webpack major version");
+}
+
+/// DIFF: const HtmlWebpackPlugin = require("@rspack/");
+const HtmlWebpackPlugin = webpack.HtmlRspackPlugin;
+
+
+/// DIFF: const OUTPUT_DIR = path.resolve(__dirname, "../dist/basic-spec");
+const OUTPUT_DIR = path.resolve(__dirname, "./js/basic-spec");
+
+jest.setTimeout(30000);
+process.on("unhandledRejection", (r) => console.log(r));
+
+function testHtmlPlugin(
+ webpackConfig,
+ expectedResults,
+ outputFile,
+ done,
+ expectErrors,
+ expectWarnings,
+) {
+ outputFile = outputFile || "index.html";
+ webpack(webpackConfig, (err, stats) => {
+ try {
+ expect(err).toBeFalsy();
+ const compilationErrors = (Array.from(stats.compilation.errors).map(i => i.message || '') || []).join("\n");
+ if (expectErrors) {
+ expect(compilationErrors).not.toBe("");
+ } else {
+ expect(compilationErrors).toBe("");
+ }
+ const compilationWarnings = (Array.from(stats.compilation.warnings) || []).join("\n");
+ if (expectWarnings) {
+ expect(compilationWarnings).not.toBe("");
+ } else {
+ expect(compilationWarnings).toBe("");
+ }
+ if (outputFile instanceof RegExp) {
+ const fileNames = Object.keys(stats.compilation.assets);
+ const matches = Object.keys(stats.compilation.assets).filter((item) =>
+ outputFile.test(item),
+ );
+ expect(matches[0] || fileNames).not.toEqual(fileNames);
+ outputFile = matches[0];
+ }
+ expect(outputFile.indexOf("[hash]") === -1).toBe(true);
+ const outputFileExists = fs.existsSync(path.join(OUTPUT_DIR, outputFile));
+ expect(outputFileExists).toBe(true);
+ if (!outputFileExists) {
+ return done();
+ }
+ const htmlContent = fs
+ .readFileSync(path.join(OUTPUT_DIR, outputFile))
+ .toString();
+ let chunksInfo;
+ for (let i = 0; i < expectedResults.length; i++) {
+ const expectedResult = expectedResults[i];
+ if (expectedResult instanceof RegExp) {
+ expect(htmlContent).toMatch(expectedResult);
+ } else if (typeof expectedResult === "object") {
+ if (expectedResult.type === "chunkhash") {
+ if (!chunksInfo) {
+ chunksInfo = getChunksInfoFromStats(stats);
+ }
+ const chunkhash = chunksInfo[expectedResult.chunkName].hash;
+ expect(htmlContent).toContain(
+ expectedResult.containStr.replace("%chunkhash%", chunkhash),
+ );
+ }
+ } else {
+ expect(htmlContent).toContain(
+ expectedResult.replace("%hash%", stats.hash),
+ );
+ }
+ }
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+}
+
+function getChunksInfoFromStats(stats) {
+ const chunks = stats.compilation.getStats().toJson().chunks;
+ const chunksInfo = {};
+ for (let i = 0; i < chunks.length; i++) {
+ const chunk = chunks[i];
+ const chunkName = chunk.names[0];
+ if (chunkName) {
+ chunksInfo[chunkName] = chunk;
+ }
+ }
+ return chunksInfo;
+}
+
+describe("HtmlWebpackPlugin", () => {
+ beforeEach((done) => {
+ rimraf(OUTPUT_DIR, done);
+ });
+
+ it("generates a default index.html file for a single entry point", (done) => {
+ testHtmlPlugin(
+ {
+ mode: "production",
+ entry: path.join(__dirname, "fixtures/index.js"),
+ output: {
+ path: OUTPUT_DIR,
+ filename: "index_bundle.js",
+ },
+ plugins: [new HtmlWebpackPlugin()],
+ },
+ [/',
+ ],
+ null,
+ done,
+ );
+ });
+
+ // TODO: support templateContent function
+ // it("should allow to use headTags and bodyTags directly in string literals", (done) => {
+ // testHtmlPlugin(
+ // {
+ // mode: "production",
+ // entry: path.join(__dirname, "fixtures/theme.js"),
+ // output: {
+ // path: OUTPUT_DIR,
+ // filename: "index_bundle.js",
+ // },
+ // module: {
+ // rules: [
+ // {
+ // test: /\.css$/,
+ // use: [MiniCssExtractPlugin.loader, "css-loader"],
+ // },
+ // ],
+ // },
+ // plugins: [
+ // new MiniCssExtractPlugin({ filename: "styles.css" }),
+ // new HtmlWebpackPlugin({
+ // scriptLoading: "blocking",
+ // inject: false,
+ // templateContent: ({ htmlWebpackPlugin }) => `
+ //
+ // ${htmlWebpackPlugin.tags.headTags}
+ // ${htmlWebpackPlugin.tags.bodyTags}
+ //
+ // `,
+ // }),
+ // ],
+ // },
+ // [
+ // '',
+ // '
${htmlWebpackPlugin.tags.bodyTags}',
+ // ],
+ // null,
+ // done,
+ // );
+ // });
+
+ it("should add the javascript assets to the head for inject:true with scriptLoading:defer", (done) => {
+ testHtmlPlugin(
+ {
+ mode: "production",
+ entry: path.join(__dirname, "fixtures/theme.js"),
+ output: {
+ path: OUTPUT_DIR,
+ filename: "index_bundle.js",
+ },
+ module: {
+ rules: [
+ {
+ test: /\.css$/,
+ use: [MiniCssExtractPlugin.loader, "css-loader"],
+ },
+ ],
+ },
+ plugins: [
+ new MiniCssExtractPlugin({ filename: "styles.css" }),
+ new HtmlWebpackPlugin({
+ scriptLoading: "defer",
+ inject: true,
+ }),
+ ],
+ },
+ [
+ '',
+ ],
+ null,
+ done,
+ );
+ });
+
+ // TODO: support templateContent function
+ // it("should allow to use headTags and bodyTags directly in string literals", (done) => {
+ // testHtmlPlugin(
+ // {
+ // mode: "production",
+ // entry: path.join(__dirname, "fixtures/theme.js"),
+ // output: {
+ // path: OUTPUT_DIR,
+ // filename: "index_bundle.js",
+ // },
+ // module: {
+ // rules: [
+ // {
+ // test: /\.css$/,
+ // use: [MiniCssExtractPlugin.loader, "css-loader"],
+ // },
+ // ],
+ // },
+ // plugins: [
+ // new MiniCssExtractPlugin({ filename: "styles.css" }),
+ // new HtmlWebpackPlugin({
+ // inject: false,
+ // templateContent: ({ htmlWebpackPlugin }) => `
+ //
+ //