diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3085788e..2455c276 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Change log
+## 3.12.0 (2024-05-19)
+
+- feat: add support for the `css-loader` option `exportType` as [css-style-sheet](https://github.com/webpack-contrib/css-loader?#exporttype)
+- test: add tests for import of CSS stylesheet
+- docs: update readme
+
## 3.11.0 (2024-04-23)
- feat: add `entryFilter` option to include or exclude entry files when the `entry` option is the path
diff --git a/README.md b/README.md
index a8caee0a..dc0486f8 100644
--- a/README.md
+++ b/README.md
@@ -529,6 +529,7 @@ See [boilerplate](https://github.com/webdiscus/webpack-html-scss-boilerplate)
- [How to resolve source assets in an attribute containing JSON value](#recipe-resolve-attr-json)
- [How to load CSS file dynamically](#recipe-dynamic-load-css) (lazy loading CSS)
- [How to import CSS class names in JS](#recipe-css-modules) (CSS modules)
+ - [How to import CSS stylesheet in JS](#recipe-css-style-sheet) ([CSSStyleSheet](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet))
- [How to load JS and CSS from `node_modules` in template](#recipe-load-js-css-from-node-modules)
- [How to import CSS or SCSS from `node_modules` in SCSS](#recipe-import-style-from-node-modules)
- [How to process a PHP template](#recipe-preprocessor-php)
@@ -5450,9 +5451,98 @@ The imported `styles` object contains generated class names like followings:
}
```
-
Read more information about [CSS Modules](https://github.com/css-modules/css-modules).
+---
+
+#### [↑ back to contents](#contents)
+
+
+
+## How to import CSS stylesheet in JS
+
+Using the `css-loader` option [exportType](https://github.com/webpack-contrib/css-loader?#exporttype) as `css-style-sheet`
+you can import the CSS stylesheets as the instance of the [CSSStyleSheet](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet) object.
+
+Import a CSS module script and apply it to a document or a shadow root like this:
+
+```js
+import sheet from './style.scss?sheet';
+
+document.adoptedStyleSheets = [sheet];
+shadowRoot.adoptedStyleSheets = [sheet];
+```
+
+You can use the `?sheet` URL query to import a style file as stylesheets.
+The query must be configured in the webpack config:
+
+```js
+module.exports = {
+ plugins: [
+ new HtmlBundlerPlugin({
+ entry: {
+ index: './src/index.html',
+ },
+ js: {
+ filename: '[name].[contenthash:8].js',
+ },
+ css: {
+ filename: '[name].[contenthash:8].css',
+ },
+ }),
+ ],
+ module: {
+ rules: [
+ {
+ test: /\.(s?css)$/,
+ oneOf: [
+ // Import CSS/SCSS source file as a CSSStyleSheet object
+ {
+ resourceQuery: /sheet/, // <= the query, e.g. style.scss?sheet
+ use: [
+ {
+ loader: 'css-loader',
+ options: {
+ exportType: 'css-style-sheet', // <= define this option
+ },
+ },
+ {
+ loader: 'sass-loader',
+ },
+ ],
+ },
+ // Import CSS/SCSS source file as a CSS string
+ {
+ use: [
+ 'css-loader',
+ 'sass-loader',
+ ],
+ }
+ ],
+ }
+ ],
+ },
+};
+```
+
+Using the universal configuration above you can apply CSS stylesheets in JS and extract CSS into separate file or inject CSS into HTML:
+
+```js
+import sheet from './style.scss?sheet'; // import as CSSStyleSheet object
+import './style2.scss?inline'; // the extracted CSS will be injected into HTML
+import './style3.scss'; // the extracted CSS will be saved into separate output file
+
+// apply stylesheet to document and shadow root
+document.adoptedStyleSheets = [sheet];
+shadowRoot.adoptedStyleSheets = [sheet];
+```
+
+This is useful for [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) and shadow DOM.
+
+More information:
+
+- [Using CSS Module Scripts to import stylesheets](https://web.dev/css-module-scripts/)
+- [Constructable Stylesheets: seamless reusable styles](https://developers.google.com/web/updates/2019/02/constructable-stylesheets)
---
@@ -6067,7 +6157,7 @@ dist/js/app-5fa74877.1aceb2db.js
Using the bundler plugin, all your style source files should be specified directly in the template.
You can import style files in JavaScript, like it works using the `mini-css-extract-plugin` and `html-webpack-plugin`,
-but it is a **dirty hack**, **bad practice**, processing is **slow**, avoid it if possible.
+but it is a **bad practice** and processing is **slower**.
You can separate the styles into multiple bundles yourself.
diff --git a/package-lock.json b/package-lock.json
index 209abdeb..1ca29439 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "html-bundler-webpack-plugin",
- "version": "3.10.0",
+ "version": "3.11.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "html-bundler-webpack-plugin",
- "version": "3.10.0",
+ "version": "3.11.0",
"license": "ISC",
"dependencies": {
"@types/html-minifier-terser": "^7.0.2",
@@ -40,6 +40,7 @@
"jest": "^29.7.0",
"liquidjs": "^10.10.0",
"markdown-it": "^14.0.0",
+ "mini-css-extract-plugin": "^2.9.0",
"mustache": "^4.2.0",
"normalize.css": "^8.0.1",
"nunjucks": "^3.2.4",
@@ -9907,6 +9908,26 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/mini-css-extract-plugin": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz",
+ "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==",
+ "dev": true,
+ "dependencies": {
+ "schema-utils": "^4.0.0",
+ "tapable": "^2.2.1"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
"node_modules/minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -16668,6 +16689,7 @@
"jest": "^29.7.0",
"liquidjs": "^10.10.0",
"markdown-it": "^14.0.0",
+ "mini-css-extract-plugin": "^2.9.0",
"mustache": "^4.2.0",
"normalize.css": "^8.0.1",
"nunjucks": "^3.2.4",
@@ -23921,6 +23943,16 @@
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true
},
+ "mini-css-extract-plugin": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz",
+ "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==",
+ "dev": true,
+ "requires": {
+ "schema-utils": "^4.0.0",
+ "tapable": "^2.2.1"
+ }
+ },
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
@@ -31954,6 +31986,16 @@
"integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
"dev": true
},
+ "mini-css-extract-plugin": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz",
+ "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==",
+ "dev": true,
+ "requires": {
+ "schema-utils": "^4.0.0",
+ "tapable": "^2.2.1"
+ }
+ },
"minimalistic-assert": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
diff --git a/package.json b/package.json
index bc8e3c06..54bf1cd8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "html-bundler-webpack-plugin",
- "version": "3.11.0",
+ "version": "3.12.0",
"description": "HTML bundler plugin for webpack handles a template as an entry point, extracts CSS and JS from their sources referenced in HTML, supports template engines like Eta, EJS, Handlebars, Nunjucks.",
"keywords": [
"html",
@@ -20,10 +20,12 @@
"twig",
"twigjs",
"integrity",
- "style",
+ "js",
"javascript",
"css",
- "scss"
+ "scss",
+ "style",
+ "stylesheet"
],
"license": "ISC",
"author": "webdiscus (https://github.com/webdiscus)",
diff --git a/src/Plugin/AssetCompiler.js b/src/Plugin/AssetCompiler.js
index 216af344..59c871d9 100644
--- a/src/Plugin/AssetCompiler.js
+++ b/src/Plugin/AssetCompiler.js
@@ -580,9 +580,10 @@ class AssetCompiler {
// the filename with an extension is available only after resolve
meta.isStyle = Option.isStyle(file);
+ meta.isCSSStyleSheet = this.isCSSStyleSheet(createData);
// skip: module loaded via importModule, css url, data-URL
- if (meta.isLoaderImport || meta.isDependencyUrl || request.startsWith('data:')) return;
+ if (meta.isLoaderImport || meta.isCSSStyleSheet || meta.isDependencyUrl || request.startsWith('data:')) return;
if (issuer) {
const isIssuerStyle = Option.isStyle(issuer);
@@ -642,6 +643,21 @@ class AssetCompiler {
meta.isScript = Collection.hasScript(request);
}
+ /**
+ * Whether the module is imported CSSStyleSheet in JS.
+ *
+ * @param {{}} module
+ * @return {boolean}
+ */
+ isCSSStyleSheet(module) {
+ return (
+ Array.isArray(module.loaders) &&
+ module?.loaders.some(
+ (loader) => loader.loader.includes('css-loader') && loader.options?.exportType === 'css-style-sheet'
+ )
+ );
+ }
+
/**
* Returns unique style loaders only.
*
@@ -812,12 +828,13 @@ class AssetCompiler {
for (const module of chunkModules) {
const { buildInfo, resource, resourceResolveData } = module;
- const { isScript, isImportedStyle } = resourceResolveData?._bundlerPluginMeta || {};
+ const { isScript, isImportedStyle, isCSSStyleSheet } = resourceResolveData?._bundlerPluginMeta || {};
let moduleType = module.type;
if (
isScript ||
isImportedStyle ||
+ isCSSStyleSheet ||
!resource ||
!resourceResolveData?.context ||
AssetInline.isDataUrl(resource)
@@ -959,7 +976,10 @@ class AssetCompiler {
// extract CSS
const cssOptions = Option.getStyleOptions(sourceFile);
- if (cssOptions == null) return;
+ if (cssOptions == null) {
+ // ignore file if css option is disabled
+ return;
+ }
const inline = Collection.isInlineStyle(resource);
const { name } = path.parse(sourceFile);
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/index.html b/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/index.html
new file mode 100644
index 00000000..319b1610
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/index.html
@@ -0,0 +1,20 @@
+
+
+
+ Test
+
+
+
+
+
+
+ Hello World!
+ Red
+ Green
+ Blue
+
+
\ No newline at end of file
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/main.cc0cc899.js b/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/main.cc0cc899.js
new file mode 100644
index 00000000..b220becd
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/main.cc0cc899.js
@@ -0,0 +1 @@
+(()=>{"use strict";var t,e={77:(t,e,n)=>{n.d(e,{Z:()=>l});var r=n(558),o=n.n(r),c=n(361),a=n.n(c)()(o());a.push([t.id,".lime-green {\n border: 5px solid limegreen;\n color: limegreen;\n}\n",""]);var i=new CSSStyleSheet;i.replaceSync(a.toString());const l=i},361:t=>{t.exports=function(t){var e=[];return e.toString=function(){return this.map((function(e){var n="",r=void 0!==e[5];return e[4]&&(n+="@supports (".concat(e[4],") {")),e[2]&&(n+="@media ".concat(e[2]," {")),r&&(n+="@layer".concat(e[5].length>0?" ".concat(e[5]):""," {")),n+=t(e),r&&(n+="}"),e[2]&&(n+="}"),e[4]&&(n+="}"),n})).join("")},e.i=function(t,n,r,o,c){"string"==typeof t&&(t=[[null,t,void 0]]);var a={};if(r)for(var i=0;i0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=c),n&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=n):u[2]=n),o&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=o):u[4]="".concat(o)),e.push(u))}},e}},558:t=>{t.exports=function(t){return t[1]}}},n={};function r(t){var o=n[t];if(void 0!==o)return o.exports;var c=n[t]={id:t,exports:{}};return e[t](c,c.exports,r),c.exports}r.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return r.d(e,{a:e}),e},r.d=(t,e)=>{for(var n in e)r.o(e,n)&&!r.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),t=r(77),console.log("sheet: ",t.Z),document.adoptedStyleSheets=[t.Z]})();
\ No newline at end of file
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/main.dc4ea4af.css b/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/main.dc4ea4af.css
new file mode 100644
index 00000000..e5bce632
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/main.dc4ea4af.css
@@ -0,0 +1,4 @@
+.royal-blue {
+ border: 5px solid royalblue;
+ color: royalblue;
+}
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/style-a.22451d9b.css b/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/style-a.22451d9b.css
new file mode 100644
index 00000000..11bead78
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/expected/style-a.22451d9b.css
@@ -0,0 +1,4 @@
+h1 {
+ color: coral;
+ font-size: 20px;
+}
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/index.html b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/index.html
new file mode 100644
index 00000000..421c42b7
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/index.html
@@ -0,0 +1,15 @@
+
+
+
+ Test
+
+
+
+
+
+ Hello World!
+ Red
+ Green
+ Blue
+
+
\ No newline at end of file
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/main.js b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/main.js
new file mode 100644
index 00000000..5fc45fe3
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/main.js
@@ -0,0 +1,7 @@
+import sheet from './style-c.css?sheet'; // `css-loader` option `exportType: 'css-style-sheet'`
+import './style-d.css'; // `css-loader` option `exportType: 'array'`
+
+console.log('sheet: ', sheet);
+
+document.adoptedStyleSheets = [sheet];
+//shadowRoot.adoptedStyleSheets = [sheet]; // error in browser: shadowRoot is not defined
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-a.css b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-a.css
new file mode 100644
index 00000000..11bead78
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-a.css
@@ -0,0 +1,4 @@
+h1 {
+ color: coral;
+ font-size: 20px;
+}
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-b.css b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-b.css
new file mode 100644
index 00000000..89cb7e28
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-b.css
@@ -0,0 +1,4 @@
+.red {
+ border: 5px solid red;
+ color: indianred;
+}
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-c.css b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-c.css
new file mode 100644
index 00000000..6b8631dc
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-c.css
@@ -0,0 +1,4 @@
+.lime-green {
+ border: 5px solid limegreen;
+ color: limegreen;
+}
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-d.css b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-d.css
new file mode 100644
index 00000000..e5bce632
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/src/style-d.css
@@ -0,0 +1,4 @@
+.royal-blue {
+ border: 5px solid royalblue;
+ color: royalblue;
+}
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet-mix/webpack.config.js b/test/cases/js-import-css-modules-type-css-style-sheet-mix/webpack.config.js
new file mode 100644
index 00000000..47037693
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet-mix/webpack.config.js
@@ -0,0 +1,59 @@
+const path = require('path');
+const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');
+
+module.exports = {
+ mode: 'production',
+
+ output: {
+ path: path.join(__dirname, 'dist/'),
+ },
+
+ plugins: [
+ new HtmlBundlerPlugin({
+ entry: {
+ index: './src/index.html',
+ },
+ js: {
+ filename: '[name].[contenthash:8].js',
+ },
+ css: {
+ filename: '[name].[contenthash:8].css',
+ },
+ }),
+ ],
+
+ module: {
+ rules: [
+ {
+ test: /\.(css)$/,
+ oneOf: [
+ // extract CSS as the CSSStyleSheet object to apply it to the browser document at runtime in JS
+ {
+ resourceQuery: /sheet/, // style.css?sheet
+ use: [
+ {
+ loader: 'css-loader',
+ options: {
+ esModule: true, // must be enabled for using CSS module and `css-style-sheet`
+ // https://github.com/webpack-contrib/css-loader?#exporttype
+ exportType: 'css-style-sheet',
+ },
+ },
+ ],
+ },
+ // extract CSS as string to inline into HTML or save into separate file
+ {
+ use: [
+ {
+ loader: 'css-loader',
+ options: {
+ exportType: 'array', // default type
+ },
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+};
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet/expected/index.html b/test/cases/js-import-css-modules-type-css-style-sheet/expected/index.html
new file mode 100644
index 00000000..c79194aa
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet/expected/index.html
@@ -0,0 +1,13 @@
+
+
+
+ Test
+
+
+
+ Hello World!
+ Red
+ Green
+ Blue
+
+
\ No newline at end of file
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet/expected/js/main.5283af7c.js b/test/cases/js-import-css-modules-type-css-style-sheet/expected/js/main.5283af7c.js
new file mode 100644
index 00000000..00873b95
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet/expected/js/main.5283af7c.js
@@ -0,0 +1 @@
+(()=>{"use strict";var n,o={367:(n,o,r)=>{r.d(o,{Z:()=>l});var e=r(558),t=r.n(e),c=r(361),a=r.n(c)()(t());a.push([n.id,"h1 {\n color: coral;\n font-size: 20px;\n}\n\n.red {\n border: 5px solid red;\n color: indianred;\n}\n\n.lime-green {\n border: 5px solid limegreen;\n color: limegreen;\n}\n\n.royal-blue {\n border: 5px solid royalblue;\n color: royalblue;\n}",""]);var i=new CSSStyleSheet;i.replaceSync(a.toString());const l=i},361:n=>{n.exports=function(n){var o=[];return o.toString=function(){return this.map((function(o){var r="",e=void 0!==o[5];return o[4]&&(r+="@supports (".concat(o[4],") {")),o[2]&&(r+="@media ".concat(o[2]," {")),e&&(r+="@layer".concat(o[5].length>0?" ".concat(o[5]):""," {")),r+=n(o),e&&(r+="}"),o[2]&&(r+="}"),o[4]&&(r+="}"),r})).join("")},o.i=function(n,r,e,t,c){"string"==typeof n&&(n=[[null,n,void 0]]);var a={};if(e)for(var i=0;i0?" ".concat(s[5]):""," {").concat(s[1],"}")),s[5]=c),r&&(s[2]?(s[1]="@media ".concat(s[2]," {").concat(s[1],"}"),s[2]=r):s[2]=r),t&&(s[4]?(s[1]="@supports (".concat(s[4],") {").concat(s[1],"}"),s[4]=t):s[4]="".concat(t)),o.push(s))}},o}},558:n=>{n.exports=function(n){return n[1]}}},r={};function e(n){var t=r[n];if(void 0!==t)return t.exports;var c=r[n]={id:n,exports:{}};return o[n](c,c.exports,e),c.exports}e.n=n=>{var o=n&&n.__esModule?()=>n.default:()=>n;return e.d(o,{a:o}),o},e.d=(n,o)=>{for(var r in o)e.o(o,r)&&!e.o(n,r)&&Object.defineProperty(n,r,{enumerable:!0,get:o[r]})},e.o=(n,o)=>Object.prototype.hasOwnProperty.call(n,o),n=e(367),console.log("sheet: ",n.Z),document.adoptedStyleSheets=[n.Z]})();
\ No newline at end of file
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet/src/index.html b/test/cases/js-import-css-modules-type-css-style-sheet/src/index.html
new file mode 100644
index 00000000..f2c0c5cc
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet/src/index.html
@@ -0,0 +1,13 @@
+
+
+
+ Test
+
+
+
+ Hello World!
+ Red
+ Green
+ Blue
+
+
\ No newline at end of file
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet/src/main.js b/test/cases/js-import-css-modules-type-css-style-sheet/src/main.js
new file mode 100644
index 00000000..877436f8
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet/src/main.js
@@ -0,0 +1,7 @@
+import sheet from './style.css'; // using the `css-loader` option `exportType: 'css-style-sheet'`
+//import sheet from './style.css' assert { type: 'css' }; // using the `css-loader` option `exportType: 'css-style-sheet'`
+
+console.log('sheet: ', sheet);
+
+document.adoptedStyleSheets = [sheet];
+//shadowRoot.adoptedStyleSheets = [sheet]; // error in browser: shadowRoot is not defined
\ No newline at end of file
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet/src/style.css b/test/cases/js-import-css-modules-type-css-style-sheet/src/style.css
new file mode 100644
index 00000000..961ac7da
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet/src/style.css
@@ -0,0 +1,19 @@
+h1 {
+ color: coral;
+ font-size: 20px;
+}
+
+.red {
+ border: 5px solid red;
+ color: indianred;
+}
+
+.lime-green {
+ border: 5px solid limegreen;
+ color: limegreen;
+}
+
+.royal-blue {
+ border: 5px solid royalblue;
+ color: royalblue;
+}
\ No newline at end of file
diff --git a/test/cases/js-import-css-modules-type-css-style-sheet/webpack.config.js b/test/cases/js-import-css-modules-type-css-style-sheet/webpack.config.js
new file mode 100644
index 00000000..347c902c
--- /dev/null
+++ b/test/cases/js-import-css-modules-type-css-style-sheet/webpack.config.js
@@ -0,0 +1,47 @@
+const path = require('path');
+const HtmlBundlerPlugin = require('@test/html-bundler-webpack-plugin');
+
+module.exports = {
+ mode: 'production',
+
+ output: {
+ path: path.join(__dirname, 'dist/'),
+ },
+
+ plugins: [
+ new HtmlBundlerPlugin({
+ entry: {
+ index: './src/index.html',
+ },
+ js: {
+ filename: 'js/[name].[contenthash:8].js',
+ },
+ css: {
+ //enabled: false, // must be false if used the `exportType: 'css-style-sheet'`
+ },
+ }),
+ ],
+
+ module: {
+ rules: [
+ {
+ test: /\.(css)$/,
+ use: [
+ {
+ loader: 'css-loader',
+ options: {
+ esModule: true, // must be enabled for using CSS module and `css-style-sheet`
+ // https://github.com/webpack-contrib/css-loader?#exporttype
+ exportType: 'css-style-sheet',
+ // see https://github.com/webpack-contrib/css-loader#modules
+ // modules: {
+ // localIdentName: '[name]__[local]--[hash:base64:5]',
+ // exportLocalsConvention: 'camelCase',
+ // },
+ },
+ },
+ ],
+ },
+ ],
+ },
+};
\ No newline at end of file
diff --git a/test/integration.test.js b/test/integration.test.js
index 77c6fe5c..d6204874 100644
--- a/test/integration.test.js
+++ b/test/integration.test.js
@@ -301,6 +301,8 @@ describe('import styles in JavaScript', () => {
test('import CSS Modules, CJS', () => compareFiles('js-import-css-modules-cjs'));
test('import CSS Modules, ESM', () => compareFiles('js-import-css-modules-esm'));
+ test('import CSS, css-style-sheet', () => compareFiles('js-import-css-modules-type-css-style-sheet'));
+ test('import CSS, css-style-sheet mix types', () => compareFiles('js-import-css-modules-type-css-style-sheet-mix'));
// dynamic import of the style in the dynamic imported js
test('dynamic import css in js', () => compareFiles('js-import-css-dynamic-import'));