diff --git a/lib/css/CssModulesPlugin.js b/lib/css/CssModulesPlugin.js index 5b32c7c57..6d4fa47c4 100644 --- a/lib/css/CssModulesPlugin.js +++ b/lib/css/CssModulesPlugin.js @@ -392,7 +392,7 @@ class CssModulesPlugin { options && options.chunkLoading !== undefined ? options.chunkLoading : globalChunkLoading; - return chunkLoading === "jsonp"; + return chunkLoading === "jsonp" || chunkLoading === "import"; }; const onceForChunkSet = new WeakSet(); /** diff --git a/test/ConfigTestCases.template.js b/test/ConfigTestCases.template.js index ab67bdf07..fb318d44a 100644 --- a/test/ConfigTestCases.template.js +++ b/test/ConfigTestCases.template.js @@ -442,6 +442,8 @@ const describeCases = config => { baseModuleScope.document = globalContext.document; baseModuleScope.setTimeout = globalContext.setTimeout; baseModuleScope.clearTimeout = globalContext.clearTimeout; + baseModuleScope.getComputedStyle = + globalContext.getComputedStyle; baseModuleScope.URL = URL; baseModuleScope.Worker = require("./helpers/createFakeWorker")({ @@ -540,6 +542,7 @@ const describeCases = config => { } if (esmMode === "unlinked") return esm; return (async () => { + if (esmMode === "unlinked") return esm; await esm.link( async (specifier, referencingModule) => { return await asModule( diff --git a/test/configCases/css/basic-esm-target-node/index.js b/test/configCases/css/basic-esm-target-node/index.js new file mode 100644 index 000000000..49a5dd1fd --- /dev/null +++ b/test/configCases/css/basic-esm-target-node/index.js @@ -0,0 +1,9 @@ +import * as style from "./style.css"; + +it("should compile and load style on demand", done => { + expect(style).toEqual(nsObj({})); + import("./style2.css").then(x => { + expect(x).toEqual(nsObj({})); + done(); + }, done); +}); diff --git a/test/configCases/css/basic-esm-target-node/style-imported.css b/test/configCases/css/basic-esm-target-node/style-imported.css new file mode 100644 index 000000000..eb0ae4514 --- /dev/null +++ b/test/configCases/css/basic-esm-target-node/style-imported.css @@ -0,0 +1,3 @@ +body { + margin: 10px; +} diff --git a/test/configCases/css/basic-esm-target-node/style.css b/test/configCases/css/basic-esm-target-node/style.css new file mode 100644 index 000000000..ba0cfaf65 --- /dev/null +++ b/test/configCases/css/basic-esm-target-node/style.css @@ -0,0 +1,4 @@ +@import "style-imported.css"; +body { + background: red; +} diff --git a/test/configCases/css/basic-esm-target-node/style2-imported.css b/test/configCases/css/basic-esm-target-node/style2-imported.css new file mode 100644 index 000000000..ff9387e5d --- /dev/null +++ b/test/configCases/css/basic-esm-target-node/style2-imported.css @@ -0,0 +1,3 @@ +body { + padding: 20px 10px; +} diff --git a/test/configCases/css/basic-esm-target-node/style2.css b/test/configCases/css/basic-esm-target-node/style2.css new file mode 100644 index 000000000..d80cbcd05 --- /dev/null +++ b/test/configCases/css/basic-esm-target-node/style2.css @@ -0,0 +1,4 @@ +@import "./style2-imported.css"; +body { + color: green; +} diff --git a/test/configCases/css/basic-esm-target-node/webpack.config.js b/test/configCases/css/basic-esm-target-node/webpack.config.js new file mode 100644 index 000000000..91b082607 --- /dev/null +++ b/test/configCases/css/basic-esm-target-node/webpack.config.js @@ -0,0 +1,13 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: "node", + mode: "development", + experiments: { + outputModule: true, + css: true + }, + output: { + module: true, + chunkFormat: "module" + } +}; diff --git a/test/configCases/css/basic-esm-target-web/index.js b/test/configCases/css/basic-esm-target-web/index.js new file mode 100644 index 000000000..c15078254 --- /dev/null +++ b/test/configCases/css/basic-esm-target-web/index.js @@ -0,0 +1,14 @@ +import * as style from "./style.css"; + +it("should compile and load style on demand", done => { + expect(style).toEqual(nsObj({})); + import("./style2.css").then(x => { + expect(x).toEqual(nsObj({})); + const style = getComputedStyle(document.body); + expect(style.getPropertyValue("background")).toBe(" red"); + expect(style.getPropertyValue("margin")).toBe(" 10px"); + expect(style.getPropertyValue("color")).toBe(" green"); + expect(style.getPropertyValue("padding")).toBe(" 20px 10px"); + done(); + }, done); +}); diff --git a/test/configCases/css/basic-esm-target-web/style-imported.css b/test/configCases/css/basic-esm-target-web/style-imported.css new file mode 100644 index 000000000..eb0ae4514 --- /dev/null +++ b/test/configCases/css/basic-esm-target-web/style-imported.css @@ -0,0 +1,3 @@ +body { + margin: 10px; +} diff --git a/test/configCases/css/basic-esm-target-web/style.css b/test/configCases/css/basic-esm-target-web/style.css new file mode 100644 index 000000000..ba0cfaf65 --- /dev/null +++ b/test/configCases/css/basic-esm-target-web/style.css @@ -0,0 +1,4 @@ +@import "style-imported.css"; +body { + background: red; +} diff --git a/test/configCases/css/basic-esm-target-web/style2-imported.css b/test/configCases/css/basic-esm-target-web/style2-imported.css new file mode 100644 index 000000000..ff9387e5d --- /dev/null +++ b/test/configCases/css/basic-esm-target-web/style2-imported.css @@ -0,0 +1,3 @@ +body { + padding: 20px 10px; +} diff --git a/test/configCases/css/basic-esm-target-web/style2.css b/test/configCases/css/basic-esm-target-web/style2.css new file mode 100644 index 000000000..d80cbcd05 --- /dev/null +++ b/test/configCases/css/basic-esm-target-web/style2.css @@ -0,0 +1,4 @@ +@import "./style2-imported.css"; +body { + color: green; +} diff --git a/test/configCases/css/basic-esm-target-web/test.config.js b/test/configCases/css/basic-esm-target-web/test.config.js new file mode 100644 index 000000000..059075728 --- /dev/null +++ b/test/configCases/css/basic-esm-target-web/test.config.js @@ -0,0 +1,8 @@ +module.exports = { + moduleScope(scope) { + const link = scope.window.document.createElement("link"); + link.rel = "stylesheet"; + link.href = "bundle0.css"; + scope.window.document.head.appendChild(link); + } +}; diff --git a/test/configCases/css/basic-esm-target-web/webpack.config.js b/test/configCases/css/basic-esm-target-web/webpack.config.js new file mode 100644 index 000000000..673fa0ebd --- /dev/null +++ b/test/configCases/css/basic-esm-target-web/webpack.config.js @@ -0,0 +1,13 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + target: "web", + mode: "development", + experiments: { + outputModule: true, + css: true + }, + output: { + module: true, + chunkFormat: "module" + } +}; diff --git a/test/configCases/css/prefetch-preload-module/chunk1-a.css b/test/configCases/css/prefetch-preload-module/chunk1-a.css new file mode 100644 index 000000000..195b6bcf6 --- /dev/null +++ b/test/configCases/css/prefetch-preload-module/chunk1-a.css @@ -0,0 +1,3 @@ +a { + color: red; +} diff --git a/test/configCases/css/prefetch-preload-module/chunk1-a.mjs b/test/configCases/css/prefetch-preload-module/chunk1-a.mjs new file mode 100644 index 000000000..e69de29bb diff --git a/test/configCases/css/prefetch-preload-module/chunk1-b.mjs b/test/configCases/css/prefetch-preload-module/chunk1-b.mjs new file mode 100644 index 000000000..e69de29bb diff --git a/test/configCases/css/prefetch-preload-module/chunk1-c.mjs b/test/configCases/css/prefetch-preload-module/chunk1-c.mjs new file mode 100644 index 000000000..e69de29bb diff --git a/test/configCases/css/prefetch-preload-module/chunk1.css b/test/configCases/css/prefetch-preload-module/chunk1.css new file mode 100644 index 000000000..195b6bcf6 --- /dev/null +++ b/test/configCases/css/prefetch-preload-module/chunk1.css @@ -0,0 +1,3 @@ +a { + color: red; +} diff --git a/test/configCases/css/prefetch-preload-module/chunk1.mjs b/test/configCases/css/prefetch-preload-module/chunk1.mjs new file mode 100644 index 000000000..eedf37837 --- /dev/null +++ b/test/configCases/css/prefetch-preload-module/chunk1.mjs @@ -0,0 +1,6 @@ +export default function() { + import(/* webpackPrefetch: true, webpackChunkName: "chunk1-a" */ "./chunk1-a.mjs"); + import(/* webpackPreload: true, webpackChunkName: "chunk1-b" */ "./chunk1-b.mjs"); + import(/* webpackPreload: true, webpackChunkName: "chunk1-a-css" */ "./chunk1-a.css"); + import(/* webpackPrefetch: 10, webpackChunkName: "chunk1-c" */ "./chunk1-c.mjs"); +} diff --git a/test/configCases/css/prefetch-preload-module/chunk2.css b/test/configCases/css/prefetch-preload-module/chunk2.css new file mode 100644 index 000000000..3b4cc03b6 --- /dev/null +++ b/test/configCases/css/prefetch-preload-module/chunk2.css @@ -0,0 +1,3 @@ +a { + color: blue; +} diff --git a/test/configCases/css/prefetch-preload-module/chunk2.mjs b/test/configCases/css/prefetch-preload-module/chunk2.mjs new file mode 100644 index 000000000..1c565540e --- /dev/null +++ b/test/configCases/css/prefetch-preload-module/chunk2.mjs @@ -0,0 +1,4 @@ +export default function() { + import(/* webpackPrefetch: true, webpackChunkName: "chunk1-a" */ "./chunk1-a.mjs"); + import(/* webpackPreload: true, webpackChunkName: "chunk1-b" */ "./chunk1-b.mjs"); +} diff --git a/test/configCases/css/prefetch-preload-module/index.mjs b/test/configCases/css/prefetch-preload-module/index.mjs new file mode 100644 index 000000000..e1150be6b --- /dev/null +++ b/test/configCases/css/prefetch-preload-module/index.mjs @@ -0,0 +1,68 @@ +// This config need to be set on initial evaluation to be effective +__webpack_nonce__ = "nonce"; +__webpack_public_path__ = "https://example.com/public/path/"; + +it("should prefetch and preload child chunks on chunk load", () => { + let link, script; + + expect(document.head._children).toHaveLength(1); + + // Test prefetch + link = document.head._children[0]; + expect(link._type).toBe("link"); + expect(link.rel).toBe("prefetch"); + expect(link.as).toBe("style"); + expect(link.href).toBe("https://example.com/public/path/chunk2-css.css"); + + const promise = import( + /* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.mjs" + ); + + expect(document.head._children).toHaveLength(2); + + // Test normal script loading + link = document.head._children[1]; + expect(link._type).toBe("link"); + expect(link.rel).toBe("preload"); + expect(link.as).toBe("style"); + expect(link.href).toBe("https://example.com/public/path/chunk1-a-css.css"); + + return promise.then(() => { + expect(document.head._children).toHaveLength(2); + + const promise2 = import( + /* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1.mjs" + ); + + // Loading chunk1 again should not trigger prefetch/preload + expect(document.head._children).toHaveLength(2); + + const promise3 = import(/* webpackChunkName: "chunk2" */ "./chunk2.mjs"); + + expect(document.head._children).toHaveLength(2); + + return promise3.then(() => { + expect(document.head._children).toHaveLength(2); + + const promise4 = import(/* webpackChunkName: "chunk1-css" */ "./chunk1.css"); + + expect(document.head._children).toHaveLength(3); + + link = document.head._children[2]; + expect(link._type).toBe("link"); + expect(link.rel).toBe("stylesheet"); + expect(link.href).toBe("https://example.com/public/path/chunk1-css.css"); + expect(link.crossOrigin).toBe("anonymous"); + + const promise5 = import(/* webpackChunkName: "chunk2-css", webpackPrefetch: true */ "./chunk2.css"); + + expect(document.head._children).toHaveLength(4); + + link = document.head._children[3]; + expect(link._type).toBe("link"); + expect(link.rel).toBe("stylesheet"); + expect(link.href).toBe("https://example.com/public/path/chunk2-css.css"); + expect(link.crossOrigin).toBe("anonymous"); + }); + }); +}); diff --git a/test/configCases/css/prefetch-preload-module/webpack.config.js b/test/configCases/css/prefetch-preload-module/webpack.config.js new file mode 100644 index 000000000..1d4d67a70 --- /dev/null +++ b/test/configCases/css/prefetch-preload-module/webpack.config.js @@ -0,0 +1,24 @@ +/** @type {import("../../../../").Configuration} */ +module.exports = { + entry: "./index.mjs", + experiments: { + outputModule: true, + css: true + }, + name: "esm", + target: "web", + output: { + publicPath: "", + module: true, + filename: "bundle0.mjs", + chunkFilename: "[name].mjs", + crossOriginLoading: "anonymous", + chunkFormat: "module" + }, + performance: { + hints: false + }, + optimization: { + minimize: false + } +}; diff --git a/test/helpers/FakeDocument.js b/test/helpers/FakeDocument.js index 9ef37106c..fc653801f 100644 --- a/test/helpers/FakeDocument.js +++ b/test/helpers/FakeDocument.js @@ -197,16 +197,16 @@ class FakeSheet { currentRule[property] = value; } }; - let css = fs.readFileSync( - path.resolve( - this._basePath, - this._element.href - .replace(/^https:\/\/test\.cases\/path\//, "") - .replace(/^https:\/\/example\.com\/public\/path\//, "") - .replace(/^https:\/\/example\.com\//, "") - ), - "utf-8" - ); + const filepath = /file:\/\//.test(this._element.href) + ? new URL(this._element.href) + : path.resolve( + this._basePath, + this._element.href + .replace(/^https:\/\/test\.cases\/path\//, "") + .replace(/^https:\/\/example\.com\/public\/path\//, "") + .replace(/^https:\/\/example\.com\//, "") + ); + let css = fs.readFileSync(filepath, "utf-8"); css = css.replace(/@import url\("([^"]+)"\);/g, (match, url) => { if (!/^https:\/\/test\.cases\/path\//.test(url)) { return url;