├── .npmignore ├── .husky ├── pre-commit └── commit-msg ├── test ├── fixtures │ ├── style.css │ ├── style-other.css │ ├── create-nonce.js │ ├── nested │ │ └── style.css │ ├── other-nested │ │ └── style.css │ ├── singleton-one.css │ ├── singleton-two.css │ ├── css-modules-local-scoped.css │ ├── hot.js │ ├── multiple.js │ ├── nonce-import.js │ ├── simple.js │ ├── singleton.js │ ├── es-modules.js │ ├── lazy-es-modules.js │ ├── nonce-require.js │ ├── commonjs-modules.js │ ├── lazy-commonjs-modules.js │ ├── lazy-nonce-import.js │ ├── lazy-negative-refs.js │ ├── lazy-nonce-require.js │ ├── lazy-simple.js │ ├── insert-options.js │ ├── es-modules.css │ ├── lazy-multiple.js │ ├── insert-test-shadow.js │ ├── lazy-options.js │ ├── css-modules.css │ ├── lazy-options-use-unuse.js │ ├── element.js │ ├── named-export.css │ ├── styleTagTransformAdditionalStyles.js │ ├── styleTagTransform.js │ ├── lazy-element.js │ ├── css-modules-link.js │ ├── css-modules.js │ ├── named-export.js │ ├── lazy-css-modules.js │ ├── insert-top.js │ ├── insertFn.js │ └── insert-to-existing-style.js ├── manual │ ├── src │ │ ├── duplicate.css │ │ ├── order-1.css │ │ ├── order-2.css │ │ ├── use-unuse.lazy.css │ │ ├── toogle.lazy.css │ │ ├── modules │ │ │ ├── common.module.css │ │ │ ├── one.module.css │ │ │ ├── two.module.css │ │ │ ├── page.module.css │ │ │ └── toolbar.module.css │ │ ├── nested.css │ │ ├── logo.png │ │ ├── nested │ │ │ └── style.css │ │ ├── bottom.css │ │ ├── custom-square.custom.css │ │ ├── style.css │ │ ├── style.link.css │ │ ├── top.css │ │ ├── style.lazy.css │ │ ├── middle.css │ │ ├── style.named-export.module.css │ │ ├── other-style.scss │ │ ├── style.named-export.lazy.module.css │ │ ├── other-style.lazy.scss │ │ ├── component.module.css │ │ ├── component.lazy.module.css │ │ ├── order.css │ │ ├── custom-square.js │ │ └── index.js │ ├── index.html │ └── webpack.config.js ├── helpers │ ├── getErrors.js │ ├── getWarnings.js │ ├── compile.js │ ├── getEntryByInjectType.js │ ├── readAssets.js │ ├── normalizeErrors.js │ ├── execute.js │ ├── index.js │ ├── readAsset.js │ ├── runInJsDom.js │ └── getCompiler.js ├── cjs.test.js ├── base-option.test.js ├── __snapshots__ │ ├── base-option.test.js.snap │ ├── lazyStyleTag-options.test.js.snap │ ├── styleTagTransform-option.test.js.snap │ ├── validate-options.test.js.snap │ ├── injectType-option.test.js.snap │ └── attributes-option.test.js.snap ├── injectType-option.test.js ├── lazyStyleTag-options.test.js ├── runtime │ ├── isEqualLocals.test.js │ ├── __snapshots__ │ │ ├── injectStylesIntoLinkTag.test.js.snap │ │ └── injectStylesIntoStyleTag.test.js.snap │ └── injectStylesIntoLinkTag.test.js ├── validate-options.test.js ├── attributes-option.test.js ├── styleTagTransform-option.test.js ├── esModule-option.test.js ├── insert-option.test.js └── loader.test.js ├── .prettierignore ├── src ├── cjs.js ├── runtime │ ├── setAttributesWithAttributesAndNonce.js │ ├── insertStyleElement.js │ ├── setAttributesWithoutAttributes.js │ ├── styleTagTransform.js │ ├── setAttributesWithAttributes.js │ ├── isEqualLocals.js │ ├── isOldIE.js │ ├── injectStylesIntoLinkTag.js │ ├── insertBySelector.js │ ├── styleDomAPI.js │ ├── singletonStyleDomAPI.js │ └── injectStylesIntoStyleTag.js ├── options.json ├── index.js └── utils.js ├── .gitattributes ├── lint-staged.config.js ├── commitlint.config.js ├── eslint.config.mjs ├── .editorconfig ├── .gitignore ├── .github └── workflows │ ├── dependency-review.yml │ └── nodejs.yml ├── .cspell.json ├── babel.config.js ├── LICENSE └── package.json /.npmignore: -------------------------------------------------------------------------------- 1 | fixtures/ 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | lint-staged 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | commitlint --edit $1 2 | -------------------------------------------------------------------------------- /test/fixtures/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/style-other.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/create-nonce.js: -------------------------------------------------------------------------------- 1 | __webpack_nonce__ = '12345678'; 2 | -------------------------------------------------------------------------------- /test/fixtures/nested/style.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/other-nested/style.css: -------------------------------------------------------------------------------- 1 | .bar { 2 | color: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/singleton-one.css: -------------------------------------------------------------------------------- 1 | .class-one { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/singleton-two.css: -------------------------------------------------------------------------------- 1 | .class-two { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/duplicate.css: -------------------------------------------------------------------------------- 1 | .duplicate { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/order-1.css: -------------------------------------------------------------------------------- 1 | .order { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/order-2.css: -------------------------------------------------------------------------------- 1 | .order { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/use-unuse.lazy.css: -------------------------------------------------------------------------------- 1 | .use { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /dist 3 | /node_modules 4 | /test/fixtures 5 | CHANGELOG.md -------------------------------------------------------------------------------- /test/manual/src/toogle.lazy.css: -------------------------------------------------------------------------------- 1 | #toggle-section { 2 | background-color: red; 3 | } 4 | -------------------------------------------------------------------------------- /src/cjs.js: -------------------------------------------------------------------------------- 1 | const loader = require("./index"); 2 | 3 | module.exports = loader.default; 4 | -------------------------------------------------------------------------------- /test/manual/src/modules/common.module.css: -------------------------------------------------------------------------------- 1 | .common { 2 | background-color: yellow; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/nested.css: -------------------------------------------------------------------------------- 1 | @import "./nested/style.css"; 2 | @import "./duplicate.css"; 3 | -------------------------------------------------------------------------------- /test/fixtures/css-modules-local-scoped.css: -------------------------------------------------------------------------------- 1 | :local(.className) { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/hot.js: -------------------------------------------------------------------------------- 1 | import style from './style.css'; 2 | 3 | window.hotApi = module.hot; 4 | -------------------------------------------------------------------------------- /test/fixtures/multiple.js: -------------------------------------------------------------------------------- 1 | import './nested/style.css'; 2 | import './other-nested/style.css'; 3 | -------------------------------------------------------------------------------- /test/fixtures/nonce-import.js: -------------------------------------------------------------------------------- 1 | import './create-nonce'; 2 | import './style.css'; 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/fixtures/simple.js: -------------------------------------------------------------------------------- 1 | import a from './style.css'; 2 | import b from './style-other.css'; 3 | -------------------------------------------------------------------------------- /test/fixtures/singleton.js: -------------------------------------------------------------------------------- 1 | import './singleton-one.css'; 2 | import './singleton-two.css'; 3 | -------------------------------------------------------------------------------- /test/fixtures/es-modules.js: -------------------------------------------------------------------------------- 1 | import * as mod from './es-modules.css'; 2 | 3 | window.__cssLoader = mod; 4 | -------------------------------------------------------------------------------- /test/manual/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack/style-loader/HEAD/test/manual/src/logo.png -------------------------------------------------------------------------------- /test/fixtures/lazy-es-modules.js: -------------------------------------------------------------------------------- 1 | import * as mod from './es-modules.css'; 2 | 3 | window.__cssLoader = mod; 4 | -------------------------------------------------------------------------------- /test/fixtures/nonce-require.js: -------------------------------------------------------------------------------- 1 | __webpack_nonce__ = '12345678'; 2 | 3 | require('./style.css'); 4 | 5 | 6 | -------------------------------------------------------------------------------- /test/manual/src/nested/style.css: -------------------------------------------------------------------------------- 1 | @import "../duplicate.css"; 2 | 3 | .duplicate { 4 | color: blue; 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/commonjs-modules.js: -------------------------------------------------------------------------------- 1 | const styles = require('./es-modules.css'); 2 | 3 | window.__cssLoader = styles; 4 | -------------------------------------------------------------------------------- /test/manual/src/bottom.css: -------------------------------------------------------------------------------- 1 | .bottom { 2 | color: blue; 3 | } 4 | 5 | .media-and-supports { 6 | color: blue; 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtures/lazy-commonjs-modules.js: -------------------------------------------------------------------------------- 1 | const styles = require('./es-modules.css'); 2 | 3 | window.__cssLoader = styles; 4 | -------------------------------------------------------------------------------- /test/manual/src/custom-square.custom.css: -------------------------------------------------------------------------------- 1 | div { 2 | width: 50px; 3 | height: 50px; 4 | background-color: red; 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/lazy-nonce-import.js: -------------------------------------------------------------------------------- 1 | import './create-nonce'; 2 | import styles from './style.css'; 3 | 4 | styles.use(); 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/fixtures/lazy-negative-refs.js: -------------------------------------------------------------------------------- 1 | import api from './style.css'; 2 | 3 | // ref still 0 4 | api.unuse(); 5 | // ref 1 6 | api.use(); 7 | -------------------------------------------------------------------------------- /test/manual/src/modules/one.module.css: -------------------------------------------------------------------------------- 1 | .selector1 { 2 | composes: common from "./common.module.css"; 3 | background-color: red; 4 | } 5 | -------------------------------------------------------------------------------- /test/manual/src/modules/two.module.css: -------------------------------------------------------------------------------- 1 | .selector2 { 2 | composes: common from "./common.module.css"; 3 | background-color: navy; 4 | } 5 | -------------------------------------------------------------------------------- /test/manual/src/modules/page.module.css: -------------------------------------------------------------------------------- 1 | .page-btn { 2 | composes: selector1 from "./one.module.css"; 3 | 4 | background: crimson; 5 | } 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Treats the lock file as binary & prevents conflict hell 2 | yarn.lock -diff 3 | * text=auto 4 | package-lock.json -diff 5 | bin/* eol=lf -------------------------------------------------------------------------------- /test/fixtures/lazy-nonce-require.js: -------------------------------------------------------------------------------- 1 | __webpack_nonce__ = '12345678'; 2 | 3 | const styles = require('./style.css'); 4 | 5 | styles.use(); 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/manual/src/modules/toolbar.module.css: -------------------------------------------------------------------------------- 1 | @value common from './common.module.css'; 2 | 3 | .toolbar > .common { 4 | background: orange; 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/lazy-simple.js: -------------------------------------------------------------------------------- 1 | import style from './style.css'; 2 | import styleOther from './style-other.css'; 3 | 4 | style.use(); 5 | styleOther.use(); 6 | -------------------------------------------------------------------------------- /test/helpers/getErrors.js: -------------------------------------------------------------------------------- 1 | import normalizeErrors from "./normalizeErrors"; 2 | 3 | export default (stats) => normalizeErrors(stats.compilation.errors); 4 | -------------------------------------------------------------------------------- /test/manual/src/style.css: -------------------------------------------------------------------------------- 1 | .red { 2 | color: red; 3 | } 4 | 5 | .green { 6 | color: green; 7 | } 8 | 9 | .blue { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/insert-options.js: -------------------------------------------------------------------------------- 1 | function insert(styleTag, options) { 2 | options.insertInto.appendChild(styleTag); 3 | } 4 | 5 | module.exports = insert; 6 | -------------------------------------------------------------------------------- /test/helpers/getWarnings.js: -------------------------------------------------------------------------------- 1 | import normalizeErrors from "./normalizeErrors"; 2 | 3 | export default (stats) => normalizeErrors(stats.compilation.warnings); 4 | -------------------------------------------------------------------------------- /test/fixtures/es-modules.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: red; 3 | } 4 | 5 | .bar { 6 | color: blue; 7 | } 8 | 9 | .baz { 10 | color: coral; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/lazy-multiple.js: -------------------------------------------------------------------------------- 1 | import style from './nested/style.css'; 2 | import otherStyle from './other-nested/style.css'; 3 | 4 | style.use(); 5 | otherStyle.use(); 6 | -------------------------------------------------------------------------------- /test/fixtures/insert-test-shadow.js: -------------------------------------------------------------------------------- 1 | function insert(element) { 2 | document.querySelector("#test-shadow").appendChild(element); 3 | } 4 | 5 | 6 | module.exports = insert; 7 | -------------------------------------------------------------------------------- /test/manual/src/style.link.css: -------------------------------------------------------------------------------- 1 | .link-red { 2 | color: red; 3 | } 4 | 5 | .link-green { 6 | color: green; 7 | } 8 | 9 | .link-blue { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/lazy-options.js: -------------------------------------------------------------------------------- 1 | import style from './style.css'; 2 | 3 | style.use({ 4 | insertInto: document.body, 5 | additionalStyles: '.some-element {color: red;}' 6 | }); 7 | 8 | -------------------------------------------------------------------------------- /test/cjs.test.js: -------------------------------------------------------------------------------- 1 | import src from "../src"; 2 | import cjs from "../src/cjs"; 3 | 4 | describe("cjs", () => { 5 | it("should exported", () => { 6 | expect(cjs).toEqual(src); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "*": [ 3 | "prettier --cache --write --ignore-unknown", 4 | "cspell --cache --no-must-find-files", 5 | ], 6 | "*.js": ["eslint --cache --fix"], 7 | }; 8 | -------------------------------------------------------------------------------- /test/fixtures/css-modules.css: -------------------------------------------------------------------------------- 1 | :local(.myClassName) { 2 | background: red; 3 | } 4 | 5 | :local(.myComposingClass) { 6 | composes: className from './css-modules-local-scoped.css'; 7 | color: blue; 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/lazy-options-use-unuse.js: -------------------------------------------------------------------------------- 1 | import style from './style.css'; 2 | 3 | style.use({ 4 | insertInto: document.body, 5 | additionalStyles: '.some-element {color: red;}' 6 | }); 7 | 8 | style.unuse(); 9 | -------------------------------------------------------------------------------- /test/fixtures/element.js: -------------------------------------------------------------------------------- 1 | const element = document.createElement('div'); 2 | 3 | element.id = "test-shadow"; 4 | document.body.appendChild(element); 5 | 6 | require('./style.css'); 7 | require('./style-other.css'); 8 | -------------------------------------------------------------------------------- /test/fixtures/named-export.css: -------------------------------------------------------------------------------- 1 | :local(.myClassName) { 2 | background: red; 3 | } 4 | 5 | :local(.myComposingClass) { 6 | composes: className from './css-modules-local-scoped.css'; 7 | color: blue; 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/styleTagTransformAdditionalStyles.js: -------------------------------------------------------------------------------- 1 | function styleTagTransform(css, style, options) { 2 | style.innerHTML = `${css}\n${options.additionalStyles}\n`; 3 | } 4 | 5 | module.exports = styleTagTransform; 6 | -------------------------------------------------------------------------------- /test/manual/src/top.css: -------------------------------------------------------------------------------- 1 | @import url("./middle.css") supports(display: flex) screen and 2 | (min-width: 400px); 3 | 4 | .top { 5 | color: red; 6 | } 7 | 8 | .media-and-supports { 9 | color: red; 10 | } 11 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["@commitlint/config-conventional"], 3 | rules: { 4 | "header-max-length": [0], 5 | "body-max-line-length": [0], 6 | "footer-max-line-length": [0], 7 | }, 8 | }; 9 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "eslint/config"; 2 | import configs from "eslint-config-webpack/configs.js"; 3 | 4 | export default defineConfig([ 5 | { 6 | extends: [configs["recommended-dirty"]], 7 | }, 8 | ]); 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /test/fixtures/styleTagTransform.js: -------------------------------------------------------------------------------- 1 | function styleTagTransform(css, style) { 2 | // eslint-disable-next-line no-param-reassign 3 | style.innerHTML = `${css}.modify{}\n`; 4 | 5 | document.head.appendChild(style); 6 | } 7 | 8 | module.exports = styleTagTransform; 9 | -------------------------------------------------------------------------------- /test/helpers/compile.js: -------------------------------------------------------------------------------- 1 | export default (compiler) => 2 | new Promise((resolve, reject) => { 3 | compiler.run((error, stats) => { 4 | if (error) { 5 | return reject(error); 6 | } 7 | 8 | return resolve(stats); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /test/helpers/getEntryByInjectType.js: -------------------------------------------------------------------------------- 1 | function getEntryByInjectType(testId, injectType) { 2 | const isLazy = injectType && injectType.toLowerCase().includes("lazy"); 3 | 4 | return `./${isLazy ? "lazy-" : ""}${testId}`; 5 | } 6 | 7 | export default getEntryByInjectType; 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | .idea/ 3 | /coverage 4 | logs 5 | *.log 6 | npm-debug.log* 7 | .eslintcache 8 | .cspellcache 9 | /dist 10 | /local 11 | /reports 12 | .DS_Store 13 | Thumbs.db 14 | .idea 15 | *.iml 16 | .vscode 17 | *.sublime-project 18 | *.sublime-workspace 19 | -------------------------------------------------------------------------------- /test/manual/src/style.lazy.css: -------------------------------------------------------------------------------- 1 | .lazy-red { 2 | color: red; 3 | } 4 | 5 | .lazy-green { 6 | color: green; 7 | } 8 | 9 | .lazy-blue { 10 | color: blue; 11 | } 12 | 13 | .lazy-background { 14 | height: 1200px; 15 | background: url("./logo.png") center no-repeat; 16 | } 17 | -------------------------------------------------------------------------------- /test/manual/src/middle.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Open+Sans&display=swap"); 2 | @import url("./bottom.css") supports(display: grid) screen and 3 | (max-width: 2400px); 4 | 5 | .middle { 6 | color: green; 7 | } 8 | 9 | .media-and-supports { 10 | color: green; 11 | } 12 | -------------------------------------------------------------------------------- /test/fixtures/lazy-element.js: -------------------------------------------------------------------------------- 1 | const element = document.createElement('div'); 2 | 3 | element.id = "test-shadow"; 4 | document.body.appendChild(element); 5 | 6 | const styles = require('./style.css').default; 7 | const stylesOther = require('./style-other.css').default; 8 | 9 | styles.use(); 10 | stylesOther.use(); 11 | -------------------------------------------------------------------------------- /src/runtime/setAttributesWithAttributesAndNonce.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | function setAttributesWithoutAttributes(styleElement, attributes) { 3 | for (const key of Object.keys(attributes)) { 4 | styleElement.setAttribute(key, attributes[key]); 5 | } 6 | } 7 | 8 | module.exports = setAttributesWithoutAttributes; 9 | -------------------------------------------------------------------------------- /test/helpers/readAssets.js: -------------------------------------------------------------------------------- 1 | import readAsset from "./readAsset"; 2 | 3 | export default function readAssets(compiler, stats) { 4 | const assets = {}; 5 | 6 | for (const asset of Object.keys(stats.compilation.assets)) { 7 | assets[asset] = readAsset(asset, compiler, stats); 8 | } 9 | 10 | return assets; 11 | } 12 | -------------------------------------------------------------------------------- /test/manual/src/style.named-export.module.css: -------------------------------------------------------------------------------- 1 | .named-export-red { 2 | color: red; 3 | } 4 | 5 | .named-export-green { 6 | color: green; 7 | } 8 | 9 | .named-export-blue { 10 | color: blue; 11 | } 12 | 13 | .named-export-background { 14 | height: 1200px; 15 | background: url("./logo.png") center no-repeat; 16 | } 17 | -------------------------------------------------------------------------------- /test/manual/src/other-style.scss: -------------------------------------------------------------------------------- 1 | $red: red; 2 | $green: green; 3 | $blue: blue; 4 | 5 | .sass-red { 6 | color: $red; 7 | } 8 | 9 | .sass-green { 10 | color: $green; 11 | } 12 | 13 | .sass-blue { 14 | color: $blue; 15 | } 16 | 17 | .sass-background { 18 | height: 1200px; 19 | background: url("./logo.png") center no-repeat; 20 | } 21 | -------------------------------------------------------------------------------- /test/manual/src/style.named-export.lazy.module.css: -------------------------------------------------------------------------------- 1 | .named-export-lazy-red { 2 | color: red; 3 | } 4 | 5 | .named-export-lazy-green { 6 | color: green; 7 | } 8 | 9 | .named-export-lazy-blue { 10 | color: blue; 11 | } 12 | 13 | .named-export-lazy-background { 14 | height: 1200px; 15 | background: url("./logo.png") center no-repeat; 16 | } 17 | -------------------------------------------------------------------------------- /test/manual/src/other-style.lazy.scss: -------------------------------------------------------------------------------- 1 | $red: red; 2 | $green: green; 3 | $blue: blue; 4 | 5 | .sass-lazy-red { 6 | color: $red; 7 | } 8 | 9 | .sass-lazy-green { 10 | color: $green; 11 | } 12 | 13 | .sass-lazy-blue { 14 | color: $blue; 15 | } 16 | 17 | .sass-lazy-background { 18 | height: 1200px; 19 | background: url("./logo.png") center no-repeat; 20 | } 21 | -------------------------------------------------------------------------------- /src/runtime/insertStyleElement.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | /* istanbul ignore next */ 3 | function insertStyleElement(options) { 4 | const element = document.createElement("style"); 5 | 6 | options.setAttributes(element, options.attributes); 7 | options.insert(element, options.options); 8 | 9 | return element; 10 | } 11 | 12 | module.exports = insertStyleElement; 13 | -------------------------------------------------------------------------------- /test/manual/src/component.module.css: -------------------------------------------------------------------------------- 1 | @value v-red: red; 2 | @value v-green: green; 3 | @value v-blue: blue; 4 | 5 | .module-red { 6 | color: v-red; 7 | } 8 | 9 | .module-green { 10 | color: v-green; 11 | } 12 | 13 | .module-blue { 14 | color: v-blue; 15 | } 16 | 17 | .module-background { 18 | height: 1200px; 19 | background: url("./logo.png") center no-repeat; 20 | } 21 | -------------------------------------------------------------------------------- /test/manual/src/component.lazy.module.css: -------------------------------------------------------------------------------- 1 | @value v-red: red; 2 | @value v-green: green; 3 | @value v-blue: blue; 4 | 5 | .module-red { 6 | color: v-red; 7 | } 8 | 9 | .module-green { 10 | color: v-green; 11 | } 12 | 13 | .module-blue { 14 | color: v-blue; 15 | } 16 | 17 | .module-background { 18 | height: 1200px; 19 | background: url("./logo.png") center no-repeat; 20 | } 21 | -------------------------------------------------------------------------------- /.github/workflows/dependency-review.yml: -------------------------------------------------------------------------------- 1 | name: "Dependency Review" 2 | on: [pull_request] 3 | 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | dependency-review: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: "Checkout Repository" 12 | uses: actions/checkout@v5 13 | - name: "Dependency Review" 14 | uses: actions/dependency-review-action@v4 15 | -------------------------------------------------------------------------------- /src/runtime/setAttributesWithoutAttributes.js: -------------------------------------------------------------------------------- 1 | /* global __webpack_nonce__ */ 2 | /* istanbul ignore next */ 3 | function setAttributesWithoutAttributes(styleElement) { 4 | const nonce = 5 | typeof __webpack_nonce__ !== "undefined" ? __webpack_nonce__ : null; 6 | 7 | if (nonce) { 8 | styleElement.setAttribute("nonce", nonce); 9 | } 10 | } 11 | 12 | module.exports = setAttributesWithoutAttributes; 13 | -------------------------------------------------------------------------------- /test/fixtures/css-modules-link.js: -------------------------------------------------------------------------------- 1 | import './css-modules.css'; 2 | 3 | const node1 = document.createElement("DIV"); 4 | const textNode1 = document.createTextNode("Water"); 5 | 6 | node1.appendChild(textNode1); 7 | 8 | document.body.appendChild(node1); 9 | 10 | const node2 = document.createElement("DIV"); 11 | const textNode2 = document.createTextNode("Ground"); 12 | 13 | node2.appendChild(textNode2); 14 | 15 | document.body.appendChild(node2); 16 | -------------------------------------------------------------------------------- /test/helpers/normalizeErrors.js: -------------------------------------------------------------------------------- 1 | function removeCWD(str) { 2 | const isWin = process.platform === "win32"; 3 | let cwd = process.cwd(); 4 | 5 | if (isWin) { 6 | str = str.replaceAll("\\", "/"); 7 | 8 | cwd = cwd.replaceAll("\\", "/"); 9 | } 10 | 11 | return str.replaceAll(new RegExp(cwd, "g"), ""); 12 | } 13 | 14 | export default (errors) => 15 | errors.map((error) => 16 | removeCWD(error.toString().split("\n").slice(0, 2).join("\n")), 17 | ); 18 | -------------------------------------------------------------------------------- /src/runtime/styleTagTransform.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | /* istanbul ignore next */ 3 | function styleTagTransform(css, styleElement) { 4 | if (styleElement.styleSheet) { 5 | styleElement.styleSheet.cssText = css; 6 | } else { 7 | while (styleElement.firstChild) { 8 | styleElement.removeChild(styleElement.firstChild); 9 | } 10 | 11 | styleElement.appendChild(document.createTextNode(css)); 12 | } 13 | } 14 | 15 | module.exports = styleTagTransform; 16 | -------------------------------------------------------------------------------- /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "language": "en,en-gb", 4 | "words": [ 5 | "commitlint", 6 | "unuse", 7 | "concat", 8 | "getdom", 9 | "Browserhacks", 10 | "toogle", 11 | "camelcase", 12 | "EAAE", 13 | "Unse", 14 | "memfs" 15 | ], 16 | 17 | "ignorePaths": [ 18 | "CHANGELOG.md", 19 | "package.json", 20 | "dist/**", 21 | "**/__snapshots__/**", 22 | "package-lock.json", 23 | "node_modules", 24 | "coverage", 25 | "*.log" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /src/runtime/setAttributesWithAttributes.js: -------------------------------------------------------------------------------- 1 | /* global __webpack_nonce__ */ 2 | /* istanbul ignore next */ 3 | function setAttributesWithoutAttributes(styleElement, attributes) { 4 | const nonce = 5 | typeof __webpack_nonce__ !== "undefined" ? __webpack_nonce__ : null; 6 | 7 | if (nonce) { 8 | attributes.nonce = nonce; 9 | } 10 | 11 | for (const key of Object.keys(attributes)) { 12 | styleElement.setAttribute(key, attributes[key]); 13 | } 14 | } 15 | 16 | module.exports = setAttributesWithoutAttributes; 17 | -------------------------------------------------------------------------------- /test/fixtures/css-modules.js: -------------------------------------------------------------------------------- 1 | import * as styles from './css-modules.css'; 2 | 3 | const node1 = document.createElement("DIV"); 4 | const textNode1 = document.createTextNode("Water"); 5 | 6 | node1.appendChild(textNode1); 7 | node1.className = styles.myClassName; 8 | 9 | document.body.appendChild(node1); 10 | 11 | const node2 = document.createElement("DIV"); 12 | const textNode2 = document.createTextNode("Ground"); 13 | 14 | node2.appendChild(textNode2); 15 | node2.className = styles.myComposingClass; 16 | 17 | document.body.appendChild(node2); 18 | -------------------------------------------------------------------------------- /test/fixtures/named-export.js: -------------------------------------------------------------------------------- 1 | import { myClassName, myComposingClass } from './named-export.css'; 2 | 3 | const node1 = document.createElement("DIV"); 4 | const textNode1 = document.createTextNode("Water"); 5 | 6 | node1.appendChild(textNode1); 7 | node1.className = myClassName; 8 | 9 | document.body.appendChild(node1); 10 | 11 | const node2 = document.createElement("DIV"); 12 | const textNode2 = document.createTextNode("Ground"); 13 | 14 | node2.appendChild(textNode2); 15 | node2.className = myComposingClass; 16 | 17 | document.body.appendChild(node2); 18 | -------------------------------------------------------------------------------- /test/helpers/execute.js: -------------------------------------------------------------------------------- 1 | import Module from "node:module"; 2 | import path from "node:path"; 3 | 4 | const parentModule = module; 5 | 6 | export default (code) => { 7 | const resource = "test.js"; 8 | const module = new Module(resource, parentModule); 9 | 10 | module.paths = Module._nodeModulePaths( 11 | path.resolve(__dirname, "../fixtures"), 12 | ); 13 | module.filename = resource; 14 | 15 | module._compile( 16 | `let __export__;${code};module.exports = __export__;`, 17 | resource, 18 | ); 19 | 20 | return module.exports; 21 | }; 22 | -------------------------------------------------------------------------------- /test/manual/src/order.css: -------------------------------------------------------------------------------- 1 | @import url("./order-1.css"); 2 | @import url("https://fonts.googleapis.com/css?family=Roboto&display=swap"); 3 | @import url("./order-2.css"); 4 | @import url("https://fonts.googleapis.com/css?family=Roboto&display=swap"); 5 | @import url("./order-1.css"); 6 | @import url("https://fonts.googleapis.com/css?family=Roboto&display=swap"); 7 | @import url("./order-2.css") screen and (min-width: 2000px); 8 | @import url("https://fonts.googleapis.com/css?family=Roboto&display=swap"); 9 | 10 | .order { 11 | width: 100%; 12 | padding: 10px; 13 | } 14 | -------------------------------------------------------------------------------- /test/fixtures/lazy-css-modules.js: -------------------------------------------------------------------------------- 1 | import styles, { myClassName, myComposingClass } from './css-modules.css'; 2 | 3 | styles.use(); 4 | 5 | const node1 = document.createElement("DIV"); 6 | const textNode1 = document.createTextNode("Water"); 7 | 8 | node1.appendChild(textNode1); 9 | node1.className = myClassName; 10 | 11 | document.body.appendChild(node1); 12 | 13 | const node2 = document.createElement("DIV"); 14 | const textNode2 = document.createTextNode("Ground"); 15 | 16 | node2.appendChild(textNode2); 17 | node2.className = myComposingClass; 18 | 19 | document.body.appendChild(node2); 20 | -------------------------------------------------------------------------------- /test/helpers/index.js: -------------------------------------------------------------------------------- 1 | export { default as compile } from "./compile"; 2 | export { default as getCompiler } from "./getCompiler"; 3 | export { default as execute } from "./execute"; 4 | export { default as getErrors } from "./getErrors"; 5 | export { default as getEntryByInjectType } from "./getEntryByInjectType"; 6 | export { default as normalizeErrors } from "./normalizeErrors"; 7 | export { default as getWarnings } from "./getWarnings"; 8 | export { default as readsAssets } from "./readAssets"; 9 | export { default as readAsset } from "./readAsset"; 10 | export { default as runInJsDom } from "./runInJsDom"; 11 | -------------------------------------------------------------------------------- /test/helpers/readAsset.js: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | 3 | export default (asset, compiler, stats) => { 4 | const usedFs = compiler.outputFileSystem; 5 | const outputPath = stats.compilation.outputOptions.path; 6 | 7 | let data = ""; 8 | let targetFile = asset; 9 | 10 | const queryStringIdx = targetFile.indexOf("?"); 11 | 12 | if (queryStringIdx >= 0) { 13 | targetFile = targetFile.slice(0, queryStringIdx); 14 | } 15 | 16 | try { 17 | data = usedFs.readFileSync(path.join(outputPath, targetFile)).toString(); 18 | } catch { 19 | data = "Error reading file"; 20 | } 21 | 22 | return data; 23 | }; 24 | -------------------------------------------------------------------------------- /src/runtime/isEqualLocals.js: -------------------------------------------------------------------------------- 1 | function isEqualLocals(a, b, isNamedExport) { 2 | if ((!a && b) || (a && !b)) { 3 | return false; 4 | } 5 | 6 | let property; 7 | 8 | for (property in a) { 9 | if (isNamedExport && property === "default") { 10 | continue; 11 | } 12 | 13 | if (a[property] !== b[property]) { 14 | return false; 15 | } 16 | } 17 | 18 | for (property in b) { 19 | if (isNamedExport && property === "default") { 20 | continue; 21 | } 22 | 23 | if (!a[property]) { 24 | return false; 25 | } 26 | } 27 | 28 | return true; 29 | } 30 | 31 | module.exports = isEqualLocals; 32 | -------------------------------------------------------------------------------- /test/base-option.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | compile, 3 | getCompiler, 4 | getErrors, 5 | getWarnings, 6 | runInJsDom, 7 | } from "./helpers/index"; 8 | 9 | describe('"base" option', () => { 10 | it("should work", async () => { 11 | expect.assertions(3); 12 | 13 | const compiler = getCompiler("./simple.js", { base: 1000 }); 14 | const stats = await compile(compiler); 15 | 16 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 17 | expect(dom.serialize()).toMatchSnapshot("DOM"); 18 | }); 19 | 20 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 21 | expect(getErrors(stats)).toMatchSnapshot("errors"); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /test/__snapshots__/base-option.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`"base" option should work: DOM 1`] = ` 4 | " 5 | style-loader test 6 | 7 | 14 | 15 |

Body

16 |
17 | 18 | 19 | 20 | " 21 | `; 22 | 23 | exports[`"base" option should work: errors 1`] = `[]`; 24 | 25 | exports[`"base" option should work: warnings 1`] = `[]`; 26 | -------------------------------------------------------------------------------- /test/fixtures/insert-top.js: -------------------------------------------------------------------------------- 1 | function insert(element) { 2 | const parent = document.querySelector("head"); 3 | const lastInsertedElement = 4 | // eslint-disable-next-line no-underscore-dangle 5 | window._lastElementInsertedByStyleLoader; 6 | 7 | if (!lastInsertedElement) { 8 | parent.insertBefore(element, parent.firstChild); 9 | } else if (lastInsertedElement.nextSibling) { 10 | parent.insertBefore(element, lastInsertedElement.nextSibling); 11 | } else { 12 | parent.appendChild(element); 13 | } 14 | 15 | // eslint-disable-next-line no-underscore-dangle 16 | window._lastElementInsertedByStyleLoader = element; 17 | } 18 | 19 | module.exports = insert; 20 | -------------------------------------------------------------------------------- /test/fixtures/insertFn.js: -------------------------------------------------------------------------------- 1 | function insert (element) { 2 | const parent = document.querySelector("head"); 3 | const lastInsertedElement = 4 | // eslint-disable-next-line no-underscore-dangle 5 | window._lastElementInsertedByStyleLoader; 6 | 7 | if (!lastInsertedElement) { 8 | parent.insertBefore(element, parent.firstChild); 9 | } else if (lastInsertedElement.nextSibling) { 10 | parent.insertBefore(element, lastInsertedElement.nextSibling); 11 | } else { 12 | parent.appendChild(element); 13 | } 14 | 15 | // eslint-disable-next-line no-underscore-dangle 16 | window._lastElementInsertedByStyleLoader = element; 17 | }; 18 | 19 | module.exports = insert; 20 | -------------------------------------------------------------------------------- /test/fixtures/insert-to-existing-style.js: -------------------------------------------------------------------------------- 1 | function insert(element) { 2 | const parent = document.querySelector("head"); 3 | const target = document.querySelector("#existing-style"); 4 | 5 | const lastInsertedElement = 6 | // eslint-disable-next-line no-underscore-dangle 7 | window._lastElementInsertedByStyleLoader; 8 | 9 | if (!lastInsertedElement) { 10 | parent.insertBefore(element, target); 11 | } else if (lastInsertedElement.nextSibling) { 12 | parent.insertBefore(element, lastInsertedElement.nextSibling); 13 | } else { 14 | parent.appendChild(element); 15 | } 16 | 17 | window._lastElementInsertedByStyleLoader = element; 18 | } 19 | 20 | module.exports = insert; 21 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const MIN_BABEL_VERSION = 7; 2 | 3 | module.exports = (api) => { 4 | api.assertVersion(MIN_BABEL_VERSION); 5 | api.cache(true); 6 | 7 | return { 8 | presets: [ 9 | [ 10 | "@babel/preset-env", 11 | { 12 | targets: { 13 | node: "18.12.0", 14 | }, 15 | }, 16 | ], 17 | ], 18 | overrides: [ 19 | { 20 | test: "./src/runtime", 21 | presets: [ 22 | [ 23 | "@babel/preset-env", 24 | { 25 | targets: { 26 | node: "0.12", 27 | }, 28 | }, 29 | ], 30 | ], 31 | }, 32 | ], 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /src/runtime/isOldIE.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/prefer-global-this */ 2 | /* eslint-disable no-undef */ 3 | /* global document */ 4 | let memo; 5 | 6 | /* istanbul ignore next */ 7 | function isOldIE() { 8 | if (typeof memo === "undefined") { 9 | // Test for IE <= 9 as proposed by Browserhacks 10 | // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805 11 | // Tests for existence of standard globals is to allow style-loader 12 | // to operate correctly into non-standard environments 13 | // @see https://github.com/webpack/style-loader/issues/177 14 | memo = Boolean( 15 | typeof window !== "undefined" && 16 | typeof document !== "undefined" && 17 | document.all && 18 | !window.atob, 19 | ); 20 | } 21 | 22 | return memo; 23 | } 24 | 25 | module.exports = isOldIE; 26 | -------------------------------------------------------------------------------- /test/helpers/runInJsDom.js: -------------------------------------------------------------------------------- 1 | import jsdom from "jsdom"; 2 | 3 | import { readAsset } from "./index"; 4 | 5 | function runInJsDom(assetName, compiler, stats, testFn) { 6 | const bundle = readAsset(assetName, compiler, stats); 7 | const virtualConsole = new jsdom.VirtualConsole(); 8 | 9 | virtualConsole.sendTo(console); 10 | 11 | const dom = new jsdom.JSDOM( 12 | ` 13 | 14 | 15 | style-loader test 16 | 17 | 18 | 19 |

Body

20 |
21 | 22 | 23 | 24 | `, 25 | { 26 | resources: "usable", 27 | runScripts: "dangerously", 28 | virtualConsole, 29 | }, 30 | ); 31 | 32 | dom.window.eval(bundle); 33 | 34 | testFn(dom, bundle); 35 | 36 | // free memory associated with the window 37 | dom.window.close(); 38 | } 39 | 40 | export default runInJsDom; 41 | -------------------------------------------------------------------------------- /src/runtime/injectStylesIntoLinkTag.js: -------------------------------------------------------------------------------- 1 | /* global document, __webpack_nonce__ */ 2 | module.exports = (url, options) => { 3 | if (typeof document === "undefined") { 4 | return () => {}; 5 | } 6 | 7 | options ||= {}; 8 | options.attributes = 9 | typeof options.attributes === "object" ? options.attributes : {}; 10 | 11 | if (typeof options.attributes.nonce === "undefined") { 12 | const nonce = 13 | typeof __webpack_nonce__ !== "undefined" ? __webpack_nonce__ : null; 14 | 15 | if (nonce) { 16 | options.attributes.nonce = nonce; 17 | } 18 | } 19 | 20 | const linkElement = document.createElement("link"); 21 | 22 | linkElement.rel = "stylesheet"; 23 | linkElement.href = url; 24 | 25 | for (const key of Object.keys(options.attributes)) { 26 | linkElement.setAttribute(key, options.attributes[key]); 27 | } 28 | 29 | options.insert(linkElement); 30 | 31 | return (newUrl) => { 32 | if (typeof newUrl === "string") { 33 | linkElement.href = newUrl; 34 | } else { 35 | linkElement.parentNode.removeChild(linkElement); 36 | } 37 | }; 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/manual/src/custom-square.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | /* eslint-disable */ 3 | 4 | import customSquareStyles from "./custom-square.custom.css"; 5 | 6 | class CustomSquare extends HTMLElement { 7 | // Specify observed attributes so that 8 | // attributeChangedCallback will work 9 | static get observedAttributes() { 10 | return ["w", "h"]; 11 | } 12 | 13 | constructor() { 14 | // Always call super first in constructor 15 | super(); 16 | 17 | this.attachShadow({ mode: "open" }); 18 | const divElement = document.createElement("div"); 19 | 20 | divElement.textContent = "Text content."; 21 | 22 | this.shadowRoot.appendChild(divElement); 23 | 24 | customSquareStyles.use({ target: this.shadowRoot }); 25 | 26 | const bgPurple = new CSSStyleSheet(); 27 | const width = this.getAttribute("w"); 28 | const height = this.getAttribute("h"); 29 | 30 | bgPurple.replace(`div { width: ${width}px; height: ${height}px; }`); 31 | 32 | this.shadowRoot.adoptedStyleSheets = [bgPurple]; 33 | } 34 | } 35 | 36 | customElements.define("custom-square", CustomSquare); 37 | 38 | export default CustomSquare; 39 | -------------------------------------------------------------------------------- /src/runtime/insertBySelector.js: -------------------------------------------------------------------------------- 1 | /* global document, window */ 2 | /* eslint-disable unicorn/prefer-global-this */ 3 | 4 | const memo = {}; 5 | 6 | /* istanbul ignore next */ 7 | function getTarget(target) { 8 | if (typeof memo[target] === "undefined") { 9 | let styleTarget = document.querySelector(target); 10 | 11 | // Special case to return head of iframe instead of iframe itself 12 | if ( 13 | window.HTMLIFrameElement && 14 | styleTarget instanceof window.HTMLIFrameElement 15 | ) { 16 | try { 17 | // This will throw an exception if access to iframe is blocked 18 | // due to cross-origin restrictions 19 | styleTarget = styleTarget.contentDocument.head; 20 | } catch { 21 | // istanbul ignore next 22 | styleTarget = null; 23 | } 24 | } 25 | 26 | memo[target] = styleTarget; 27 | } 28 | 29 | return memo[target]; 30 | } 31 | 32 | /* istanbul ignore next */ 33 | function insertBySelector(insert, style) { 34 | const target = getTarget(insert); 35 | 36 | if (!target) { 37 | throw new Error( 38 | "Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.", 39 | ); 40 | } 41 | 42 | target.appendChild(style); 43 | } 44 | 45 | module.exports = insertBySelector; 46 | -------------------------------------------------------------------------------- /src/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Style Loader options", 3 | "type": "object", 4 | "properties": { 5 | "injectType": { 6 | "description": "Allows to setup how styles will be injected into DOM.", 7 | "link": "https://github.com/webpack/style-loader#injecttype", 8 | "enum": [ 9 | "styleTag", 10 | "singletonStyleTag", 11 | "autoStyleTag", 12 | "lazyStyleTag", 13 | "lazySingletonStyleTag", 14 | "lazyAutoStyleTag", 15 | "linkTag" 16 | ] 17 | }, 18 | "attributes": { 19 | "description": "Adds custom attributes to tag.", 20 | "link": "https://github.com/webpack/style-loader#attributes", 21 | "type": "object" 22 | }, 23 | "insert": { 24 | "description": "Inserts ` 7 | 8 | 9 |

Body

10 |
11 | 12 | 13 | 14 | " 15 | `; 16 | 17 | exports[`lazyStyleTag options should pass "options" to "insert" function and unuse: errors 1`] = `[]`; 18 | 19 | exports[`lazyStyleTag options should pass "options" to "insert" function and unuse: warnings 1`] = `[]`; 20 | 21 | exports[`lazyStyleTag options should pass "options" to "insert" function: DOM 1`] = ` 22 | " 23 | style-loader test 24 | 25 | 26 | 27 |

Body

28 |
29 | 30 | 31 | 32 | " 36 | `; 37 | 38 | exports[`lazyStyleTag options should pass "options" to "insert" function: errors 1`] = `[]`; 39 | 40 | exports[`lazyStyleTag options should pass "options" to "insert" function: warnings 1`] = `[]`; 41 | 42 | exports[`lazyStyleTag options should pass "options" to "styleTagTransform" function: DOM 1`] = ` 43 | " 44 | style-loader test 45 | 46 | 52 | 53 |

Body

54 |
55 | 56 | 57 | 58 | " 59 | `; 60 | 61 | exports[`lazyStyleTag options should pass "options" to "styleTagTransform" function: errors 1`] = `[]`; 62 | 63 | exports[`lazyStyleTag options should pass "options" to "styleTagTransform" function: warnings 1`] = `[]`; 64 | -------------------------------------------------------------------------------- /test/lazyStyleTag-options.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import { 4 | compile, 5 | getCompiler, 6 | getEntryByInjectType, 7 | getErrors, 8 | getWarnings, 9 | runInJsDom, 10 | } from "./helpers/index"; 11 | 12 | describe("lazyStyleTag options", () => { 13 | it('should pass "options" to "insert" function', async () => { 14 | expect.assertions(3); 15 | 16 | const entry = getEntryByInjectType("options.js", "lazyStyleTag"); 17 | const compiler = getCompiler(entry, { 18 | injectType: "lazyStyleTag", 19 | insert: require.resolve("./fixtures/insert-options.js"), 20 | }); 21 | const stats = await compile(compiler); 22 | 23 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 24 | expect(dom.serialize()).toMatchSnapshot("DOM"); 25 | }); 26 | 27 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 28 | expect(getErrors(stats)).toMatchSnapshot("errors"); 29 | }); 30 | 31 | it('should pass "options" to "insert" function and unuse', async () => { 32 | expect.assertions(3); 33 | 34 | const entry = getEntryByInjectType("options-use-unuse.js", "lazyStyleTag"); 35 | const compiler = getCompiler(entry, { 36 | injectType: "lazyStyleTag", 37 | insert: require.resolve("./fixtures/insert-options.js"), 38 | }); 39 | const stats = await compile(compiler); 40 | 41 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 42 | expect(dom.serialize()).toMatchSnapshot("DOM"); 43 | }); 44 | 45 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 46 | expect(getErrors(stats)).toMatchSnapshot("errors"); 47 | }); 48 | 49 | it('should pass "options" to "styleTagTransform" function', async () => { 50 | expect.assertions(3); 51 | 52 | const entry = getEntryByInjectType("options.js", "lazyStyleTag"); 53 | const compiler = getCompiler(entry, { 54 | injectType: "lazyStyleTag", 55 | styleTagTransform: require.resolve( 56 | "./fixtures/styleTagTransformAdditionalStyles", 57 | ), 58 | }); 59 | const stats = await compile(compiler); 60 | 61 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 62 | expect(dom.serialize()).toMatchSnapshot("DOM"); 63 | }); 64 | 65 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 66 | expect(getErrors(stats)).toMatchSnapshot("errors"); 67 | }); 68 | }); 69 | -------------------------------------------------------------------------------- /test/runtime/isEqualLocals.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import isEqualLocals from "../../src/runtime/isEqualLocals"; 4 | 5 | describe("isEqualLocals", () => { 6 | it("should work", () => { 7 | expect(isEqualLocals()).toBe(true); 8 | expect(isEqualLocals({}, {})).toBe(true); 9 | 10 | expect(isEqualLocals(undefined, undefined)).toBe(true); 11 | expect(isEqualLocals({ foo: "bar" }, { foo: "bar" })).toBe(true); 12 | expect( 13 | isEqualLocals({ foo: "bar", bar: "baz" }, { foo: "bar", bar: "baz" }), 14 | ).toBe(true); 15 | expect( 16 | isEqualLocals({ foo: "bar", bar: "baz" }, { bar: "baz", foo: "bar" }), 17 | ).toBe(true); 18 | expect( 19 | isEqualLocals({ bar: "baz", foo: "bar" }, { foo: "bar", bar: "baz" }), 20 | ).toBe(true); 21 | 22 | expect(isEqualLocals(undefined, { foo: "bar" })).toBe(false); 23 | 24 | expect(isEqualLocals({ foo: "bar" }, undefined)).toBe(false); 25 | 26 | expect(isEqualLocals({ foo: "bar" }, { foo: "baz" })).toBe(false); 27 | 28 | expect(isEqualLocals({ foo: "bar" }, { bar: "bar" })).toBe(false); 29 | expect(isEqualLocals({ bar: "bar" }, { foo: "bar" })).toBe(false); 30 | 31 | expect(isEqualLocals({ foo: "bar" }, { foo: "bar", bar: "baz" })).toBe( 32 | false, 33 | ); 34 | expect(isEqualLocals({ foo: "bar", bar: "baz" }, { foo: "bar" })).toBe( 35 | false, 36 | ); 37 | 38 | // Should never happen, but let's test it 39 | expect(isEqualLocals({ foo: "bar" }, { foo: true })).toBe(false); 40 | expect(isEqualLocals({ foo: true }, { foo: "bar" })).toBe(false); 41 | 42 | expect(isEqualLocals({ foo: "bar" }, { foo: undefined })).toBe(false); 43 | 44 | expect(isEqualLocals({ foo: undefined }, { foo: "bar" })).toBe(false); 45 | expect(isEqualLocals({ foo: { foo: "bar" } }, { foo: "bar" })).toBe(false); 46 | 47 | expect(isEqualLocals({ foo: "bar" }, { foo: "bar" }, true)).toBe(true); 48 | expect(isEqualLocals({ foo: "bar" }, { foo: "baz" }, true)).toBe(false); 49 | expect( 50 | isEqualLocals( 51 | { default: "foo", foo: "bar" }, 52 | { default: "bar", foo: "bar" }, 53 | true, 54 | ), 55 | ).toBe(true); 56 | expect( 57 | isEqualLocals( 58 | { default: "foo", foo: "bar" }, 59 | { default: "bar", foo: "baz" }, 60 | true, 61 | ), 62 | ).toBe(false); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/validate-options.test.js: -------------------------------------------------------------------------------- 1 | import { compile, getCompiler } from "./helpers"; 2 | 3 | describe("validate options", () => { 4 | const tests = { 5 | injectType: { 6 | success: [ 7 | "styleTag", 8 | "singletonStyleTag", 9 | "autoStyleTag", 10 | "lazyStyleTag", 11 | "lazySingletonStyleTag", 12 | "lazyAutoStyleTag", 13 | "linkTag", 14 | ], 15 | failure: ["unknown"], 16 | }, 17 | attributes: { 18 | success: [{}, { id: "id" }], 19 | failure: [true], 20 | }, 21 | insert: { 22 | success: ["selector", require.resolve("./fixtures/insertFn.js")], 23 | failure: [true], 24 | }, 25 | esModule: { 26 | success: [true, false], 27 | failure: ["true"], 28 | }, 29 | styleTagTransform: { 30 | success: [require.resolve("./fixtures/styleTagTransform")], 31 | failure: [true, []], 32 | }, 33 | unknown: { 34 | success: [], 35 | failure: [1, true, false, "test", /test/, [], {}, { foo: "bar" }], 36 | }, 37 | }; 38 | 39 | function stringifyValue(value) { 40 | if ( 41 | Array.isArray(value) || 42 | (value && typeof value === "object" && value.constructor === Object) 43 | ) { 44 | return JSON.stringify(value); 45 | } 46 | 47 | return value; 48 | } 49 | 50 | async function createTestCase(key, value, type) { 51 | it(`should ${ 52 | type === "success" ? "successfully validate" : "throw an error on" 53 | } the "${key}" option with "${stringifyValue(value)}" value`, async () => { 54 | const compiler = getCompiler("simple.js", { [key]: value }); 55 | 56 | let stats; 57 | 58 | try { 59 | stats = await compile(compiler); 60 | } finally { 61 | if (type === "success") { 62 | expect(stats.hasErrors()).toBe(false); 63 | } else if (type === "failure") { 64 | const { 65 | compilation: { errors }, 66 | } = stats; 67 | 68 | expect(errors).toHaveLength(2); 69 | expect(() => { 70 | throw new Error(errors[0].error.message); 71 | }).toThrowErrorMatchingSnapshot(); 72 | } 73 | } 74 | }); 75 | } 76 | 77 | for (const [key, values] of Object.entries(tests)) { 78 | for (const type of Object.keys(values)) { 79 | for (const value of values[type]) { 80 | createTestCase(key, value, type); 81 | } 82 | } 83 | } 84 | }); 85 | -------------------------------------------------------------------------------- /src/runtime/singletonStyleDomAPI.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | /* istanbul ignore next */ 3 | const replaceText = (function replaceText() { 4 | const textStore = []; 5 | 6 | return function replace(index, replacement) { 7 | textStore[index] = replacement; 8 | 9 | return textStore.filter(Boolean).join("\n"); 10 | }; 11 | })(); 12 | 13 | /* istanbul ignore next */ 14 | function apply(styleElement, index, remove, obj) { 15 | let css; 16 | 17 | if (remove) { 18 | css = ""; 19 | } else { 20 | css = ""; 21 | 22 | if (obj.supports) { 23 | css += `@supports (${obj.supports}) {`; 24 | } 25 | 26 | if (obj.media) { 27 | css += `@media ${obj.media} {`; 28 | } 29 | 30 | const needLayer = typeof obj.layer !== "undefined"; 31 | 32 | if (needLayer) { 33 | css += `@layer${obj.layer.length > 0 ? ` ${obj.layer}` : ""} {`; 34 | } 35 | 36 | css += obj.css; 37 | 38 | if (needLayer) { 39 | css += "}"; 40 | } 41 | 42 | if (obj.media) { 43 | css += "}"; 44 | } 45 | 46 | if (obj.supports) { 47 | css += "}"; 48 | } 49 | } 50 | 51 | // For old IE 52 | /* istanbul ignore if */ 53 | if (styleElement.styleSheet) { 54 | styleElement.styleSheet.cssText = replaceText(index, css); 55 | } else { 56 | const cssNode = document.createTextNode(css); 57 | const { childNodes } = styleElement; 58 | 59 | if (childNodes[index]) { 60 | styleElement.removeChild(childNodes[index]); 61 | } 62 | 63 | if (childNodes.length) { 64 | styleElement.insertBefore(cssNode, childNodes[index]); 65 | } else { 66 | styleElement.appendChild(cssNode); 67 | } 68 | } 69 | } 70 | 71 | const singletonData = { 72 | singleton: null, 73 | singletonCounter: 0, 74 | }; 75 | 76 | /* istanbul ignore next */ 77 | function domAPI(options) { 78 | if (typeof document === "undefined") { 79 | return { 80 | update: () => {}, 81 | remove: () => {}, 82 | }; 83 | } 84 | 85 | const styleIndex = singletonData.singletonCounter++; 86 | const styleElement = (singletonData.singleton ||= 87 | options.insertStyleElement(options)); 88 | 89 | return { 90 | update: (obj) => { 91 | apply(styleElement, styleIndex, false, obj); 92 | }, 93 | remove: (obj) => { 94 | apply(styleElement, styleIndex, true, obj); 95 | }, 96 | }; 97 | } 98 | 99 | module.exports = domAPI; 100 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: style-loader 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - next 8 | pull_request: 9 | branches: 10 | - main 11 | - next 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | lint: 18 | name: Lint - ${{ matrix.os }} - Node v${{ matrix.node-version }} 19 | 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | 23 | strategy: 24 | matrix: 25 | os: [ubuntu-latest] 26 | node-version: [lts/*] 27 | 28 | runs-on: ${{ matrix.os }} 29 | 30 | concurrency: 31 | group: lint-${{ matrix.os }}-v${{ matrix.node-version }}-${{ github.ref }} 32 | cancel-in-progress: true 33 | 34 | steps: 35 | - uses: actions/checkout@v5 36 | with: 37 | fetch-depth: 0 38 | 39 | - name: Use Node.js ${{ matrix.node-version }} 40 | uses: actions/setup-node@v4 41 | with: 42 | node-version: ${{ matrix.node-version }} 43 | cache: "npm" 44 | 45 | - name: Install dependencies 46 | run: npm ci 47 | 48 | - name: Lint 49 | run: npm run lint 50 | 51 | - name: Security audit 52 | run: npm run security 53 | 54 | - name: Validate PR commits with commitlint 55 | if: github.event_name == 'pull_request' 56 | run: npx commitlint --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose 57 | 58 | test: 59 | name: Test - ${{ matrix.os }} - Node v${{ matrix.node-version }}, Webpack ${{ matrix.webpack-version }} 60 | 61 | strategy: 62 | matrix: 63 | os: [ubuntu-latest, windows-latest, macos-latest] 64 | node-version: [18.x, 20.x, 22.x, 24.x] 65 | webpack-version: [latest] 66 | 67 | runs-on: ${{ matrix.os }} 68 | 69 | concurrency: 70 | group: test-${{ matrix.os }}-v${{ matrix.node-version }}-${{ matrix.webpack-version }}-${{ github.ref }} 71 | cancel-in-progress: true 72 | 73 | steps: 74 | - name: Setup Git 75 | if: matrix.os == 'windows-latest' 76 | run: git config --global core.autocrlf input 77 | 78 | - uses: actions/checkout@v5 79 | 80 | - name: Use Node.js ${{ matrix.node-version }} 81 | uses: actions/setup-node@v4 82 | with: 83 | node-version: ${{ matrix.node-version }} 84 | cache: "npm" 85 | 86 | - name: Install dependencies 87 | run: npm ci 88 | 89 | - name: Install webpack ${{ matrix.webpack-version }} 90 | if: matrix.webpack-version != 'latest' 91 | run: npm i webpack@${{ matrix.webpack-version }} 92 | 93 | - name: Run tests for webpack version ${{ matrix.webpack-version }} 94 | run: npm run test:coverage -- --ci 95 | 96 | - name: Submit coverage data to codecov 97 | uses: codecov/codecov-action@v5 98 | with: 99 | token: ${{ secrets.CODECOV_TOKEN }} 100 | -------------------------------------------------------------------------------- /src/runtime/injectStylesIntoStyleTag.js: -------------------------------------------------------------------------------- 1 | const stylesInDOM = []; 2 | 3 | function getIndexByIdentifier(identifier) { 4 | let result = -1; 5 | 6 | for (let i = 0; i < stylesInDOM.length; i++) { 7 | if (stylesInDOM[i].identifier === identifier) { 8 | result = i; 9 | break; 10 | } 11 | } 12 | 13 | return result; 14 | } 15 | 16 | function addElementStyle(obj, options) { 17 | const api = options.domAPI(options); 18 | 19 | api.update(obj); 20 | 21 | const updater = (newObj) => { 22 | if (newObj) { 23 | if ( 24 | newObj.css === obj.css && 25 | newObj.media === obj.media && 26 | newObj.sourceMap === obj.sourceMap && 27 | newObj.supports === obj.supports && 28 | newObj.layer === obj.layer 29 | ) { 30 | return; 31 | } 32 | 33 | api.update((obj = newObj)); 34 | } else { 35 | api.remove(); 36 | } 37 | }; 38 | 39 | return updater; 40 | } 41 | 42 | function modulesToDom(list, options) { 43 | const idCountMap = {}; 44 | const identifiers = []; 45 | 46 | for (let i = 0; i < list.length; i++) { 47 | const item = list[i]; 48 | const id = options.base ? item[0] + options.base : item[0]; 49 | const count = idCountMap[id] || 0; 50 | const identifier = `${id} ${count}`; 51 | 52 | idCountMap[id] = count + 1; 53 | 54 | const indexByIdentifier = getIndexByIdentifier(identifier); 55 | const obj = { 56 | css: item[1], 57 | media: item[2], 58 | sourceMap: item[3], 59 | supports: item[4], 60 | layer: item[5], 61 | }; 62 | 63 | if (indexByIdentifier !== -1) { 64 | stylesInDOM[indexByIdentifier].references++; 65 | stylesInDOM[indexByIdentifier].updater(obj); 66 | } else { 67 | const updater = addElementStyle(obj, options); 68 | 69 | options.byIndex = i; 70 | 71 | stylesInDOM.splice(i, 0, { 72 | identifier, 73 | updater, 74 | references: 1, 75 | }); 76 | } 77 | 78 | identifiers.push(identifier); 79 | } 80 | 81 | return identifiers; 82 | } 83 | 84 | module.exports = (list, options) => { 85 | options ||= {}; 86 | 87 | list ||= []; 88 | 89 | let lastIdentifiers = modulesToDom(list, options); 90 | 91 | return function update(newList) { 92 | newList ||= []; 93 | 94 | for (let i = 0; i < lastIdentifiers.length; i++) { 95 | const identifier = lastIdentifiers[i]; 96 | const index = getIndexByIdentifier(identifier); 97 | 98 | stylesInDOM[index].references--; 99 | } 100 | 101 | const newLastIdentifiers = modulesToDom(newList, options); 102 | 103 | for (let i = 0; i < lastIdentifiers.length; i++) { 104 | const identifier = lastIdentifiers[i]; 105 | const index = getIndexByIdentifier(identifier); 106 | 107 | if (stylesInDOM[index].references === 0) { 108 | stylesInDOM[index].updater(); 109 | stylesInDOM.splice(index, 1); 110 | } 111 | } 112 | 113 | lastIdentifiers = newLastIdentifiers; 114 | }; 115 | }; 116 | -------------------------------------------------------------------------------- /test/attributes-option.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | compile, 3 | getCompiler, 4 | getEntryByInjectType, 5 | getErrors, 6 | getWarnings, 7 | runInJsDom, 8 | } from "./helpers/index"; 9 | 10 | describe('"attributes" option', () => { 11 | const injectTypes = [ 12 | "styleTag", 13 | "singletonStyleTag", 14 | "lazyStyleTag", 15 | "lazySingletonStyleTag", 16 | "linkTag", 17 | ]; 18 | 19 | for (const injectType of injectTypes) { 20 | it(`should add attributes to tag when the "injectType" option is "${injectType}"`, async () => { 21 | expect.assertions(3); 22 | 23 | const entry = getEntryByInjectType("simple.js", injectType); 24 | const compiler = getCompiler(entry, { 25 | injectType, 26 | attributes: { 27 | type: "text/css", 28 | foo: "bar", 29 | "data-id": "style-tag-id", 30 | }, 31 | }); 32 | const stats = await compile(compiler); 33 | 34 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 35 | expect(dom.serialize()).toMatchSnapshot("DOM"); 36 | }); 37 | 38 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 39 | expect(getErrors(stats)).toMatchSnapshot("errors"); 40 | }); 41 | 42 | it(`should apply nonce attribute when "injectType" option is "${injectType}"`, async () => { 43 | expect.assertions(3); 44 | 45 | const entry = getEntryByInjectType("simple.js", injectType); 46 | const compiler = getCompiler(entry, { 47 | injectType, 48 | attributes: { 49 | nonce: "234567", 50 | }, 51 | }); 52 | const stats = await compile(compiler); 53 | 54 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 55 | expect(dom.serialize()).toMatchSnapshot("DOM"); 56 | }); 57 | 58 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 59 | expect(getErrors(stats)).toMatchSnapshot("errors"); 60 | }); 61 | 62 | it(`should add nonce attribute when "injectType" option is "${injectType}"`, async () => { 63 | expect.assertions(3); 64 | 65 | const entry = getEntryByInjectType("nonce-require.js", injectType); 66 | const compiler = getCompiler(entry, { injectType, esModule: false }); 67 | const stats = await compile(compiler); 68 | 69 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 70 | expect(dom.serialize()).toMatchSnapshot("DOM"); 71 | }); 72 | 73 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 74 | expect(getErrors(stats)).toMatchSnapshot("errors"); 75 | }); 76 | 77 | it(`should add nonce attribute when "injectType" option is "${injectType}" #2`, async () => { 78 | expect.assertions(3); 79 | 80 | const entry = getEntryByInjectType("nonce-import.js", injectType); 81 | const compiler = getCompiler(entry, { injectType }); 82 | const stats = await compile(compiler); 83 | 84 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 85 | expect(dom.serialize()).toMatchSnapshot("DOM"); 86 | }); 87 | 88 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 89 | expect(getErrors(stats)).toMatchSnapshot("errors"); 90 | }); 91 | } 92 | }); 93 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "style-loader", 3 | "version": "4.0.0", 4 | "description": "style loader module for webpack", 5 | "keywords": [ 6 | "webpack" 7 | ], 8 | "homepage": "https://github.com/webpack/style-loader", 9 | "bugs": "https://github.com/webpack/style-loader/issues", 10 | "repository": "webpack/style-loader", 11 | "funding": { 12 | "type": "opencollective", 13 | "url": "https://opencollective.com/webpack" 14 | }, 15 | "license": "MIT", 16 | "author": "Tobias Koppers @sokra", 17 | "main": "dist/cjs.js", 18 | "files": [ 19 | "dist" 20 | ], 21 | "scripts": { 22 | "start": "npm run build -- -w", 23 | "clean": "del-cli dist", 24 | "validate:runtime": "es-check es3 \"dist/runtime/**/*.js\"", 25 | "prebuild": "npm run clean", 26 | "build": "cross-env NODE_ENV=production babel src -d dist --copy-files", 27 | "postbuild": "npm run validate:runtime", 28 | "commitlint": "commitlint --from=main", 29 | "security": "npm audit --production", 30 | "lint:prettier": "prettier --cache --list-different .", 31 | "lint:js": "eslint --cache .", 32 | "lint:spelling": "cspell --cache --no-must-find-files --quiet \"**/*.*\"", 33 | "lint": "npm-run-all -l -p \"lint:**\"", 34 | "fix:js": "npm run lint:js -- --fix", 35 | "fix:prettier": "npm run lint:prettier -- --write", 36 | "fix": "npm-run-all -l fix:js fix:prettier", 37 | "test:only": "cross-env NODE_ENV=test jest", 38 | "test:watch": "npm run test:only -- --watch", 39 | "test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage", 40 | "test:manual": "npm run build && webpack serve ./test/manual/src/index.js --open --config test/manual/webpack.config.js", 41 | "pretest": "npm run lint", 42 | "test": "npm run test:coverage", 43 | "prepare": "husky && npm run build", 44 | "release": "standard-version" 45 | }, 46 | "devDependencies": { 47 | "@babel/cli": "^7.24.7", 48 | "@babel/core": "^7.24.7", 49 | "@babel/preset-env": "^7.24.7", 50 | "@commitlint/cli": "^19.3.0", 51 | "@commitlint/config-conventional": "^19.2.2", 52 | "@eslint/markdown": "^7.1.0", 53 | "babel-jest": "^30.0.0", 54 | "cross-env": "^7.0.3", 55 | "cspell": "^8.10.0", 56 | "css-loader": "^7.1.2", 57 | "del-cli": "^5.1.0", 58 | "es-check": "^7.2.1", 59 | "eslint": "^9.32.0", 60 | "eslint-config-prettier": "^10.1.8", 61 | "eslint-config-webpack": "^4.5.1", 62 | "eslint-plugin-import": "^2.32.0", 63 | "eslint-plugin-jest": "^29.0.1", 64 | "eslint-plugin-n": "^17.21.3", 65 | "file-loader": "^6.2.0", 66 | "husky": "^9.1.3", 67 | "jest": "^30.0.0", 68 | "jest-environment-jsdom": "^30.0.0", 69 | "jsdom": "^24.1.0", 70 | "lint-staged": "^15.2.7", 71 | "memfs": "^4.9.3", 72 | "npm-run-all": "^4.1.5", 73 | "prettier": "^3.3.2", 74 | "sass": "^1.77.6", 75 | "sass-loader": "^14.2.1", 76 | "semver": "^7.6.0", 77 | "standard-version": "^9.5.0", 78 | "typescript-eslint": "^8.38.0", 79 | "webpack": "^5.92.1", 80 | "webpack-cli": "^5.1.4", 81 | "webpack-dev-server": "^5.0.4" 82 | }, 83 | "peerDependencies": { 84 | "webpack": "^5.27.0" 85 | }, 86 | "engines": { 87 | "node": ">= 18.12.0" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/manual/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | style-loader manual test 7 | 8 | 9 |

style-loader manual tests

10 | 11 |
12 |

The "injectType" is "styleTag"

13 | 14 |
15 |

Pure css

16 |
Red
17 |
Green
18 |
Blue
19 |
20 |
21 | 22 |
23 |

Sass/SCSS

24 |
Red
25 |
Green
26 |
Blue
27 |
28 |
29 |
30 | 31 |
32 |

The "injectType" is "lazyStyleTag"

33 | 34 |
35 |

Pure css

36 |
Red
37 |
Green
38 |
Blue
39 |
40 |
41 | 42 |
43 |

Sass/SCSS

44 |
Red
45 |
Green
46 |
Blue
47 |
48 |
49 |
50 | 51 |
52 |

The "injectType" is "linkTag"

53 | 54 |
55 |

Pure css

56 | 57 | 58 | 59 | 60 |
61 |
62 | 63 |
64 |

Use/Unuse

65 |
No color after timeout 6000ms
66 |
67 | 68 |
69 |

Order

70 |
BACKGROUND SHOULD BE RED
71 |
72 | 73 |
74 |

Duplicate

75 |
SHOULD BE BLUE
76 |
77 | 78 |
79 |

Modules

80 |
BACKGROUND SHOULD BE RED
81 |
BACKGROUND SHOULD BE NAVY
82 | 83 |
84 |
BACKGROUND SHOULD BE ORANGE
85 |
86 | 87 |
BACKGROUND SHOULD BE CRIMSON
88 |
89 | 90 |
91 |

Toggle

92 |
93 | 94 |
95 |

Media and Supports

96 |
BLUE
97 |
GREEN
98 |
RED
99 |
RED
100 |
101 | 102 |
103 |

Custom element

104 | 105 |
106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /test/styleTagTransform-option.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import { 4 | compile, 5 | getCompiler, 6 | getEntryByInjectType, 7 | getErrors, 8 | getWarnings, 9 | runInJsDom, 10 | } from "./helpers/index"; 11 | 12 | describe('"styleTagTransform" option', () => { 13 | it('should work when the "styleTagTransform" option is not specify', async () => { 14 | const entry = getEntryByInjectType("simple.js", "styleTag"); 15 | const compiler = getCompiler(entry, { 16 | injectType: "styleTag", 17 | }); 18 | const stats = await compile(compiler); 19 | 20 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 21 | expect(dom.serialize()).toMatchSnapshot("DOM"); 22 | }); 23 | 24 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 25 | expect(getErrors(stats)).toMatchSnapshot("errors"); 26 | }); 27 | 28 | it('should work when the "styleTagTransform" option is specify', async () => { 29 | const entry = getEntryByInjectType("simple.js", "styleTag"); 30 | const compiler = getCompiler(entry, { 31 | injectType: "styleTag", 32 | styleTagTransform: require.resolve("./fixtures/styleTagTransform"), 33 | }); 34 | const stats = await compile(compiler); 35 | 36 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 37 | expect(dom.serialize()).toMatchSnapshot("DOM"); 38 | }); 39 | 40 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 41 | expect(getErrors(stats)).toMatchSnapshot("errors"); 42 | }); 43 | 44 | it('should work when the "styleTagTransform" option is specify and injectType lazyStyleTag', async () => { 45 | const entry = getEntryByInjectType("simple.js", "lazyStyleTag"); 46 | const compiler = getCompiler(entry, { 47 | injectType: "lazyStyleTag", 48 | }); 49 | const stats = await compile(compiler); 50 | 51 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 52 | expect(dom.serialize()).toMatchSnapshot("DOM"); 53 | }); 54 | 55 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 56 | expect(getErrors(stats)).toMatchSnapshot("errors"); 57 | }); 58 | 59 | it('should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag', async () => { 60 | const entry = getEntryByInjectType("simple.js", "lazyStyleTag"); 61 | const compiler = getCompiler(entry, { 62 | injectType: "lazyStyleTag", 63 | styleTagTransform: require.resolve("./fixtures/styleTagTransform"), 64 | }); 65 | const stats = await compile(compiler); 66 | 67 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 68 | expect(dom.serialize()).toMatchSnapshot("DOM"); 69 | }); 70 | 71 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 72 | expect(getErrors(stats)).toMatchSnapshot("errors"); 73 | }); 74 | 75 | it('should "styleTagTransform" function path added to buildDependencies when injectType lazyStyleTag', async () => { 76 | const styleTagTransformFn = require.resolve("./fixtures/styleTagTransform"); 77 | const entry = getEntryByInjectType("simple.js", "lazyStyleTag"); 78 | const compiler = getCompiler(entry, { 79 | injectType: "lazyStyleTag", 80 | styleTagTransform: styleTagTransformFn, 81 | }); 82 | const stats = await compile(compiler); 83 | const { buildDependencies } = stats.compilation; 84 | 85 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 86 | expect(dom.serialize()).toMatchSnapshot("DOM"); 87 | }); 88 | 89 | expect(buildDependencies.has(styleTagTransformFn)).toBe(true); 90 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 91 | expect(getErrors(stats)).toMatchSnapshot("errors"); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /test/runtime/__snapshots__/injectStylesIntoLinkTag.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`addStyle should throw error with incorrect "insert" option 1`] = `"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."`; 4 | 5 | exports[`addStyle should throw error with invalid "insert" option 1`] = `"'#test><><><' is not a valid selector"`; 6 | 7 | exports[`addStyle should work 1`] = `"Title

Hello world

"`; 8 | 9 | exports[`addStyle should work with "__webpack_nonce__" variable 1`] = `"Title

Hello world

"`; 10 | 11 | exports[`addStyle should work with "attributes" option 1`] = `"Title

Hello world

"`; 12 | 13 | exports[`addStyle should work with "insert" option #2 1`] = `"Title

Hello world

"`; 14 | 15 | exports[`addStyle should work with "insert" option #3 1`] = `"Title

Hello world

"`; 16 | 17 | exports[`addStyle should work with "insert" option #3 2`] = `""`; 18 | 19 | exports[`addStyle should work with "insert" option #4 1`] = `"Title

Hello world

"`; 20 | 21 | exports[`addStyle should work with "insert" option #5 1`] = `"Title

Hello world

"`; 22 | 23 | exports[`addStyle should work with "insert" option 1`] = `"Title

Hello world

"`; 24 | 25 | exports[`addStyle should work with "nonce" attribute and "__webpack_nonce__" variable 1`] = `"Title

Hello world

"`; 26 | 27 | exports[`addStyle should work with updates #2 1`] = `"Title

Hello world

"`; 28 | 29 | exports[`addStyle should work with updates #2 2`] = `"Title

Hello world

"`; 30 | 31 | exports[`addStyle should work with updates #3 1`] = `"Title

Hello world

"`; 32 | 33 | exports[`addStyle should work with updates #3 2`] = `"Title

Hello world

"`; 34 | 35 | exports[`addStyle should work with updates #4 1`] = `"Title

Hello world

"`; 36 | 37 | exports[`addStyle should work with updates #4 2`] = `"Title

Hello world

"`; 38 | 39 | exports[`addStyle should work with updates #5 1`] = `"Title

Hello world

"`; 40 | 41 | exports[`addStyle should work with updates #5 2`] = `"Title

Hello world

"`; 42 | 43 | exports[`addStyle should work with updates 1`] = `"Title

Hello world

"`; 44 | 45 | exports[`addStyle should work with updates 2`] = `"Title

Hello world

"`; 46 | -------------------------------------------------------------------------------- /test/__snapshots__/styleTagTransform-option.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`"styleTagTransform" option should "styleTagTransform" function path added to buildDependencies when injectType lazyStyleTag: DOM 1`] = ` 4 | " 5 | style-loader test 6 | 7 | 16 | 17 |

Body

18 |
19 | 20 | 21 | 22 | " 23 | `; 24 | 25 | exports[`"styleTagTransform" option should "styleTagTransform" function path added to buildDependencies when injectType lazyStyleTag: errors 1`] = `[]`; 26 | 27 | exports[`"styleTagTransform" option should "styleTagTransform" function path added to buildDependencies when injectType lazyStyleTag: warnings 1`] = `[]`; 28 | 29 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is not specify: DOM 1`] = ` 30 | " 31 | style-loader test 32 | 33 | 40 | 41 |

Body

42 |
43 | 44 | 45 | 46 | " 47 | `; 48 | 49 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is not specify: errors 1`] = `[]`; 50 | 51 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is not specify: warnings 1`] = `[]`; 52 | 53 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: DOM 1`] = ` 54 | " 55 | style-loader test 56 | 57 | 66 | 67 |

Body

68 |
69 | 70 | 71 | 72 | " 73 | `; 74 | 75 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: errors 1`] = `[]`; 76 | 77 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is path to module and injectType lazyStyleTag: warnings 1`] = `[]`; 78 | 79 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is specify and injectType lazyStyleTag: DOM 1`] = ` 80 | " 81 | style-loader test 82 | 83 | 90 | 91 |

Body

92 |
93 | 94 | 95 | 96 | " 97 | `; 98 | 99 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is specify and injectType lazyStyleTag: errors 1`] = `[]`; 100 | 101 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is specify and injectType lazyStyleTag: warnings 1`] = `[]`; 102 | 103 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is specify: DOM 1`] = ` 104 | " 105 | style-loader test 106 | 107 | 116 | 117 |

Body

118 |
119 | 120 | 121 | 122 | " 123 | `; 124 | 125 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is specify: errors 1`] = `[]`; 126 | 127 | exports[`"styleTagTransform" option should work when the "styleTagTransform" option is specify: warnings 1`] = `[]`; 128 | -------------------------------------------------------------------------------- /test/esModule-option.test.js: -------------------------------------------------------------------------------- 1 | import path from "node:path"; 2 | 3 | import { 4 | compile, 5 | getCompiler, 6 | getEntryByInjectType, 7 | getErrors, 8 | getWarnings, 9 | runInJsDom, 10 | } from "./helpers/index"; 11 | 12 | describe('"esModule" option', () => { 13 | const injectTypes = [ 14 | "styleTag", 15 | "singletonStyleTag", 16 | "autoStyleTag", 17 | "lazyStyleTag", 18 | "lazySingletonStyleTag", 19 | "lazyAutoStyleTag", 20 | "linkTag", 21 | ]; 22 | 23 | const moduleTypes = ["commonjs-modules.js", "es-modules.js"]; 24 | 25 | const esModuleValues = [ 26 | // No CSS modules 27 | { styleLoader: {}, cssLoader: {} }, 28 | { styleLoader: { esModule: false }, cssLoader: { esModule: false } }, 29 | { styleLoader: { esModule: true }, cssLoader: { esModule: false } }, 30 | { styleLoader: { esModule: false }, cssLoader: { esModule: true } }, 31 | { styleLoader: { esModule: true }, cssLoader: { esModule: true } }, 32 | { styleLoader: {}, cssLoader: { modules: false } }, 33 | 34 | // CSS modules 35 | { styleLoader: {}, cssLoader: { modules: true } }, 36 | { styleLoader: {}, cssLoader: { modules: { namedExport: false } } }, 37 | { styleLoader: {}, cssLoader: { modules: { namedExport: true } } }, 38 | { 39 | styleLoader: { esModule: false }, 40 | cssLoader: { esModule: false, modules: { namedExport: false } }, 41 | }, 42 | { 43 | styleLoader: { esModule: true }, 44 | cssLoader: { esModule: false, modules: { namedExport: false } }, 45 | }, 46 | { 47 | styleLoader: { esModule: false }, 48 | cssLoader: { esModule: true, modules: { namedExport: false } }, 49 | }, 50 | { 51 | styleLoader: { esModule: true }, 52 | cssLoader: { esModule: true, modules: { namedExport: false } }, 53 | }, 54 | 55 | { 56 | styleLoader: { esModule: false }, 57 | cssLoader: { esModule: true, modules: { namedExport: true } }, 58 | }, 59 | { 60 | styleLoader: { esModule: true }, 61 | cssLoader: { esModule: true, modules: { namedExport: true } }, 62 | }, 63 | 64 | // exportOnlyLocals should not lead to error 65 | { 66 | styleLoader: { esModule: true }, 67 | cssLoader: { 68 | esModule: true, 69 | modules: { namedExport: true, exportOnlyLocals: true }, 70 | }, 71 | }, 72 | { 73 | styleLoader: { esModule: true }, 74 | cssLoader: { 75 | esModule: true, 76 | modules: { namedExport: false, exportOnlyLocals: true }, 77 | }, 78 | }, 79 | ]; 80 | 81 | for (const injectType of injectTypes) { 82 | for (const esModuleValue of esModuleValues) { 83 | for (const moduleType of moduleTypes) { 84 | let testName = ""; 85 | 86 | testName += `CONFIG: ${JSON.stringify(esModuleValue)},`; 87 | 88 | testName += ` MODULE_TYPE: "${moduleType.split(".")[0]}",`; 89 | 90 | testName += ` INJECT_TYPE: "${injectType}"`; 91 | 92 | it(`${testName}`, async () => { 93 | const entry = getEntryByInjectType(moduleType, injectType); 94 | const compiler = getCompiler( 95 | entry, 96 | {}, 97 | { 98 | module: { 99 | parser: { 100 | javascript: { 101 | exportsPresence: 102 | esModuleValue.cssLoader.modules && 103 | esModuleValue.cssLoader.modules.exportOnlyLocals 104 | ? false 105 | : "error", 106 | }, 107 | }, 108 | rules: [ 109 | { 110 | test: /\.css$/i, 111 | use: [ 112 | { 113 | loader: path.resolve(__dirname, "../src/cjs.js"), 114 | options: { 115 | injectType, 116 | ...esModuleValue.styleLoader, 117 | }, 118 | }, 119 | injectType === "linkTag" 120 | ? { 121 | loader: "file-loader", 122 | options: { 123 | esModule: true, 124 | name: "[path][name].[ext]", 125 | }, 126 | } 127 | : { 128 | loader: "css-loader", 129 | options: esModuleValue.cssLoader, 130 | }, 131 | ], 132 | }, 133 | ], 134 | }, 135 | }, 136 | ); 137 | const stats = await compile(compiler); 138 | 139 | runInJsDom("main.bundle.js", compiler, stats, (dom) => { 140 | expect(dom.serialize()).toMatchSnapshot("DOM"); 141 | 142 | expect(dom.window.__cssLoader).toMatchSnapshot("exports"); 143 | }); 144 | 145 | expect(getWarnings(stats)).toMatchSnapshot("warnings"); 146 | expect(getErrors(stats)).toMatchSnapshot("errors"); 147 | }); 148 | } 149 | } 150 | } 151 | }); 152 | -------------------------------------------------------------------------------- /test/__snapshots__/validate-options.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`validate options should throw an error on the "attributes" option with "true" value 1`] = ` 4 | "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. 5 | - options.attributes should be an object: 6 | object { … } 7 | -> Adds custom attributes to tag. 8 | -> Read more at https://github.com/webpack/style-loader#attributes" 9 | `; 10 | 11 | exports[`validate options should throw an error on the "esModule" option with "true" value 1`] = ` 12 | "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. 13 | - options.esModule should be a boolean. 14 | -> Use the ES modules syntax. 15 | -> Read more at https://github.com/webpack/css-loader#esmodule" 16 | `; 17 | 18 | exports[`validate options should throw an error on the "injectType" option with "unknown" value 1`] = ` 19 | "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. 20 | - options.injectType should be one of these: 21 | "styleTag" | "singletonStyleTag" | "autoStyleTag" | "lazyStyleTag" | "lazySingletonStyleTag" | "lazyAutoStyleTag" | "linkTag" 22 | -> Allows to setup how styles will be injected into DOM. 23 | -> Read more at https://github.com/webpack/style-loader#injecttype" 24 | `; 25 | 26 | exports[`validate options should throw an error on the "insert" option with "true" value 1`] = ` 27 | "Invalid options object. Style Loader has been initialized using an options object that does not match the API schema. 28 | - options.insert should be a string. 29 | -> Inserts \` 11 | 18 | 19 |

Body

20 |
21 | 22 | 23 | 24 | " 25 | `; 26 | 27 | exports[`"injectType" option should work when the "injectType" option is "autoStyleTag": errors 1`] = `[]`; 28 | 29 | exports[`"injectType" option should work when the "injectType" option is "autoStyleTag": warnings 1`] = `[]`; 30 | 31 | exports[`"injectType" option should work when the "injectType" option is "lazyAutoStyleTag" with non DOM environment: errors 1`] = `[]`; 32 | 33 | exports[`"injectType" option should work when the "injectType" option is "lazyAutoStyleTag" with non DOM environment: warnings 1`] = `[]`; 34 | 35 | exports[`"injectType" option should work when the "injectType" option is "lazyAutoStyleTag": DOM 1`] = ` 36 | " 37 | style-loader test 38 | 39 | 46 | 47 |

Body

48 |
49 | 50 | 51 | 52 | " 53 | `; 54 | 55 | exports[`"injectType" option should work when the "injectType" option is "lazyAutoStyleTag": errors 1`] = `[]`; 56 | 57 | exports[`"injectType" option should work when the "injectType" option is "lazyAutoStyleTag": warnings 1`] = `[]`; 58 | 59 | exports[`"injectType" option should work when the "injectType" option is "lazySingletonStyleTag" with non DOM environment: errors 1`] = `[]`; 60 | 61 | exports[`"injectType" option should work when the "injectType" option is "lazySingletonStyleTag" with non DOM environment: warnings 1`] = `[]`; 62 | 63 | exports[`"injectType" option should work when the "injectType" option is "lazySingletonStyleTag": DOM 1`] = ` 64 | " 65 | style-loader test 66 | 67 | 74 | 75 |

Body

76 |
77 | 78 | 79 | 80 | " 81 | `; 82 | 83 | exports[`"injectType" option should work when the "injectType" option is "lazySingletonStyleTag": errors 1`] = `[]`; 84 | 85 | exports[`"injectType" option should work when the "injectType" option is "lazySingletonStyleTag": warnings 1`] = `[]`; 86 | 87 | exports[`"injectType" option should work when the "injectType" option is "lazyStyleTag" with non DOM environment: errors 1`] = `[]`; 88 | 89 | exports[`"injectType" option should work when the "injectType" option is "lazyStyleTag" with non DOM environment: warnings 1`] = `[]`; 90 | 91 | exports[`"injectType" option should work when the "injectType" option is "lazyStyleTag": DOM 1`] = ` 92 | " 93 | style-loader test 94 | 95 | 102 | 103 |

Body

104 |
105 | 106 | 107 | 108 | " 109 | `; 110 | 111 | exports[`"injectType" option should work when the "injectType" option is "lazyStyleTag": errors 1`] = `[]`; 112 | 113 | exports[`"injectType" option should work when the "injectType" option is "lazyStyleTag": warnings 1`] = `[]`; 114 | 115 | exports[`"injectType" option should work when the "injectType" option is "linkTag" with non DOM environment: errors 1`] = `[]`; 116 | 117 | exports[`"injectType" option should work when the "injectType" option is "linkTag" with non DOM environment: warnings 1`] = `[]`; 118 | 119 | exports[`"injectType" option should work when the "injectType" option is "linkTag": DOM 1`] = ` 120 | " 121 | style-loader test 122 | 123 | 124 | 125 |

Body

126 |
127 | 128 | 129 | 130 | " 131 | `; 132 | 133 | exports[`"injectType" option should work when the "injectType" option is "linkTag": errors 1`] = `[]`; 134 | 135 | exports[`"injectType" option should work when the "injectType" option is "linkTag": warnings 1`] = `[]`; 136 | 137 | exports[`"injectType" option should work when the "injectType" option is "singletonStyleTag" with non DOM environment: errors 1`] = `[]`; 138 | 139 | exports[`"injectType" option should work when the "injectType" option is "singletonStyleTag" with non DOM environment: warnings 1`] = `[]`; 140 | 141 | exports[`"injectType" option should work when the "injectType" option is "singletonStyleTag": DOM 1`] = ` 142 | " 143 | style-loader test 144 | 145 | 152 | 153 |

Body

154 |
155 | 156 | 157 | 158 | " 159 | `; 160 | 161 | exports[`"injectType" option should work when the "injectType" option is "singletonStyleTag": errors 1`] = `[]`; 162 | 163 | exports[`"injectType" option should work when the "injectType" option is "singletonStyleTag": warnings 1`] = `[]`; 164 | 165 | exports[`"injectType" option should work when the "injectType" option is "styleTag" with non DOM environment: errors 1`] = `[]`; 166 | 167 | exports[`"injectType" option should work when the "injectType" option is "styleTag" with non DOM environment: warnings 1`] = `[]`; 168 | 169 | exports[`"injectType" option should work when the "injectType" option is "styleTag": DOM 1`] = ` 170 | " 171 | style-loader test 172 | 173 | 180 | 181 |

Body

182 |
183 | 184 | 185 | 186 | " 187 | `; 188 | 189 | exports[`"injectType" option should work when the "injectType" option is "styleTag": errors 1`] = `[]`; 190 | 191 | exports[`"injectType" option should work when the "injectType" option is "styleTag": warnings 1`] = `[]`; 192 | -------------------------------------------------------------------------------- /test/manual/webpack.config.js: -------------------------------------------------------------------------------- 1 | // Commands: 2 | // SOURCE_MAP=yes npm run test:manual 3 | const ENABLE_SOURCE_MAP = 4 | typeof process.env.SOURCE_MAP !== "undefined" 5 | ? Boolean(process.env.SOURCE_MAP) 6 | : false; 7 | 8 | const ENABLE_ES_MODULE = 9 | typeof process.env.ES_MODULE !== "undefined" 10 | ? Boolean(process.env.ES_MODULE) 11 | : true; 12 | 13 | const ENABLE_PREVIOUS_ES_MODULE = 14 | typeof process.env.PREVIOUS_ES_MODULE !== "undefined" 15 | ? Boolean(process.env.PREVIOUS_ES_MODULE) 16 | : true; 17 | 18 | module.exports = { 19 | devtool: ENABLE_SOURCE_MAP ? "source-map" : false, 20 | mode: "development", 21 | output: { 22 | publicPath: "/dist/", 23 | }, 24 | module: { 25 | rules: [ 26 | { 27 | test: /\.css$/i, 28 | exclude: [ 29 | /\.module\.css$/i, 30 | /\.lazy\.css$/i, 31 | /\.lazy\.module\.css$/i, 32 | /\.link\.css$/i, 33 | /\.custom\.css$/i, 34 | ], 35 | use: [ 36 | { 37 | loader: require.resolve("../../dist/cjs.js"), 38 | options: { 39 | esModule: ENABLE_ES_MODULE, 40 | // injectType: 'singletonStyleTag', 41 | }, 42 | }, 43 | { 44 | loader: "css-loader", 45 | options: { 46 | sourceMap: ENABLE_SOURCE_MAP, 47 | esModule: ENABLE_PREVIOUS_ES_MODULE, 48 | }, 49 | }, 50 | ], 51 | }, 52 | { 53 | test: /\.module\.css$/i, 54 | exclude: [ 55 | /\.lazy\.css$/i, 56 | /\.link\.css$/i, 57 | /\.lazy\.module\.css$/i, 58 | /\.named-export\.module\.css$/i, 59 | /\.named-export\.lazy\.module\.css$/i, 60 | ], 61 | use: [ 62 | { 63 | loader: require.resolve("../../dist/cjs.js"), 64 | options: { esModule: ENABLE_ES_MODULE }, 65 | }, 66 | { 67 | loader: "css-loader", 68 | options: { 69 | sourceMap: ENABLE_SOURCE_MAP, 70 | esModule: ENABLE_PREVIOUS_ES_MODULE, 71 | modules: true, 72 | }, 73 | }, 74 | ], 75 | }, 76 | { 77 | test: /\.lazy\.css$/i, 78 | use: [ 79 | { 80 | loader: require.resolve("../../dist/cjs.js"), 81 | options: { injectType: "lazyStyleTag", esModule: ENABLE_ES_MODULE }, 82 | }, 83 | { 84 | loader: "css-loader", 85 | options: { 86 | sourceMap: ENABLE_SOURCE_MAP, 87 | esModule: ENABLE_PREVIOUS_ES_MODULE, 88 | }, 89 | }, 90 | ], 91 | }, 92 | { 93 | test: /\.custom\.css$/i, 94 | use: [ 95 | { 96 | loader: require.resolve("../../dist/cjs.js"), 97 | options: { 98 | injectType: "lazyStyleTag", 99 | esModule: ENABLE_ES_MODULE, 100 | insert: function insert(element, options) { 101 | // eslint-disable-next-line 102 | var parent = options.target || document.head; 103 | 104 | parent.appendChild(element); 105 | }, 106 | }, 107 | }, 108 | { 109 | loader: "css-loader", 110 | options: { 111 | sourceMap: ENABLE_SOURCE_MAP, 112 | esModule: ENABLE_PREVIOUS_ES_MODULE, 113 | }, 114 | }, 115 | ], 116 | }, 117 | 118 | { 119 | test: /\.lazy\.module\.css$/i, 120 | exclude: [/\.named-export\.lazy\.module\.css$/i], 121 | use: [ 122 | { 123 | loader: require.resolve("../../dist/cjs.js"), 124 | options: { injectType: "lazyStyleTag", esModule: ENABLE_ES_MODULE }, 125 | }, 126 | { 127 | loader: "css-loader", 128 | options: { 129 | sourceMap: ENABLE_SOURCE_MAP, 130 | esModule: ENABLE_PREVIOUS_ES_MODULE, 131 | modules: true, 132 | }, 133 | }, 134 | ], 135 | }, 136 | { 137 | test: /\.link\.css$/i, 138 | use: [ 139 | { 140 | loader: require.resolve("../../dist/cjs.js"), 141 | options: { injectType: "linkTag", esModule: ENABLE_ES_MODULE }, 142 | }, 143 | { 144 | loader: "file-loader", 145 | options: { esModule: ENABLE_PREVIOUS_ES_MODULE }, 146 | }, 147 | ], 148 | }, 149 | { 150 | test: /\.scss$/i, 151 | exclude: /\.lazy\.scss$/i, 152 | use: [ 153 | { 154 | loader: require.resolve("../../dist/cjs.js"), 155 | options: { esModule: ENABLE_ES_MODULE }, 156 | }, 157 | { 158 | loader: "css-loader", 159 | options: { 160 | sourceMap: ENABLE_SOURCE_MAP, 161 | esModule: ENABLE_PREVIOUS_ES_MODULE, 162 | }, 163 | }, 164 | { 165 | loader: "sass-loader", 166 | options: { 167 | implementation: require("sass"), 168 | sourceMap: ENABLE_SOURCE_MAP, 169 | }, 170 | }, 171 | ], 172 | }, 173 | { 174 | test: /\.lazy\.scss$/i, 175 | use: [ 176 | { 177 | loader: require.resolve("../../dist/cjs.js"), 178 | options: { injectType: "lazyStyleTag", esModule: ENABLE_ES_MODULE }, 179 | }, 180 | { 181 | loader: "css-loader", 182 | options: { 183 | sourceMap: ENABLE_SOURCE_MAP, 184 | esModule: ENABLE_PREVIOUS_ES_MODULE, 185 | }, 186 | }, 187 | { 188 | loader: "sass-loader", 189 | options: { 190 | implementation: require("sass"), 191 | sourceMap: ENABLE_SOURCE_MAP, 192 | }, 193 | }, 194 | ], 195 | }, 196 | { 197 | test: /\.named-export\.module\.css$/i, 198 | use: [ 199 | { 200 | loader: require.resolve("../../dist/cjs.js"), 201 | options: { 202 | esModule: true, 203 | }, 204 | }, 205 | { 206 | loader: "css-loader", 207 | options: { 208 | sourceMap: ENABLE_SOURCE_MAP, 209 | esModule: true, 210 | modules: { 211 | namedExport: true, 212 | }, 213 | }, 214 | }, 215 | ], 216 | }, 217 | { 218 | test: /\.named-export\.lazy\.module\.css$/i, 219 | use: [ 220 | { 221 | loader: require.resolve("../../dist/cjs.js"), 222 | options: { 223 | injectType: "lazyStyleTag", 224 | esModule: true, 225 | }, 226 | }, 227 | { 228 | loader: "css-loader", 229 | options: { 230 | sourceMap: ENABLE_SOURCE_MAP, 231 | esModule: true, 232 | modules: { 233 | namedExport: true, 234 | }, 235 | }, 236 | }, 237 | ], 238 | }, 239 | { 240 | test: /\.png$/i, 241 | type: "asset/resource", 242 | }, 243 | ], 244 | }, 245 | devServer: { 246 | hot: true, 247 | liveReload: false, 248 | static: __dirname, 249 | }, 250 | }; 251 | -------------------------------------------------------------------------------- /test/runtime/injectStylesIntoLinkTag.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | 5 | /* global document */ 6 | 7 | import injectStylesIntoLinkTag from "../../src/runtime/injectStylesIntoLinkTag"; 8 | 9 | import insertBySelector from "../../src/runtime/insertBySelector"; 10 | 11 | const getInsertFn = (place) => insertBySelector.bind(null, place); 12 | 13 | function insertAtTop(element) { 14 | const parent = document.querySelector("head"); 15 | 16 | const lastInsertedElement = globalThis._lastElementInsertedByStyleLoader; 17 | 18 | if (!lastInsertedElement) { 19 | parent.insertBefore(element, parent.firstChild); 20 | } else if (lastInsertedElement.nextSibling) { 21 | parent.insertBefore(element, lastInsertedElement.nextSibling); 22 | } else { 23 | parent.appendChild(element); 24 | } 25 | 26 | globalThis._lastElementInsertedByStyleLoader = element; 27 | } 28 | 29 | function insertBeforeAt(element) { 30 | const parent = document.querySelector("head"); 31 | const target = document.querySelector("#id"); 32 | 33 | const lastInsertedElement = globalThis._lastElementInsertedByStyleLoader; 34 | 35 | if (!lastInsertedElement) { 36 | parent.insertBefore(element, target); 37 | } else if (lastInsertedElement.nextSibling) { 38 | parent.insertBefore(element, lastInsertedElement.nextSibling); 39 | } else { 40 | parent.appendChild(element); 41 | } 42 | 43 | globalThis._lastElementInsertedByStyleLoader = element; 44 | } 45 | 46 | describe("addStyle", () => { 47 | let defaultOptions; 48 | 49 | beforeEach(() => { 50 | document.head.innerHTML = "Title"; 51 | document.body.innerHTML = "

Hello world

"; 52 | 53 | defaultOptions = { 54 | insert: getInsertFn("head"), 55 | }; 56 | }); 57 | 58 | // Each query should have be unique because style-loader caching styles in dom 59 | 60 | it("should work", () => { 61 | injectStylesIntoLinkTag("./style-1.css", defaultOptions); 62 | 63 | expect(document.documentElement.innerHTML).toMatchSnapshot(); 64 | }); 65 | 66 | it('should work with "attributes" option', () => { 67 | injectStylesIntoLinkTag("./style-2.css", { 68 | ...defaultOptions, 69 | attributes: { foo: "bar" }, 70 | }); 71 | 72 | expect(document.documentElement.innerHTML).toMatchSnapshot(); 73 | }); 74 | 75 | it('should work with "__webpack_nonce__" variable', () => { 76 | globalThis.__webpack_nonce__ = "12345678"; 77 | 78 | injectStylesIntoLinkTag("./style-3.css", defaultOptions); 79 | 80 | expect(document.documentElement.innerHTML).toMatchSnapshot(); 81 | 82 | globalThis.__webpack_nonce__ = undefined; 83 | }); 84 | 85 | it('should work with "nonce" attribute and "__webpack_nonce__" variable', () => { 86 | globalThis.__webpack_nonce__ = "12345678"; 87 | 88 | injectStylesIntoLinkTag("./style-4.css", { 89 | ...defaultOptions, 90 | attributes: { nonce: "87654321" }, 91 | }); 92 | 93 | expect(document.documentElement.innerHTML).toMatchSnapshot(); 94 | 95 | globalThis.__webpack_nonce__ = undefined; 96 | }); 97 | 98 | it('should work with "insert" option', () => { 99 | injectStylesIntoLinkTag("./style-5.css", { 100 | ...defaultOptions, 101 | insert: getInsertFn("head"), 102 | }); 103 | 104 | expect(document.documentElement.innerHTML).toMatchSnapshot(); 105 | }); 106 | 107 | it('should work with "insert" option #2', () => { 108 | injectStylesIntoLinkTag("./style-6.css", { 109 | ...defaultOptions, 110 | insert: getInsertFn("body"), 111 | }); 112 | 113 | expect(document.documentElement.innerHTML).toMatchSnapshot(); 114 | }); 115 | 116 | it('should work with "insert" option #3', () => { 117 | document.body.innerHTML = 118 | "

Hello world

18 | 19 | 20 | " 21 | `; 22 | 23 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "lazySingletonStyleTag": errors 1`] = `[]`; 24 | 25 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "lazySingletonStyleTag": warnings 1`] = `[]`; 26 | 27 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "lazyStyleTag": DOM 1`] = ` 28 | " 29 | style-loader test 30 | 31 | 38 | 39 |

Body

40 |
41 | 42 | 43 | 44 | " 45 | `; 46 | 47 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "lazyStyleTag": errors 1`] = `[]`; 48 | 49 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "lazyStyleTag": warnings 1`] = `[]`; 50 | 51 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "linkTag": DOM 1`] = ` 52 | " 53 | style-loader test 54 | 55 | 56 | 57 |

Body

58 |
59 | 60 | 61 | 62 | " 63 | `; 64 | 65 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "linkTag": errors 1`] = `[]`; 66 | 67 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "linkTag": warnings 1`] = `[]`; 68 | 69 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "singletonStyleTag": DOM 1`] = ` 70 | " 71 | style-loader test 72 | 73 | 80 | 81 |

Body

82 |
83 | 84 | 85 | 86 | " 87 | `; 88 | 89 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "singletonStyleTag": errors 1`] = `[]`; 90 | 91 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "singletonStyleTag": warnings 1`] = `[]`; 92 | 93 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "styleTag": DOM 1`] = ` 94 | " 95 | style-loader test 96 | 97 | 104 | 105 |

Body

106 |
107 | 108 | 109 | 110 | " 111 | `; 112 | 113 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "styleTag": errors 1`] = `[]`; 114 | 115 | exports[`"attributes" option should add attributes to tag when the "injectType" option is "styleTag": warnings 1`] = `[]`; 116 | 117 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazySingletonStyleTag" #2: DOM 1`] = ` 118 | " 119 | style-loader test 120 | 121 | 125 | 126 |

Body

127 |
128 | 129 | 130 | 131 | " 132 | `; 133 | 134 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazySingletonStyleTag" #2: errors 1`] = `[]`; 135 | 136 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazySingletonStyleTag" #2: warnings 1`] = `[]`; 137 | 138 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazySingletonStyleTag": DOM 1`] = ` 139 | " 140 | style-loader test 141 | 142 | 146 | 147 |

Body

148 |
149 | 150 | 151 | 152 | " 153 | `; 154 | 155 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazySingletonStyleTag": errors 1`] = `[]`; 156 | 157 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazySingletonStyleTag": warnings 1`] = `[]`; 158 | 159 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazyStyleTag" #2: DOM 1`] = ` 160 | " 161 | style-loader test 162 | 163 | 167 | 168 |

Body

169 |
170 | 171 | 172 | 173 | " 174 | `; 175 | 176 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazyStyleTag" #2: errors 1`] = `[]`; 177 | 178 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazyStyleTag" #2: warnings 1`] = `[]`; 179 | 180 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazyStyleTag": DOM 1`] = ` 181 | " 182 | style-loader test 183 | 184 | 188 | 189 |

Body

190 |
191 | 192 | 193 | 194 | " 195 | `; 196 | 197 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazyStyleTag": errors 1`] = `[]`; 198 | 199 | exports[`"attributes" option should add nonce attribute when "injectType" option is "lazyStyleTag": warnings 1`] = `[]`; 200 | 201 | exports[`"attributes" option should add nonce attribute when "injectType" option is "linkTag" #2: DOM 1`] = ` 202 | " 203 | style-loader test 204 | 205 | 206 | 207 |

Body

208 |
209 | 210 | 211 | 212 | " 213 | `; 214 | 215 | exports[`"attributes" option should add nonce attribute when "injectType" option is "linkTag" #2: errors 1`] = `[]`; 216 | 217 | exports[`"attributes" option should add nonce attribute when "injectType" option is "linkTag" #2: warnings 1`] = `[]`; 218 | 219 | exports[`"attributes" option should add nonce attribute when "injectType" option is "linkTag": DOM 1`] = ` 220 | " 221 | style-loader test 222 | 223 | 224 | 225 |

Body

226 |
227 | 228 | 229 | 230 | " 231 | `; 232 | 233 | exports[`"attributes" option should add nonce attribute when "injectType" option is "linkTag": errors 1`] = `[]`; 234 | 235 | exports[`"attributes" option should add nonce attribute when "injectType" option is "linkTag": warnings 1`] = `[]`; 236 | 237 | exports[`"attributes" option should add nonce attribute when "injectType" option is "singletonStyleTag" #2: DOM 1`] = ` 238 | " 239 | style-loader test 240 | 241 | 245 | 246 |

Body

247 |
248 | 249 | 250 | 251 | " 252 | `; 253 | 254 | exports[`"attributes" option should add nonce attribute when "injectType" option is "singletonStyleTag" #2: errors 1`] = `[]`; 255 | 256 | exports[`"attributes" option should add nonce attribute when "injectType" option is "singletonStyleTag" #2: warnings 1`] = `[]`; 257 | 258 | exports[`"attributes" option should add nonce attribute when "injectType" option is "singletonStyleTag": DOM 1`] = ` 259 | " 260 | style-loader test 261 | 262 | 266 | 267 |

Body

268 |
269 | 270 | 271 | 272 | " 273 | `; 274 | 275 | exports[`"attributes" option should add nonce attribute when "injectType" option is "singletonStyleTag": errors 1`] = `[]`; 276 | 277 | exports[`"attributes" option should add nonce attribute when "injectType" option is "singletonStyleTag": warnings 1`] = `[]`; 278 | 279 | exports[`"attributes" option should add nonce attribute when "injectType" option is "styleTag" #2: DOM 1`] = ` 280 | " 281 | style-loader test 282 | 283 | 287 | 288 |

Body

289 |
290 | 291 | 292 | 293 | " 294 | `; 295 | 296 | exports[`"attributes" option should add nonce attribute when "injectType" option is "styleTag" #2: errors 1`] = `[]`; 297 | 298 | exports[`"attributes" option should add nonce attribute when "injectType" option is "styleTag" #2: warnings 1`] = `[]`; 299 | 300 | exports[`"attributes" option should add nonce attribute when "injectType" option is "styleTag": DOM 1`] = ` 301 | " 302 | style-loader test 303 | 304 | 308 | 309 |

Body

310 |
311 | 312 | 313 | 314 | " 315 | `; 316 | 317 | exports[`"attributes" option should add nonce attribute when "injectType" option is "styleTag": errors 1`] = `[]`; 318 | 319 | exports[`"attributes" option should add nonce attribute when "injectType" option is "styleTag": warnings 1`] = `[]`; 320 | 321 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "lazySingletonStyleTag": DOM 1`] = ` 322 | " 323 | style-loader test 324 | 325 | 332 | 333 |

Body

334 |
335 | 336 | 337 | 338 | " 339 | `; 340 | 341 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "lazySingletonStyleTag": errors 1`] = `[]`; 342 | 343 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "lazySingletonStyleTag": warnings 1`] = `[]`; 344 | 345 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "lazyStyleTag": DOM 1`] = ` 346 | " 347 | style-loader test 348 | 349 | 356 | 357 |

Body

358 |
359 | 360 | 361 | 362 | " 363 | `; 364 | 365 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "lazyStyleTag": errors 1`] = `[]`; 366 | 367 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "lazyStyleTag": warnings 1`] = `[]`; 368 | 369 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "linkTag": DOM 1`] = ` 370 | " 371 | style-loader test 372 | 373 | 374 | 375 |

Body

376 |
377 | 378 | 379 | 380 | " 381 | `; 382 | 383 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "linkTag": errors 1`] = `[]`; 384 | 385 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "linkTag": warnings 1`] = `[]`; 386 | 387 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "singletonStyleTag": DOM 1`] = ` 388 | " 389 | style-loader test 390 | 391 | 398 | 399 |

Body

400 |
401 | 402 | 403 | 404 | " 405 | `; 406 | 407 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "singletonStyleTag": errors 1`] = `[]`; 408 | 409 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "singletonStyleTag": warnings 1`] = `[]`; 410 | 411 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "styleTag": DOM 1`] = ` 412 | " 413 | style-loader test 414 | 415 | 422 | 423 |

Body

424 |
425 | 426 | 427 | 428 | " 429 | `; 430 | 431 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "styleTag": errors 1`] = `[]`; 432 | 433 | exports[`"attributes" option should apply nonce attribute when "injectType" option is "styleTag": warnings 1`] = `[]`; 434 | -------------------------------------------------------------------------------- /test/runtime/__snapshots__/injectStylesIntoStyleTag.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing 2 | 3 | exports[`addStyle issue 447 #2 1`] = `"Title

Hello world

"`; 4 | 5 | exports[`addStyle issue 447 #2 2`] = `"Title

Hello world

"`; 6 | 7 | exports[`addStyle issue 447 1`] = `"Title

Hello world

"`; 8 | 9 | exports[`addStyle should avoid duplicate modules 1`] = `"Title

Hello world

"`; 10 | 11 | exports[`addStyle should throw error with incorrect "insert" option 1`] = `"Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid."`; 12 | 13 | exports[`addStyle should throw error with invalid "insert" option 1`] = `"'#test><><><' is not a valid selector"`; 14 | 15 | exports[`addStyle should work 1`] = `"Title

Hello world

"`; 16 | 17 | exports[`addStyle should work with "__webpack_nonce__" variable 1`] = `"Title

Hello world

"`; 18 | 19 | exports[`addStyle should work with "attributes" option #2 1`] = `"Title

Hello world

"`; 20 | 21 | exports[`addStyle should work with "attributes" option 1`] = `"Title

Hello world

"`; 22 | 23 | exports[`addStyle should work with "base" option 1`] = `"Title

Hello world

"`; 24 | 25 | exports[`addStyle should work with "insert" option #2 1`] = `"Title

Hello world

"`; 26 | 27 | exports[`addStyle should work with "insert" option #3 1`] = `"Title

Hello world

"`; 28 | 29 | exports[`addStyle should work with "insert" option #3 2`] = `""`; 30 | 31 | exports[`addStyle should work with "insert" option #4 1`] = `"Title

Hello world

"`; 32 | 33 | exports[`addStyle should work with "insert" option #5 1`] = `"Title

Hello world

"`; 34 | 35 | exports[`addStyle should work with "insert" option #6 1`] = `"Title

Hello world

"`; 36 | 37 | exports[`addStyle should work with "insert" option 1`] = `"Title

Hello world

"`; 38 | 39 | exports[`addStyle should work with "nonce" attribute and "__webpack_nonce__" variable 1`] = `"Title

Hello world

"`; 40 | 41 | exports[`addStyle should work with empty layer 1`] = `"Title

Hello world

"`; 42 | 43 | exports[`addStyle should work with empty modules list #2 1`] = `"Title

Hello world

"`; 44 | 45 | exports[`addStyle should work with empty modules list #2 2`] = `"Title

Hello world

"`; 46 | 47 | exports[`addStyle should work with empty modules list #2 3`] = `"Title

Hello world

"`; 48 | 49 | exports[`addStyle should work with empty modules list #3 1`] = `"Title

Hello world

"`; 50 | 51 | exports[`addStyle should work with empty modules list #3 2`] = `"Title

Hello world

"`; 52 | 53 | exports[`addStyle should work with empty modules list #3 3`] = `"Title

Hello world

"`; 54 | 55 | exports[`addStyle should work with empty modules list #3 4`] = `"Title

Hello world

"`; 56 | 57 | exports[`addStyle should work with empty modules list 1`] = `"Title

Hello world

"`; 58 | 59 | exports[`addStyle should work with empty modules list 2`] = `"Title

Hello world

"`; 60 | 61 | exports[`addStyle should work with layer 1`] = `"Title

Hello world

"`; 62 | 63 | exports[`addStyle should work with media 1`] = `"Title

Hello world

"`; 64 | 65 | exports[`addStyle should work with multiple styles 1`] = `"Title

Hello world

"`; 66 | 67 | exports[`addStyle should work with same module id in list 1`] = `"Title

Hello world

"`; 68 | 69 | exports[`addStyle should work with source maps 1`] = ` 70 | "Title

Hello world

" 72 | `; 73 | 74 | exports[`addStyle should work with supports 1`] = `"Title

Hello world

"`; 75 | 76 | exports[`addStyle should work with updates #2 1`] = `"Title

Hello world

"`; 77 | 78 | exports[`addStyle should work with updates #2 2`] = `"Title

Hello world

"`; 79 | 80 | exports[`addStyle should work with updates #3 1`] = `"Title

Hello world

"`; 81 | 82 | exports[`addStyle should work with updates #3 2`] = `"Title

Hello world

"`; 83 | 84 | exports[`addStyle should work with updates #4 1`] = `"Title

Hello world

"`; 85 | 86 | exports[`addStyle should work with updates #4 2`] = `"Title

Hello world

"`; 87 | 88 | exports[`addStyle should work with updates #5 1`] = `"Title

Hello world

"`; 89 | 90 | exports[`addStyle should work with updates #5 2`] = `"Title

Hello world

"`; 91 | 92 | exports[`addStyle should work with updates #6 1`] = `"Title

Hello world

"`; 93 | 94 | exports[`addStyle should work with updates #6 2`] = `"Title

Hello world

"`; 95 | 96 | exports[`addStyle should work with updates #6 3`] = `"Title

Hello world

"`; 97 | 98 | exports[`addStyle should work with updates #6 4`] = `"Title

Hello world

"`; 99 | 100 | exports[`addStyle should work with updates #6 5`] = `"Title

Hello world

"`; 101 | 102 | exports[`addStyle should work with updates #6 6`] = `"Title

Hello world

"`; 103 | 104 | exports[`addStyle should work with updates #6 7`] = `"Title

Hello world

"`; 105 | 106 | exports[`addStyle should work with updates #7 1`] = `"Title

Hello world

"`; 107 | 108 | exports[`addStyle should work with updates #7 2`] = `"Title

Hello world

"`; 109 | 110 | exports[`addStyle should work with updates #8 1`] = ` 111 | "Title

Hello world

" 114 | `; 115 | 116 | exports[`addStyle should work with updates #8 2`] = ` 117 | "Title

Hello world

" 120 | `; 121 | 122 | exports[`addStyle should work with updates #8 3`] = `"Title

Hello world

"`; 123 | 124 | exports[`addStyle should work with updates #9 1`] = `"Title

Hello world

"`; 125 | 126 | exports[`addStyle should work with updates #9 2`] = `"Title

Hello world

"`; 127 | 128 | exports[`addStyle should work with updates #10 1`] = `"Title

Hello world

"`; 129 | 130 | exports[`addStyle should work with updates #10 2`] = `"Title

Hello world

"`; 131 | 132 | exports[`addStyle should work with updates #11 1`] = `"Title

Hello world

"`; 133 | 134 | exports[`addStyle should work with updates #11 2`] = `"Title

Hello world

"`; 135 | 136 | exports[`addStyle should work with updates #12 1`] = `"Title

Hello world

"`; 137 | 138 | exports[`addStyle should work with updates #12 2`] = `"Title

Hello world

"`; 139 | 140 | exports[`addStyle should work with updates #12 duplicate 1`] = `"Title

Hello world

"`; 141 | 142 | exports[`addStyle should work with updates #12 duplicate 2`] = `"Title

Hello world

"`; 143 | 144 | exports[`addStyle should work with updates #13 1`] = `"Title

Hello world

"`; 145 | 146 | exports[`addStyle should work with updates #13 2`] = `"Title

Hello world

"`; 147 | 148 | exports[`addStyle should work with updates #13 3`] = `"Title

Hello world

"`; 149 | 150 | exports[`addStyle should work with updates #13 4`] = `"Title

Hello world

"`; 151 | 152 | exports[`addStyle should work with updates #13 5`] = `"Title

Hello world

"`; 153 | 154 | exports[`addStyle should work with updates #13 6`] = `"Title

Hello world

"`; 155 | 156 | exports[`addStyle should work with updates #13 7`] = `"Title

Hello world

"`; 157 | 158 | exports[`addStyle should work with updates #14 1`] = `"Title

Hello world

"`; 159 | 160 | exports[`addStyle should work with updates #14 2`] = `"Title

Hello world

"`; 161 | 162 | exports[`addStyle should work with updates #14 3`] = `"Title

Hello world

"`; 163 | 164 | exports[`addStyle should work with updates #14 4`] = `"Title

Hello world

"`; 165 | 166 | exports[`addStyle should work with updates #14 5`] = `"Title

Hello world

"`; 167 | 168 | exports[`addStyle should work with updates 1`] = `"Title

Hello world

"`; 169 | 170 | exports[`addStyle should work with updates 2`] = `"Title

Hello world

"`; 171 | 172 | exports[`addStyle should work without modules 1`] = `"Title

Hello world

"`; 173 | 174 | exports[`addStyle should work without modules 2`] = `"Title

Hello world

"`; 175 | --------------------------------------------------------------------------------