├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .gitattributes ├── .github ├── CODEOWNERS └── ISSUE_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .prettierignore ├── .prettierrc ├── .prettierrc.js ├── .snyk ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── babel.config.js ├── codecov.yml ├── index.d.ts ├── jest.config.js ├── lint-staged.config.js ├── package.json ├── renovate.json ├── src ├── CssDependency.js ├── cjs.js ├── hmr │ └── hotModuleReplacement.js ├── index.js ├── loader-options.json ├── loader.js └── plugin-options.json ├── test ├── HMR.test.js ├── TestCases.test.js ├── TestMemoryFS.test.js ├── __snapshots__ │ ├── HMR.test.js.snap │ ├── validate-loader-options.test.js.snap │ └── validate-plugin-options.test.js.snap ├── cases │ ├── at-import │ │ ├── a.css │ │ ├── aa.css │ │ ├── ab.css │ │ ├── ac.css │ │ ├── ad.css │ │ ├── ae.css │ │ ├── b.css │ │ ├── ba.css │ │ ├── bb.css │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── chunkFilename │ │ ├── async.css │ │ ├── expected │ │ │ ├── 1.async.css │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── commonjs-module-syntax │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── composes-async │ │ ├── async-1.css │ │ ├── async-2.css │ │ ├── expected │ │ │ ├── async-1.css │ │ │ └── dedupe.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── contenthash-multiple-entries │ │ ├── entryA.js │ │ ├── entryB.js │ │ ├── entryC.js │ │ ├── entryD.js │ │ ├── entryE.js │ │ ├── expected │ │ │ ├── 96236f7f51b351aabd20.css │ │ │ └── f22bc5a793a5a86ad253.css │ │ ├── styleA.css │ │ ├── styleB.css │ │ ├── styleC.css │ │ ├── styleD.css │ │ └── webpack.config.js │ ├── contenthash │ │ ├── expected │ │ │ ├── 1.main.c4d90d38e7a606ae4d4c.css │ │ │ └── 2.main.64d1032b1547f22458a7.css │ │ ├── index.js │ │ ├── style1.css │ │ ├── style2.css │ │ └── webpack.config.js │ ├── css-import │ │ ├── a.css │ │ ├── b.css │ │ ├── c.css │ │ ├── expected │ │ │ └── main.css │ │ ├── index.css │ │ └── webpack.config.js │ ├── default-options │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── es-module-syntax │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── filename-with-template │ │ ├── async.css │ │ ├── expected │ │ │ ├── async.css │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── filename-without-template │ │ ├── async.css │ │ ├── expected │ │ │ ├── 1.main.css │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── hmr │ │ ├── a.css │ │ ├── b.css │ │ ├── c.css │ │ ├── expected │ │ │ ├── webpack-4 │ │ │ │ ├── main.css │ │ │ │ └── main.js │ │ │ └── webpack-5 │ │ │ │ ├── main.css │ │ │ │ └── main.js │ │ ├── index.css │ │ └── webpack.config.js │ ├── ignoreOrder │ │ ├── e1.css │ │ ├── e2.css │ │ ├── expected │ │ │ └── styles.css │ │ ├── index.js │ │ ├── index2.js │ │ └── webpack.config.js │ ├── ignoreOrderFalse │ │ ├── e1.css │ │ ├── e2.css │ │ ├── e3.css │ │ ├── expected │ │ │ └── styles.css │ │ ├── index.js │ │ ├── index2.js │ │ ├── index3.js │ │ ├── warnings.js │ │ └── webpack.config.js │ ├── ignoreOrderFalseWithoutGoodChunks │ │ ├── e1.css │ │ ├── e2.css │ │ ├── e3.css │ │ ├── e4.css │ │ ├── expected │ │ │ └── styles.css │ │ ├── index.js │ │ ├── index2.js │ │ ├── index3.js │ │ ├── index4.js │ │ ├── warnings.js │ │ └── webpack.config.js │ ├── import │ │ ├── a.css │ │ ├── b.css │ │ ├── c.css │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── insert-function │ │ ├── expected │ │ │ ├── 1.css │ │ │ ├── 1.js │ │ │ └── main.js │ │ ├── index.html │ │ ├── src │ │ │ ├── index.js │ │ │ └── inject.css │ │ ├── style.css │ │ ├── webpack.config.e2e.js │ │ └── webpack.config.js │ ├── insert-string │ │ ├── expected │ │ │ ├── 1.css │ │ │ ├── 1.js │ │ │ └── main.js │ │ ├── index.html │ │ ├── src │ │ │ ├── index.js │ │ │ └── inject.css │ │ ├── webpack.config.e2e.js │ │ └── webpack.config.js │ ├── js-hash │ │ ├── expected │ │ │ ├── style.922798e08e96756adb4a.1.css │ │ │ └── style.fe78b7a6c50df391f00c.2.css │ │ ├── index.js │ │ ├── loader.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── moduleFilename │ │ ├── expected │ │ │ └── demo │ │ │ │ └── css │ │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── moduleFilenameMutableFilename │ │ ├── expected │ │ │ └── mutated.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── multiple-entry │ │ ├── a.css │ │ ├── async-one.js │ │ ├── async-two.js │ │ ├── b.css │ │ ├── c.css │ │ ├── d.css │ │ ├── expected │ │ │ ├── async-one.css │ │ │ ├── async-two.css │ │ │ ├── main-one.css │ │ │ └── main-two.css │ │ ├── index-one.js │ │ ├── index-two.js │ │ └── webpack.config.js │ ├── nested │ │ ├── a.css │ │ ├── b.css │ │ ├── component.css │ │ ├── component.js │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── no-source-map │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── publicpath-emptystring │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── react.svg │ │ ├── style.css │ │ └── webpack.config.js │ ├── publicpath-function │ │ ├── expected │ │ │ └── nested │ │ │ │ ├── again │ │ │ │ └── style.css │ │ │ │ └── style.css │ │ ├── nested │ │ │ ├── again │ │ │ │ └── style.css │ │ │ └── style.css │ │ ├── react.svg │ │ └── webpack.config.js │ ├── publicpath-trailing-slash │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── react.svg │ │ ├── style.css │ │ └── webpack.config.js │ ├── shared-import │ │ ├── a.css │ │ ├── b.css │ │ ├── c.css │ │ ├── expected │ │ │ ├── 1.css │ │ │ └── main.css │ │ ├── index.js │ │ ├── shared.css │ │ └── webpack.config.js │ ├── simple-async-load-css-fallback │ │ ├── a.css │ │ ├── async-one.js │ │ ├── async-two.js │ │ ├── b.css │ │ ├── c.css │ │ ├── d.css │ │ ├── e.css │ │ ├── expected │ │ │ ├── async-one.css │ │ │ ├── async-two.css │ │ │ └── main.css │ │ ├── f.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── simple-async-load-css │ │ ├── a.css │ │ ├── async-one.js │ │ ├── async-two.js │ │ ├── b.css │ │ ├── c.css │ │ ├── d.css │ │ ├── e.css │ │ ├── expected │ │ │ ├── async-one.css │ │ │ ├── async-two.css │ │ │ └── main.css │ │ ├── f.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── simple-async-source-map │ │ ├── async.css │ │ ├── async.js │ │ ├── expected │ │ │ ├── 1.css │ │ │ ├── 1.css.map │ │ │ ├── 2.css │ │ │ ├── 2.css.map │ │ │ ├── main.css │ │ │ └── main.css.map │ │ ├── in-async.css │ │ ├── index.js │ │ ├── main.css │ │ └── webpack.config.js │ ├── simple-async │ │ ├── async.css │ │ ├── async.js │ │ ├── expected │ │ │ ├── 1.css │ │ │ ├── 2.css │ │ │ └── main.css │ │ ├── in-async.css │ │ ├── index.js │ │ ├── main.css │ │ └── webpack.config.js │ ├── simple-commonjs-syntax │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── simple-css-modules-mode-global │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── simple-css-modules-mode-local │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── simple-css-modules-mode-pure │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── simple-es-module-syntax │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── simple-multiple │ │ ├── a.css │ │ ├── b.css │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── simple-publicpath │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── react.svg │ │ ├── style.css │ │ └── webpack.config.js │ ├── simple │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── source-map │ │ ├── expected │ │ │ └── main.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js │ ├── split-chunks-single │ │ ├── a.css │ │ ├── b.css │ │ ├── c.css │ │ ├── chunk1.js │ │ ├── chunk2.js │ │ ├── d.css │ │ ├── e1.css │ │ ├── e2.css │ │ ├── entry1.js │ │ ├── entry2.js │ │ ├── expected │ │ │ └── styles.css │ │ ├── f.css │ │ ├── g.css │ │ ├── h.css │ │ ├── style.css │ │ └── webpack.config.js │ └── split-chunks │ │ ├── expected │ │ ├── main.css │ │ └── vendors.css │ │ ├── index.js │ │ ├── style.css │ │ └── webpack.config.js ├── cjs.test.js ├── fixtures │ ├── simple.css │ └── simple.js ├── helpers │ ├── compile.js │ ├── getCompiler.js │ └── index.js ├── ignoreOrder-option.test.js ├── inject-option.test.js ├── manual │ ├── README.md │ ├── index.html │ ├── src │ │ ├── crossorigin.css │ │ ├── crossorigin.js │ │ ├── index.js │ │ ├── initial.css │ │ ├── lazy-failure.css │ │ ├── lazy-failure.js │ │ ├── lazy.css │ │ ├── lazy.js │ │ ├── lazy2.css │ │ ├── preloaded1.css │ │ ├── preloaded1.js │ │ ├── preloaded2.css │ │ ├── preloaded2.js │ │ ├── simple.css │ │ └── simple.module.css │ └── webpack.config.js ├── validate-loader-options.test.js └── validate-plugin-options.test.js └── yarn.lock /.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 | insert_final_newline = true 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /dist 3 | /node_modules 4 | /test/fixtures 5 | /test/cases/*/expected 6 | /test/js 7 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: 'babel-eslint', 4 | extends: ['@webpack-contrib/eslint-config-webpack', 'prettier'], 5 | }; 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json -diff 2 | * text=auto 3 | test/cases/* eol=lf 4 | bin/* eol=lf 5 | yarn.lock -diff 6 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # extract-css-chunks-webpack-plugin maintainers 2 | * @ScriptedAlchemy 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 1. Check the version of package you are using. If it's not the newest version, update and try again (see changelog while updating!). 2 | 2. If the issue is still there, write a minimal project showing the problem and expected output. 3 | 3. Link to the project and mention Node version and OS in your report. 4 | 5 | **IMPORTANT! You should use [Stack Overflow](https://stackoverflow.com/) for support related questions.** 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | logs 2 | *.log 3 | npm-debug.log* 4 | .eslintcache 5 | /coverage 6 | /dist 7 | /test/js 8 | /local 9 | /reports 10 | /node_modules 11 | .DS_Store 12 | Thumbs.db 13 | .idea 14 | .vscode 15 | *.sublime-project 16 | *.sublime-workspace 17 | .idea 18 | *.iml 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example/ 2 | test/ 3 | .gitattributes 4 | .git 5 | .github 6 | coverage/ 7 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /coverage 2 | /dist 3 | /node_modules 4 | /test/fixtures 5 | /test/cases/*/expected 6 | /test/js 7 | CHANGELOG.md 8 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "arrowParens": "always" 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | singleQuote: true, 3 | trailingComma: 'es5', 4 | arrowParens: 'always', 5 | }; 6 | -------------------------------------------------------------------------------- /.snyk: -------------------------------------------------------------------------------- 1 | # Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. 2 | version: v1.13.5 3 | ignore: {} 4 | # patches apply the minimum changes required to fix a vulnerability 5 | patch: 6 | SNYK-JS-LODASH-450202: 7 | - webpack-external-import > babel-traverse > lodash: 8 | patched: '2019-07-04T06:20:01.150Z' 9 | - webpack-external-import > @babel/helper-module-imports > @babel/types > lodash: 10 | patched: '2019-07-04T06:20:01.150Z' 11 | - webpack-external-import > babel-traverse > babel-types > lodash: 12 | patched: '2019-07-04T06:20:01.150Z' 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - v10 5 | before_script: 6 | - nvm use v8 7 | - yarn --ignore-engines 8 | script: 9 | - yarn lint 10 | - yarn test 11 | after_success: 12 | - npm run semantic-release 13 | branches: 14 | except: 15 | - /^v\d+\.\d+\.\d+$/ 16 | notifications: 17 | email: false 18 | webhooks: 19 | urls: 20 | - https://webhooks.gitter.im/e/5156be73e058008e1ed2 21 | on_success: always # options: [always|never|change] default: always 22 | on_failure: always # options: [always|never|change] default: always 23 | on_start: never # options: [always|never|change] default: always 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [4.9.0](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/compare/v4.8.0...v4.9.0) (2020-12-07) 6 | 7 | 8 | ### Features 9 | 10 | * adding federated stats ([#303](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/303)) ([c17d755](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/commit/c17d75547a5efb5971d03555163ea4d685080706)) 11 | * adding federated stats ([#304](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/304)) ([1eab2d0](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/commit/1eab2d0b8868837de8f89f68c1ad14be741f3e78)) 12 | 13 | 14 | ### Bug Fixes 15 | 16 | * Check if bestMatch exists before pop() ([#294](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/issues/294)) ([ef82da9](https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/commit/ef82da9068bd699344774d39f61b7ead8cf682cb)) 17 | 18 | ### [0.9.0](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.8.2...v0.9.0) (2019-12-20) 19 | 20 | 21 | ### Features 22 | 23 | * new `esModule` option ([#475](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/475)) ([596e47a](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/596e47a8aead53f9cc0e2b1e09a2c20e455e45c1)) 24 | 25 | ### [0.8.2](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.8.1...v0.8.2) (2019-12-17) 26 | 27 | 28 | ### Bug Fixes 29 | 30 | * context for dependencies ([#474](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/474)) ([0269860](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/0269860adb0eaad477901188eea66693fedf7769)) 31 | 32 | ### [0.8.1](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.8.0...v0.8.1) (2019-12-17) 33 | 34 | 35 | ### Bug Fixes 36 | 37 | * use filename mutated after instantiation ([#430](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/430)) ([0bacfac](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/0bacfac7ef4a06b4810fbc140875f7a038caa5bc)) 38 | * improve warning of conflict order ([#465](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/465)) ([357d073](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/357d073bf0259f2c44e613ad4dfcbcc8354e4be3)) 39 | * support ES module syntax ([#472](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/472)) ([2f72e1a](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/2f72e1aa267de23f121441714e88406f579e77b2)) 40 | 41 | ## [0.8.0](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.7.0...v0.8.0) (2019-07-16) 42 | 43 | 44 | ### Features 45 | 46 | * Add ignoreOrder option ([#422](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/422)) ([4ad3373](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/4ad3373)) 47 | 48 | 49 | 50 | ## [0.7.0](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.6.0...v0.7.0) (2019-05-27) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * do not attempt to reload unrequestable urls ([#378](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/378)) ([44d00ea](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/44d00ea)) 56 | * fix `publicPath` regression ([#384](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/384)) ([582ebfe](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/582ebfe)) 57 | * enable using plugin without defining options ([#393](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/393)) ([a7dee8c](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/a7dee8c)) 58 | * downgrading normalize-url ([#399](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/399)) ([0dafaf6](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/0dafaf6)) 59 | * hmr do not crash on link without href ([#400](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/400)) ([aa9b541](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/aa9b541)) 60 | * hmr reload with invalid link url ([#402](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/402)) ([30a19b0](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/30a19b0)) 61 | 62 | 63 | ### Features 64 | 65 | * add `moduleFilename` option ([#381](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/381)) ([13e9cbf](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/13e9cbf)) 66 | 67 | 68 | 69 | 70 | # [0.6.0](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.5.0...v0.6.0) (2019-04-10) 71 | 72 | 73 | ### Features 74 | 75 | * added error code to chunk load Error ([#347](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/347)) ([b653641](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/b653641)) 76 | * adding hot module reloading ([#334](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/334)) ([4ed9c5a](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/4ed9c5a)) 77 | * publicPath can be a function ([#373](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/373)) ([7b1425a](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/7b1425a)) 78 | 79 | 80 | 81 | 82 | # [0.5.0](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.4.5...v0.5.0) (2018-12-07) 83 | 84 | 85 | ### Features 86 | 87 | * add crossOriginLoading option support ([#313](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/313)) ([ffb0d87](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/ffb0d87)) 88 | 89 | 90 | 91 | 92 | ## [0.4.5](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.4.4...v0.4.5) (2018-11-21) 93 | 94 | 95 | ### Bug Fixes 96 | 97 | * **index:** allow requesting failed async css files ([#292](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/292)) ([2eb0af5](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/2eb0af5)) 98 | 99 | 100 | 101 | 102 | ## [0.4.4](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.4.3...v0.4.4) (2018-10-10) 103 | 104 | 105 | ### Bug Fixes 106 | 107 | * **index:** assign empty `module.id` to prevent `contenthash` from changing unnecessarily ([#284](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/284)) ([d7946d0](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/d7946d0)) 108 | 109 | 110 | 111 | 112 | ## [0.4.3](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.4.2...v0.4.3) (2018-09-18) 113 | 114 | 115 | ### Bug Fixes 116 | 117 | * **loader:** pass `emitFile` to the child compilation (`loaderContext.emitFile`) ([#177](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/177)) ([18c066e](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/18c066e)) 118 | 119 | 120 | 121 | 122 | ## [0.4.2](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.4.0...v0.4.2) (2018-08-21) 123 | 124 | 125 | ### Bug Fixes 126 | 127 | * use correct order when multiple chunk groups are merged ([#246](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/246)) ([c3b363d](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/c3b363d)) 128 | 129 | 130 | 131 | 132 | ## [0.4.1](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/compare/v0.4.0...v0.4.1) (2018-06-29) 133 | 134 | 135 | ### Bug Fixes 136 | 137 | * CSS ordering with multiple entry points ([#130](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/issues/130)) ([79373eb](https://github.com/webpack-contrib/extract-css-chunks-webpack-plugin/commit/79373eb)) 138 | 139 | 140 | 141 | # Change Log 142 | 143 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 144 | 145 | x.x.x / -- 146 | ================== 147 | 148 | * Bug fix - 149 | * Feature - 150 | * Chore - 151 | * Docs - 152 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors 2 | Copyright Zack Jackson 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | 'Software'), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /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: '6.9.0', 14 | }, 15 | }, 16 | ], 17 | ], 18 | overrides: [ 19 | { 20 | test: '**/hmr/**/*.js', 21 | presets: [ 22 | [ 23 | '@babel/preset-env', 24 | { 25 | targets: { 26 | node: '0.12', 27 | }, 28 | }, 29 | ], 30 | ], 31 | }, 32 | ], 33 | }; 34 | }; 35 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | branch: master 3 | coverage: 4 | precision: 2 5 | round: down 6 | range: 70...100 7 | status: 8 | project: 'no' 9 | patch: 'yes' 10 | comment: 'off' 11 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'extract-css-chunks-webpack-plugin' { 2 | import { ChunkData, Plugin } from 'webpack'; 3 | 4 | class ExtractCssChunksPlugin extends Plugin { 5 | static loader: string; 6 | 7 | constructor(options?: ExtractCssChunksPlugin.PluginOptions); 8 | } 9 | 10 | namespace ExtractCssChunksPlugin { 11 | interface PluginOptions { 12 | /** 13 | * The filename of the entry chunk. 14 | */ 15 | filename?: string; 16 | /** 17 | * The filename of non-entry chunks. 18 | */ 19 | chunkFilename?: string; 20 | /** 21 | * Generates a file name (or template) based on chunk data. 22 | */ 23 | moduleFilename?: (chunk: ChunkData) => string; 24 | /** 25 | * Remove warnings about conflicting order. 26 | */ 27 | ignoreOrder?: boolean; 28 | /** 29 | * Inserts `` at the given position (https://github.com/faceyspacey/extract-css-chunks-webpack-pluginn#insert). 30 | */ 31 | insert?: string | Function; 32 | } 33 | } 34 | 35 | export = ExtractCssChunksPlugin; 36 | } 37 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testURL: 'http://localhost/', 3 | preset: 'jest-puppeteer', 4 | transformIgnorePatterns: ['/node_modules/', '/dist/'], 5 | watchPathIgnorePatterns: ['/test/js'], 6 | }; 7 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.js': ['prettier --write', 'eslint --fix', 'git add'], 3 | '*.{json,md,yml,css}': ['prettier --write', 'git add'], 4 | }; 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extract-css-chunks-webpack-plugin", 3 | "version": "4.10.0", 4 | "description": "Extract CSS from chunks into stylesheets + HMR. Supports Webpack 4 + SSR", 5 | "license": "MIT", 6 | "author": "James Gillmore ", 7 | "contributors": [ 8 | "Zack Jackson (https://github.com/ScriptedAlchemy)" 9 | ], 10 | "homepage": "http://github.com/faceyspacey/extract-css-chunks-webpack-plugin", 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/faceyspacey/extract-css-chunks-webpack-plugin.git" 14 | }, 15 | "config": { 16 | "commitizen": { 17 | "path": "./node_modules/cz-conventional-changelog" 18 | } 19 | }, 20 | "keywords": [ 21 | "css", 22 | "chunks", 23 | "code splitting", 24 | "mini-css", 25 | "hot", 26 | "hmr", 27 | "universal", 28 | "webpack", 29 | "webpack 4", 30 | "css-hot-loader", 31 | "extract-css-chunks-webpack-plugin" 32 | ], 33 | "main": "dist/cjs.js", 34 | "types": "index.d.ts", 35 | "engines": { 36 | "node": ">= 6.9.0" 37 | }, 38 | "scripts": { 39 | "commit": "git-cz", 40 | "start": "npm run build -- -w", 41 | "prebuild": "npm run clean", 42 | "build": "cross-env NODE_ENV=production babel src -d dist --ignore \"src/**/*.test.js\" --copy-files", 43 | "postbuild": "es-check es5 dist/hmr/hotModuleReplacement.js", 44 | "clean": "del-cli dist", 45 | "commitlint": "commitlint --from=master", 46 | "lint:prettier": "prettier \"{**/*,*}.{js,json,md,yml,css}\" --list-different", 47 | "lint:js": "eslint --cache src test", 48 | "lint": "npm-run-all -l -p \"lint:js\" \"lint:prettier\"", 49 | "prepare": "npm run build", 50 | "release": "standard-version", 51 | "security": "npm audit", 52 | "test:only": "cross-env NODE_ENV=test jest --updateSnapshot", 53 | "test:watch": "cross-env NODE_ENV=test jest --watch", 54 | "test:coverage": "cross-env NODE_ENV=test jest --collectCoverageFrom=\"src/**/*.js\" --coverage", 55 | "test:manual": "npm run build && webpack-dev-server test/manual/src/index.js --open --config test/manual/webpack.config.js", 56 | "pretest": "npm run lint", 57 | "test": "cross-env NODE_ENV=test npm run test:coverage", 58 | "defaults": "webpack-defaults", 59 | "semantic-release": "npx semantic-release", 60 | "travis": "npm run ci:coverage" 61 | }, 62 | "files": [ 63 | "dist", 64 | "index.d.ts" 65 | ], 66 | "peerDependencies": { 67 | "webpack": "^4.4.0 || ^5.0.0" 68 | }, 69 | "dependencies": { 70 | "loader-utils": "^2.0.4", 71 | "normalize-url": "1.9.1", 72 | "schema-utils": "^1.0.0", 73 | "webpack-sources": "^1.1.0" 74 | }, 75 | "devDependencies": { 76 | "@babel/cli": "7.8.4", 77 | "@babel/core": "7.9.0", 78 | "@babel/preset-env": "7.9.5", 79 | "@commitlint/cli": "8.3.5", 80 | "@commitlint/config-conventional": "8.3.4", 81 | "@webpack-contrib/defaults": "6.3.0", 82 | "@webpack-contrib/eslint-config-webpack": "3.0.0", 83 | "babel-eslint": "10.1.0", 84 | "babel-jest": "25.5.1", 85 | "commitizen": "4.0.4", 86 | "commitlint-azure-pipelines-cli": "1.0.3", 87 | "cross-env": "7.0.2", 88 | "css-loader": "3.5.2", 89 | "cz-conventional-changelog": "3.1.0", 90 | "del": "5.1.0", 91 | "del-cli": "3.0.0", 92 | "es-check": "5.1.0", 93 | "eslint": "6.8.0", 94 | "eslint-config-prettier": "6.10.1", 95 | "eslint-plugin-import": "2.20.2", 96 | "eslint-plugin-prettier": "3.1.2", 97 | "file-loader": "6.0.0", 98 | "husky": "4.2.3", 99 | "jest": "25.5.4", 100 | "jest-dev-server": "4.4.0", 101 | "jest-junit": "10.0.0", 102 | "jest-puppeteer": "4.4.0", 103 | "lint-staged": "10.0.8", 104 | "memfs": "3.1.2", 105 | "npm-run-all": "4.1.5", 106 | "prerender-loader": "1.3.0", 107 | "prettier": "1.19.1", 108 | "puppeteer": "2.1.1", 109 | "serve": "11.3.0", 110 | "standard-version": "8.0.1", 111 | "webpack": "4.43.0", 112 | "webpack-cli": "3.3.11", 113 | "webpack-dev-server": "3.10.3" 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | ":combinePatchMinorReleases", 4 | ":ignoreUnstable", 5 | ":prImmediately", 6 | ":semanticPrefixFixDepsChoreOthers", 7 | ":updateNotScheduled", 8 | ":automergeDisabled", 9 | ":ignoreModulesAndTests", 10 | ":maintainLockFilesDisabled", 11 | ":autodetectPinVersions", 12 | ":prHourlyLimit4", 13 | ":prConcurrentLimit20", 14 | "group:monorepos", 15 | "group:recommended", 16 | "helpers:disableTypesNodeMajor", 17 | ":pinAllExceptPeerDependencies", 18 | ":pinOnlyDevDependencies" 19 | ], 20 | "packageRules": [ 21 | { 22 | "packageNames": ["normalize-url"], 23 | "allowedVersions": "1.9.1" 24 | }, 25 | { 26 | "packageNames": ["schema-utils"], 27 | "allowedVersions": "^1" 28 | }, 29 | { "updateTypes": ["minor", "patch", "pin", "digest"] } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /src/CssDependency.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | 3 | export default class CssDependency extends webpack.Dependency { 4 | constructor( 5 | { identifier, content, media, sourceMap }, 6 | context, 7 | identifierIndex 8 | ) { 9 | super(); 10 | 11 | this.identifier = identifier; 12 | this.identifierIndex = identifierIndex; 13 | this.content = content; 14 | this.media = media; 15 | this.sourceMap = sourceMap; 16 | this.context = context; 17 | } 18 | 19 | getResourceIdentifier() { 20 | return `css-module-${this.identifier}-${this.identifierIndex}`; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/cjs.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./index').default; 2 | -------------------------------------------------------------------------------- /src/hmr/hotModuleReplacement.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | /* 3 | eslint-disable 4 | no-console, 5 | func-names 6 | */ 7 | 8 | const normalizeUrl = require('normalize-url'); 9 | 10 | const srcByModuleId = Object.create(null); 11 | 12 | const noDocument = typeof document === 'undefined'; 13 | 14 | const { forEach } = Array.prototype; 15 | 16 | function debounce(fn, time) { 17 | let timeout = 0; 18 | 19 | return function() { 20 | const self = this; 21 | // eslint-disable-next-line prefer-rest-params 22 | const args = arguments; 23 | 24 | const functionCall = function functionCall() { 25 | return fn.apply(self, args); 26 | }; 27 | 28 | clearTimeout(timeout); 29 | timeout = setTimeout(functionCall, time); 30 | }; 31 | } 32 | 33 | function noop() {} 34 | 35 | function getCurrentScriptUrl(moduleId) { 36 | let src = srcByModuleId[moduleId]; 37 | 38 | if (!src) { 39 | if (document.currentScript) { 40 | ({ src } = document.currentScript); 41 | } else { 42 | const scripts = document.getElementsByTagName('script'); 43 | const lastScriptTag = scripts[scripts.length - 1]; 44 | 45 | if (lastScriptTag) { 46 | ({ src } = lastScriptTag); 47 | } 48 | } 49 | 50 | srcByModuleId[moduleId] = src; 51 | } 52 | 53 | return function(fileMap) { 54 | if (!src) { 55 | return null; 56 | } 57 | 58 | const splitResult = src.split(/([^\\/]+)\.js$/); 59 | const filename = splitResult && splitResult[1]; 60 | 61 | if (!filename) { 62 | return [src.replace('.js', '.css')]; 63 | } 64 | 65 | if (!fileMap) { 66 | return [src.replace('.js', '.css')]; 67 | } 68 | 69 | return fileMap.split(',').map((mapRule) => { 70 | const reg = new RegExp(`${filename}\\.js$`, 'g'); 71 | 72 | return normalizeUrl( 73 | src.replace(reg, `${mapRule.replace(/{fileName}/g, filename)}.css`), 74 | { stripWWW: false } 75 | ); 76 | }); 77 | }; 78 | } 79 | 80 | function updateCss(el, url) { 81 | if (!url) { 82 | if (!el.href) { 83 | return; 84 | } 85 | 86 | // eslint-disable-next-line 87 | url = el.href.split('?')[0]; 88 | } 89 | 90 | if (!isUrlRequest(url)) { 91 | return; 92 | } 93 | 94 | if (el.isLoaded === false) { 95 | // We seem to be about to replace a css link that hasn't loaded yet. 96 | // We're probably changing the same file more than once. 97 | return; 98 | } 99 | 100 | if (!url || !(url.indexOf('.css') > -1)) { 101 | return; 102 | } 103 | 104 | // eslint-disable-next-line no-param-reassign 105 | el.visited = true; 106 | 107 | const newEl = el.cloneNode(); 108 | 109 | newEl.isLoaded = false; 110 | 111 | newEl.addEventListener('load', () => { 112 | newEl.isLoaded = true; 113 | el.parentNode.removeChild(el); 114 | }); 115 | 116 | newEl.addEventListener('error', () => { 117 | newEl.isLoaded = true; 118 | el.parentNode.removeChild(el); 119 | }); 120 | 121 | newEl.href = `${url}?${Date.now()}`; 122 | 123 | if (el.nextSibling) { 124 | el.parentNode.insertBefore(newEl, el.nextSibling); 125 | } else { 126 | el.parentNode.appendChild(newEl); 127 | } 128 | } 129 | 130 | function getReloadUrl(href, src) { 131 | let ret; 132 | 133 | // eslint-disable-next-line no-param-reassign 134 | href = normalizeUrl(href, { stripWWW: false }); 135 | 136 | // eslint-disable-next-line array-callback-return 137 | src.some((url) => { 138 | if (href.indexOf(src) > -1) { 139 | ret = url; 140 | } 141 | }); 142 | 143 | return ret; 144 | } 145 | 146 | function reloadStyle(src) { 147 | const elements = document.querySelectorAll('link'); 148 | let loaded = false; 149 | 150 | forEach.call(elements, (el) => { 151 | if (!el.href) { 152 | return; 153 | } 154 | 155 | const url = getReloadUrl(el.href, src); 156 | 157 | if (!isUrlRequest(url)) { 158 | return; 159 | } 160 | 161 | if (el.visited === true) { 162 | return; 163 | } 164 | 165 | if (url) { 166 | updateCss(el, url); 167 | 168 | loaded = true; 169 | } 170 | }); 171 | 172 | return loaded; 173 | } 174 | 175 | function reloadAll() { 176 | const elements = document.querySelectorAll('link'); 177 | 178 | forEach.call(elements, (el) => { 179 | if (el.visited === true) { 180 | return; 181 | } 182 | 183 | updateCss(el); 184 | }); 185 | } 186 | 187 | function isUrlRequest(url) { 188 | // An URL is not an request if 189 | 190 | // It is not http or https 191 | if (!/^https?:/i.test(url)) { 192 | return false; 193 | } 194 | 195 | return true; 196 | } 197 | 198 | module.exports = function(moduleId, options) { 199 | if (noDocument) { 200 | console.log('no window.document found, will not HMR CSS'); 201 | 202 | return noop; 203 | } 204 | 205 | const getScriptSrc = getCurrentScriptUrl(moduleId); 206 | 207 | function update() { 208 | const src = getScriptSrc(options.filename); 209 | const reloaded = reloadStyle(src); 210 | 211 | if (options.locals) { 212 | console.log('[HMR] Detected local css modules. Reload all css'); 213 | 214 | reloadAll(); 215 | 216 | return; 217 | } 218 | 219 | if (reloaded && !options.reloadAll) { 220 | console.log('[HMR] css reload %s', src.join(' ')); 221 | } else { 222 | console.log('[HMR] Reload all css'); 223 | 224 | reloadAll(); 225 | } 226 | } 227 | 228 | return debounce(update, 50); 229 | }; 230 | -------------------------------------------------------------------------------- /src/loader-options.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": true, 4 | "properties": { 5 | "publicPath": { 6 | "anyOf": [ 7 | { 8 | "type": "string" 9 | }, 10 | { 11 | "instanceof": "Function" 12 | } 13 | ] 14 | }, 15 | "esModule": { 16 | "type": "boolean" 17 | }, 18 | "hmr": { 19 | "type": "boolean" 20 | }, 21 | "reloadAll": { 22 | "type": "boolean" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/loader.js: -------------------------------------------------------------------------------- 1 | import NativeModule from 'module'; 2 | 3 | import path from 'path'; 4 | 5 | import loaderUtils from 'loader-utils'; 6 | import NodeTemplatePlugin from 'webpack/lib/node/NodeTemplatePlugin'; 7 | import NodeTargetPlugin from 'webpack/lib/node/NodeTargetPlugin'; 8 | import LibraryTemplatePlugin from 'webpack/lib/LibraryTemplatePlugin'; 9 | import SingleEntryPlugin from 'webpack/lib/SingleEntryPlugin'; 10 | import LimitChunkCountPlugin from 'webpack/lib/optimize/LimitChunkCountPlugin'; 11 | import validateOptions from 'schema-utils'; 12 | 13 | import CssDependency from './CssDependency'; 14 | 15 | import schema from './loader-options.json'; 16 | 17 | const pluginName = 'extract-css-chunks-webpack-plugin'; 18 | 19 | function hotLoader(content, context) { 20 | const accept = context.locals 21 | ? '' 22 | : 'module.hot.accept(undefined, cssReload);'; 23 | 24 | return `${content} 25 | if(module.hot) { 26 | // ${Date.now()} 27 | var cssReload = require(${loaderUtils.stringifyRequest( 28 | context.context, 29 | path.join(__dirname, 'hmr/hotModuleReplacement.js') 30 | )})(module.id, ${JSON.stringify({ 31 | ...context.options, 32 | locals: !!context.locals, 33 | })}); 34 | module.hot.dispose(cssReload); 35 | ${accept} 36 | } 37 | `; 38 | } 39 | 40 | function evalModuleCode(loaderContext, code, filename) { 41 | const module = new NativeModule(filename, loaderContext); 42 | 43 | module.paths = NativeModule._nodeModulePaths(loaderContext.context); // eslint-disable-line no-underscore-dangle 44 | module.filename = filename; 45 | module._compile(code, filename); // eslint-disable-line no-underscore-dangle 46 | 47 | return module.exports; 48 | } 49 | 50 | function findModuleById(modules, id) { 51 | for (const module of modules) { 52 | if (module.id === id) { 53 | return module; 54 | } 55 | } 56 | 57 | return null; 58 | } 59 | 60 | export function pitch(request) { 61 | const options = loaderUtils.getOptions(this) || {}; 62 | 63 | validateOptions(schema, options, 'Mini CSS Extract Plugin Loader'); 64 | 65 | const loaders = this.loaders.slice(this.loaderIndex + 1); 66 | 67 | this.addDependency(this.resourcePath); 68 | 69 | const childFilename = '*'; 70 | const publicPath = 71 | typeof options.publicPath === 'string' 72 | ? options.publicPath === '' || options.publicPath.endsWith('/') 73 | ? options.publicPath 74 | : `${options.publicPath}/` 75 | : typeof options.publicPath === 'function' 76 | ? options.publicPath(this.resourcePath, this.rootContext) 77 | : this._compilation.outputOptions.publicPath; 78 | const outputOptions = { 79 | filename: childFilename, 80 | publicPath, 81 | }; 82 | const childCompiler = this._compilation.createChildCompiler( 83 | `${pluginName} ${request}`, 84 | outputOptions 85 | ); 86 | 87 | new NodeTemplatePlugin(outputOptions).apply(childCompiler); 88 | new LibraryTemplatePlugin(null, 'commonjs2').apply(childCompiler); 89 | new NodeTargetPlugin().apply(childCompiler); 90 | new SingleEntryPlugin(this.context, `!!${request}`, pluginName).apply( 91 | childCompiler 92 | ); 93 | new LimitChunkCountPlugin({ maxChunks: 1 }).apply(childCompiler); 94 | 95 | childCompiler.hooks.thisCompilation.tap( 96 | `${pluginName} loader`, 97 | (compilation) => { 98 | compilation.hooks.normalModuleLoader.tap( 99 | `${pluginName} loader`, 100 | (loaderContext, module) => { 101 | // eslint-disable-next-line no-param-reassign 102 | loaderContext.emitFile = this.emitFile; 103 | 104 | if (module.request === request) { 105 | // eslint-disable-next-line no-param-reassign 106 | module.loaders = loaders.map((loader) => { 107 | return { 108 | loader: loader.path, 109 | options: loader.options, 110 | ident: loader.ident, 111 | }; 112 | }); 113 | } 114 | } 115 | ); 116 | } 117 | ); 118 | 119 | let source; 120 | 121 | childCompiler.hooks.afterCompile.tap(pluginName, (compilation) => { 122 | source = 123 | compilation.assets[childFilename] && 124 | compilation.assets[childFilename].source(); 125 | 126 | // Remove all chunk assets 127 | compilation.chunks.forEach((chunk) => { 128 | chunk.files.forEach((file) => { 129 | delete compilation.assets[file]; // eslint-disable-line no-param-reassign 130 | }); 131 | }); 132 | }); 133 | 134 | const callback = this.async(); 135 | 136 | childCompiler.runAsChild((err, entries, compilation) => { 137 | const addDependencies = (dependencies) => { 138 | if (!Array.isArray(dependencies) && dependencies != null) { 139 | throw new Error( 140 | `Exported value was not extracted as an array: ${JSON.stringify( 141 | dependencies 142 | )}` 143 | ); 144 | } 145 | 146 | const identifierCountMap = new Map(); 147 | 148 | for (const dependency of dependencies) { 149 | const count = identifierCountMap.get(dependency.identifier) || 0; 150 | 151 | this._module.addDependency( 152 | new CssDependency(dependency, dependency.context, count) 153 | ); 154 | identifierCountMap.set(dependency.identifier, count + 1); 155 | } 156 | }; 157 | 158 | if (err) { 159 | return callback(err); 160 | } 161 | 162 | if (compilation.errors.length > 0) { 163 | return callback(compilation.errors[0]); 164 | } 165 | 166 | compilation.fileDependencies.forEach((dep) => { 167 | this.addDependency(dep); 168 | }, this); 169 | 170 | compilation.contextDependencies.forEach((dep) => { 171 | this.addContextDependency(dep); 172 | }, this); 173 | 174 | if (!source) { 175 | return callback(new Error("Didn't get a result from child compiler")); 176 | } 177 | 178 | let locals; 179 | 180 | try { 181 | let dependencies; 182 | let exports = evalModuleCode(this, source, request); 183 | // eslint-disable-next-line no-underscore-dangle 184 | exports = exports.__esModule ? exports.default : exports; 185 | locals = exports && exports.locals; 186 | if (!Array.isArray(exports)) { 187 | dependencies = [[null, exports]]; 188 | } else { 189 | dependencies = exports.map(([id, content, media, sourceMap]) => { 190 | const module = findModuleById(compilation.modules, id); 191 | 192 | return { 193 | identifier: module.identifier(), 194 | context: module.context, 195 | content, 196 | media, 197 | sourceMap, 198 | }; 199 | }); 200 | } 201 | addDependencies(dependencies); 202 | } catch (e) { 203 | return callback(e); 204 | } 205 | 206 | const esModule = 207 | typeof options.esModule !== 'undefined' ? options.esModule : false; 208 | const result = locals 209 | ? `\n${esModule ? 'export default' : 'module.exports ='} ${JSON.stringify( 210 | locals 211 | )};` 212 | : ''; 213 | 214 | let resultSource = `// extracted by ${pluginName}`; 215 | 216 | resultSource += options.hmr 217 | ? hotLoader(result, { context: this.context, options, locals }) 218 | : result; 219 | 220 | return callback(null, resultSource); 221 | }); 222 | } 223 | 224 | export default function() {} 225 | -------------------------------------------------------------------------------- /src/plugin-options.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": true, 4 | "properties": { 5 | "filename": { 6 | "type": "string" 7 | }, 8 | "chunkFilename": { 9 | "type": "string" 10 | }, 11 | "moduleFilename": { 12 | "instanceof": "Function" 13 | }, 14 | "ignoreOrder": { 15 | "type": "boolean" 16 | }, 17 | "insert": { 18 | "description": "Inserts `` at the given position (https://github.com/faceyspacey/extract-css-chunks-webpack-pluginn#insert).", 19 | "anyOf": [ 20 | { 21 | "type": "string" 22 | }, 23 | { 24 | "instanceof": "Function" 25 | } 26 | ] 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/HMR.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jest-environment jsdom 3 | */ 4 | /* eslint-env browser */ 5 | /* eslint-disable no-console */ 6 | 7 | import hotModuleReplacement from '../src/hmr/hotModuleReplacement'; 8 | 9 | function getLoadEvent() { 10 | const event = document.createEvent('Event'); 11 | 12 | event.initEvent('load', false, false); 13 | 14 | return event; 15 | } 16 | 17 | function getErrorEvent() { 18 | const event = document.createEvent('Event'); 19 | 20 | event.initEvent('error', false, false); 21 | 22 | return event; 23 | } 24 | 25 | describe('HMR', () => { 26 | let consoleMock = null; 27 | 28 | beforeEach(() => { 29 | consoleMock = jest.spyOn(console, 'log').mockImplementation(() => () => {}); 30 | 31 | jest.spyOn(Date, 'now').mockImplementation(() => 1479427200000); 32 | 33 | document.head.innerHTML = 34 | ''; 35 | document.body.innerHTML = ''; 36 | }); 37 | 38 | afterEach(() => { 39 | consoleMock.mockClear(); 40 | }); 41 | 42 | it('should works', (done) => { 43 | const update = hotModuleReplacement('./src/style.css', {}); 44 | 45 | update(); 46 | 47 | setTimeout(() => { 48 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 49 | 50 | const links = Array.prototype.slice.call( 51 | document.querySelectorAll('link') 52 | ); 53 | 54 | expect(links[0].visited).toBe(true); 55 | expect(document.head.innerHTML).toMatchSnapshot(); 56 | 57 | links[1].dispatchEvent(getLoadEvent()); 58 | 59 | expect(links[1].isLoaded).toBe(true); 60 | 61 | done(); 62 | }, 100); 63 | }); 64 | 65 | it('should works with multiple updates', (done) => { 66 | const update = hotModuleReplacement('./src/style.css', {}); 67 | 68 | update(); 69 | 70 | setTimeout(() => { 71 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 72 | 73 | const links = Array.prototype.slice.call( 74 | document.querySelectorAll('link') 75 | ); 76 | 77 | expect(links[0].visited).toBe(true); 78 | expect(document.head.innerHTML).toMatchSnapshot(); 79 | 80 | links[1].dispatchEvent(getLoadEvent()); 81 | 82 | expect(links[1].isLoaded).toBe(true); 83 | 84 | jest.spyOn(Date, 'now').mockImplementation(() => 1479427200001); 85 | 86 | const update2 = hotModuleReplacement('./src/style.css', {}); 87 | 88 | update2(); 89 | 90 | setTimeout(() => { 91 | const links2 = Array.prototype.slice.call( 92 | document.querySelectorAll('link') 93 | ); 94 | 95 | expect(links2[0].visited).toBe(true); 96 | expect(links2[0].isLoaded).toBe(true); 97 | expect(document.head.innerHTML).toMatchSnapshot(); 98 | 99 | links2[1].dispatchEvent(getLoadEvent()); 100 | 101 | expect(links2[1].isLoaded).toBe(true); 102 | 103 | done(); 104 | }, 100); 105 | }, 100); 106 | }); 107 | 108 | it('should reloads with locals', (done) => { 109 | const update = hotModuleReplacement('./src/style.css', { 110 | locals: { foo: 'bar' }, 111 | }); 112 | 113 | update(); 114 | 115 | setTimeout(() => { 116 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 117 | 118 | const links = Array.prototype.slice.call( 119 | document.querySelectorAll('link') 120 | ); 121 | 122 | expect(links[0].visited).toBe(true); 123 | expect(document.head.innerHTML).toMatchSnapshot(); 124 | 125 | links[1].dispatchEvent(getLoadEvent()); 126 | 127 | expect(links[1].isLoaded).toBe(true); 128 | 129 | done(); 130 | }, 100); 131 | }); 132 | 133 | it('should reloads with reloadAll option', (done) => { 134 | const update = hotModuleReplacement('./src/style.css', { 135 | reloadAll: true, 136 | }); 137 | 138 | update(); 139 | 140 | setTimeout(() => { 141 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 142 | 143 | const links = Array.prototype.slice.call( 144 | document.querySelectorAll('link') 145 | ); 146 | 147 | expect(links[0].visited).toBe(true); 148 | expect(document.head.innerHTML).toMatchSnapshot(); 149 | 150 | links[1].dispatchEvent(getLoadEvent()); 151 | 152 | expect(links[1].isLoaded).toBe(true); 153 | 154 | done(); 155 | }, 100); 156 | }); 157 | 158 | it('should reloads with non http/https link href', (done) => { 159 | document.head.innerHTML = 160 | ''; 161 | 162 | const update = hotModuleReplacement('./src/style.css', {}); 163 | 164 | update(); 165 | 166 | setTimeout(() => { 167 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 168 | 169 | const links = Array.prototype.slice.call( 170 | document.querySelectorAll('link') 171 | ); 172 | 173 | expect(links[0].visited).toBe(true); 174 | expect(document.head.innerHTML).toMatchSnapshot(); 175 | 176 | links[1].dispatchEvent(getLoadEvent()); 177 | 178 | expect(links[1].isLoaded).toBe(true); 179 | expect(links[2].visited).toBeUndefined(); 180 | 181 | done(); 182 | }, 100); 183 | }); 184 | 185 | it('should reloads with # link href', (done) => { 186 | document.head.innerHTML = 187 | ''; 188 | 189 | const update = hotModuleReplacement('./src/style.css', {}); 190 | 191 | update(); 192 | 193 | setTimeout(() => { 194 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 195 | 196 | const links = Array.prototype.slice.call( 197 | document.querySelectorAll('link') 198 | ); 199 | 200 | expect(links[0].visited).toBe(true); 201 | expect(document.head.innerHTML).toMatchSnapshot(); 202 | 203 | links[1].dispatchEvent(getLoadEvent()); 204 | 205 | expect(links[1].isLoaded).toBe(true); 206 | expect(links[2].visited).toBeUndefined(); 207 | 208 | done(); 209 | }, 100); 210 | }); 211 | 212 | it('should reloads with link without href', (done) => { 213 | document.head.innerHTML = 214 | ''; 215 | 216 | const update = hotModuleReplacement('./src/style.css', {}); 217 | 218 | update(); 219 | 220 | setTimeout(() => { 221 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 222 | 223 | const links = Array.prototype.slice.call( 224 | document.querySelectorAll('link') 225 | ); 226 | 227 | expect(links[0].visited).toBe(true); 228 | expect(document.head.innerHTML).toMatchSnapshot(); 229 | 230 | links[1].dispatchEvent(getLoadEvent()); 231 | 232 | expect(links[1].isLoaded).toBe(true); 233 | expect(links[2].visited).toBeUndefined(); 234 | 235 | done(); 236 | }, 100); 237 | }); 238 | 239 | it('should reloads with absolute remove url', (done) => { 240 | document.head.innerHTML = 241 | ''; 242 | 243 | const update = hotModuleReplacement('./src/style.css', {}); 244 | 245 | update(); 246 | 247 | setTimeout(() => { 248 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 249 | 250 | const links = Array.prototype.slice.call( 251 | document.querySelectorAll('link') 252 | ); 253 | 254 | expect(links[0].visited).toBe(true); 255 | expect(document.head.innerHTML).toMatchSnapshot(); 256 | 257 | links[1].dispatchEvent(getLoadEvent()); 258 | 259 | expect(links[1].isLoaded).toBe(true); 260 | expect(links[2].visited).toBeUndefined(); 261 | 262 | done(); 263 | }, 100); 264 | }); 265 | 266 | it('should handle error event', (done) => { 267 | const update = hotModuleReplacement('./src/style.css', {}); 268 | 269 | update(); 270 | 271 | setTimeout(() => { 272 | expect(console.log.mock.calls[0][0]).toMatchSnapshot(); 273 | 274 | const links = Array.prototype.slice.call( 275 | document.querySelectorAll('link') 276 | ); 277 | 278 | expect(links[0].visited).toBe(true); 279 | expect(document.head.innerHTML).toMatchSnapshot(); 280 | 281 | links[1].dispatchEvent(getErrorEvent()); 282 | 283 | expect(links[1].isLoaded).toBe(true); 284 | 285 | done(); 286 | }, 100); 287 | }); 288 | }); 289 | -------------------------------------------------------------------------------- /test/TestCases.test.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | import webpack from 'webpack'; 5 | 6 | function compareDirectory(actual, expected) { 7 | const files = fs.readdirSync(expected); 8 | 9 | for (const file of files) { 10 | const absoluteFilePath = path.resolve(expected, file); 11 | 12 | const stats = fs.lstatSync(absoluteFilePath); 13 | 14 | if (stats.isDirectory()) { 15 | compareDirectory( 16 | path.resolve(actual, file), 17 | path.resolve(expected, file) 18 | ); 19 | } else if (stats.isFile()) { 20 | const content = fs.readFileSync(path.resolve(expected, file), 'utf8'); 21 | const actualContent = fs.readFileSync(path.resolve(actual, file), 'utf8'); 22 | 23 | expect(actualContent).toEqual(content); 24 | } 25 | } 26 | } 27 | 28 | function compareWarning(actual, expectedFile) { 29 | if (!fs.existsSync(expectedFile)) { 30 | return; 31 | } 32 | 33 | // eslint-disable-next-line global-require, import/no-dynamic-require 34 | const expected = require(expectedFile); 35 | 36 | expect(actual.trim()).toBe(expected.trim()); 37 | } 38 | 39 | describe('TestCases', () => { 40 | const casesDirectory = path.resolve(__dirname, 'cases'); 41 | const outputDirectory = path.resolve(__dirname, 'js'); 42 | 43 | for (const directory of fs.readdirSync(casesDirectory)) { 44 | if (!/^(\.|_)/.test(directory)) { 45 | // eslint-disable-next-line no-loop-func 46 | it(`${directory} should compile to the expected result`, (done) => { 47 | const directoryForCase = path.resolve(casesDirectory, directory); 48 | const outputDirectoryForCase = path.resolve(outputDirectory, directory); 49 | // eslint-disable-next-line import/no-dynamic-require, global-require 50 | const webpackConfig = require(path.resolve( 51 | directoryForCase, 52 | 'webpack.config.js' 53 | )); 54 | 55 | for (const config of [].concat(webpackConfig)) { 56 | Object.assign( 57 | config, 58 | { 59 | mode: 'none', 60 | context: directoryForCase, 61 | output: Object.assign( 62 | { 63 | path: outputDirectoryForCase, 64 | }, 65 | config.output 66 | ), 67 | }, 68 | config 69 | ); 70 | } 71 | 72 | webpack(webpackConfig, (err, stats) => { 73 | if (err) { 74 | done(err); 75 | return; 76 | } 77 | 78 | done(); 79 | 80 | // eslint-disable-next-line no-console 81 | console.log( 82 | stats.toString({ 83 | context: path.resolve(__dirname, '..'), 84 | chunks: true, 85 | chunkModules: true, 86 | modules: false, 87 | }) 88 | ); 89 | 90 | if (stats.hasErrors()) { 91 | done( 92 | new Error( 93 | stats.toString({ 94 | context: path.resolve(__dirname, '..'), 95 | errorDetails: true, 96 | }) 97 | ) 98 | ); 99 | 100 | return; 101 | } 102 | 103 | const expectedDirectory = path.resolve(directoryForCase, 'expected'); 104 | const expectedDirectoryByVersion = path.join( 105 | expectedDirectory, 106 | `webpack-${webpack.version[0]}` 107 | ); 108 | 109 | if (fs.existsSync(expectedDirectoryByVersion)) { 110 | compareDirectory( 111 | outputDirectoryForCase, 112 | expectedDirectoryByVersion 113 | ); 114 | } else { 115 | compareDirectory(outputDirectoryForCase, expectedDirectory); 116 | } 117 | 118 | const expectedWarning = path.resolve(directoryForCase, 'warnings.js'); 119 | const actualWarning = stats.toString({ 120 | all: false, 121 | warnings: true, 122 | }); 123 | compareWarning(actualWarning, expectedWarning); 124 | 125 | done(); 126 | }); 127 | }, 10000); 128 | } 129 | } 130 | }); 131 | -------------------------------------------------------------------------------- /test/TestMemoryFS.test.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { createFsFromVolume, Volume } from 'memfs'; 4 | import webpack from 'webpack'; 5 | 6 | const assetsNames = (json) => json.assets.map((asset) => asset.name); 7 | 8 | describe('TestMemoryFS', () => { 9 | it('should preserve asset even if not emitted', (done) => { 10 | const casesDirectory = path.resolve(__dirname, 'cases'); 11 | const directoryForCase = path.resolve(casesDirectory, 'simple-publicpath'); 12 | // eslint-disable-next-line import/no-dynamic-require, global-require 13 | const webpackConfig = require(path.resolve( 14 | directoryForCase, 15 | 'webpack.config.js' 16 | )); 17 | const compiler = webpack({ 18 | ...webpackConfig, 19 | mode: 'development', 20 | context: directoryForCase, 21 | cache: false, 22 | }); 23 | const outputFileSystem = createFsFromVolume(new Volume()); 24 | // Todo remove when we drop webpack@4 support 25 | outputFileSystem.join = path.join.bind(path); 26 | 27 | compiler.outputFileSystem = outputFileSystem; 28 | 29 | compiler.run((err1, stats1) => { 30 | if (err1) { 31 | done(err1); 32 | 33 | return; 34 | } 35 | 36 | compiler.run((err2, stats2) => { 37 | if (err2) { 38 | done(err2); 39 | 40 | return; 41 | } 42 | 43 | expect(assetsNames(stats1.toJson())).toEqual( 44 | assetsNames(stats2.toJson()) 45 | ); 46 | 47 | done(); 48 | }); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/__snapshots__/HMR.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`HMR should handle error event 1`] = `"[HMR] css reload %s"`; 4 | 5 | exports[`HMR should handle error event 2`] = `""`; 6 | 7 | exports[`HMR should reloads with # link href 1`] = `"[HMR] css reload %s"`; 8 | 9 | exports[`HMR should reloads with # link href 2`] = `""`; 10 | 11 | exports[`HMR should reloads with absolute remove url 1`] = `"[HMR] css reload %s"`; 12 | 13 | exports[`HMR should reloads with absolute remove url 2`] = `""`; 14 | 15 | exports[`HMR should reloads with link without href 1`] = `"[HMR] css reload %s"`; 16 | 17 | exports[`HMR should reloads with link without href 2`] = `""`; 18 | 19 | exports[`HMR should reloads with locals 1`] = `"[HMR] Detected local css modules. Reload all css"`; 20 | 21 | exports[`HMR should reloads with locals 2`] = `""`; 22 | 23 | exports[`HMR should reloads with non http/https link href 1`] = `"[HMR] css reload %s"`; 24 | 25 | exports[`HMR should reloads with non http/https link href 2`] = `""`; 26 | 27 | exports[`HMR should reloads with reloadAll option 1`] = `"[HMR] Reload all css"`; 28 | 29 | exports[`HMR should reloads with reloadAll option 2`] = `""`; 30 | 31 | exports[`HMR should works 1`] = `"[HMR] css reload %s"`; 32 | 33 | exports[`HMR should works 2`] = `""`; 34 | 35 | exports[`HMR should works with multiple updates 1`] = `"[HMR] css reload %s"`; 36 | 37 | exports[`HMR should works with multiple updates 2`] = `""`; 38 | 39 | exports[`HMR should works with multiple updates 3`] = `""`; 40 | -------------------------------------------------------------------------------- /test/__snapshots__/validate-loader-options.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`validate options should throw an error on the "esModule" option with "1" value 1`] = ` 4 | "Mini CSS Extract Plugin Loader Invalid Options 5 | 6 | options.esModule should be boolean 7 | " 8 | `; 9 | 10 | exports[`validate options should throw an error on the "hmr" option with "1" value 1`] = ` 11 | "Mini CSS Extract Plugin Loader Invalid Options 12 | 13 | options.hmr should be boolean 14 | " 15 | `; 16 | 17 | exports[`validate options should throw an error on the "publicPath" option with "true" value 1`] = ` 18 | "Mini CSS Extract Plugin Loader Invalid Options 19 | 20 | options.publicPath should be string 21 | options.publicPath should pass \\"instanceof\\" keyword validation 22 | options.publicPath should match some schema in anyOf 23 | " 24 | `; 25 | 26 | exports[`validate options should throw an error on the "reloadAll" option with "1" value 1`] = ` 27 | "Mini CSS Extract Plugin Loader Invalid Options 28 | 29 | options.reloadAll should be boolean 30 | " 31 | `; 32 | -------------------------------------------------------------------------------- /test/__snapshots__/validate-plugin-options.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`validate options should throw an error on the "chunkFilename" option with "true" value 1`] = ` 4 | "Mini CSS Extract Plugin Invalid Options 5 | 6 | options.chunkFilename should be string 7 | " 8 | `; 9 | 10 | exports[`validate options should throw an error on the "filename" option with "true" value 1`] = ` 11 | "Mini CSS Extract Plugin Invalid Options 12 | 13 | options.filename should be string 14 | " 15 | `; 16 | 17 | exports[`validate options should throw an error on the "ignoreOrder" option with "1" value 1`] = ` 18 | "Mini CSS Extract Plugin Invalid Options 19 | 20 | options.ignoreOrder should be boolean 21 | " 22 | `; 23 | 24 | exports[`validate options should throw an error on the "moduleFilename" option with "true" value 1`] = ` 25 | "Mini CSS Extract Plugin Invalid Options 26 | 27 | options.moduleFilename should pass \\"instanceof\\" keyword validation 28 | " 29 | `; 30 | -------------------------------------------------------------------------------- /test/cases/at-import/a.css: -------------------------------------------------------------------------------- 1 | @import './ae.css'; 2 | @import './aa.css'; 3 | @import './ab.css'; 4 | @import './ac.css'; 5 | @import './ad.css'; 6 | 7 | body { 8 | background: red; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/at-import/aa.css: -------------------------------------------------------------------------------- 1 | .aa { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/at-import/ab.css: -------------------------------------------------------------------------------- 1 | .ab { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/at-import/ac.css: -------------------------------------------------------------------------------- 1 | .ac { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/at-import/ad.css: -------------------------------------------------------------------------------- 1 | .ad { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/at-import/ae.css: -------------------------------------------------------------------------------- 1 | .ae { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/at-import/b.css: -------------------------------------------------------------------------------- 1 | @import './ba.css'; 2 | @import './bb.css'; 3 | 4 | body { 5 | background: yellow; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/at-import/ba.css: -------------------------------------------------------------------------------- 1 | .ba { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/at-import/bb.css: -------------------------------------------------------------------------------- 1 | .bb { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/at-import/expected/main.css: -------------------------------------------------------------------------------- 1 | .ae { 2 | background: green; 3 | } 4 | 5 | .aa { 6 | background: green; 7 | } 8 | 9 | .ab { 10 | background: green; 11 | } 12 | 13 | .ac { 14 | background: green; 15 | } 16 | 17 | .ad { 18 | background: green; 19 | } 20 | 21 | body { 22 | background: red; 23 | } 24 | 25 | .ba { 26 | background: green; 27 | } 28 | 29 | .bb { 30 | background: green; 31 | } 32 | 33 | body { 34 | background: yellow; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /test/cases/at-import/index.js: -------------------------------------------------------------------------------- 1 | import './a.css'; 2 | import './b.css'; 3 | -------------------------------------------------------------------------------- /test/cases/at-import/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/chunkFilename/async.css: -------------------------------------------------------------------------------- 1 | .async { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/chunkFilename/expected/1.async.css: -------------------------------------------------------------------------------- 1 | .async { 2 | color: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/chunkFilename/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/chunkFilename/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | 3 | /* eslint-disable-next-line no-unused-expressions */ 4 | import(/* webpackChunkName: "async" */ './async.css'); 5 | -------------------------------------------------------------------------------- /test/cases/chunkFilename/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/chunkFilename/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | chunkFilename: '[id].[name].css', 17 | }), 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /test/cases/commonjs-module-syntax/expected/main.css: -------------------------------------------------------------------------------- 1 | .foo__style__a { 2 | background: red; 3 | } 4 | 5 | .foo__style__b { 6 | color: green; 7 | } 8 | 9 | .c { 10 | color: blue; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/cases/commonjs-module-syntax/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/commonjs-module-syntax/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | background: red; 3 | } 4 | 5 | :local(.b) { 6 | color: green; 7 | } 8 | 9 | :global(.c) { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/commonjs-module-syntax/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | { 11 | loader: Self.loader, 12 | options: { esModule: false }, 13 | }, 14 | { 15 | loader: 'css-loader', 16 | options: { 17 | modules: { 18 | mode: 'local', 19 | localIdentName: 'foo__[name]__[local]', 20 | }, 21 | }, 22 | }, 23 | ], 24 | }, 25 | ], 26 | }, 27 | plugins: [ 28 | new Self({ 29 | filename: '[name].css', 30 | }), 31 | ], 32 | }; 33 | -------------------------------------------------------------------------------- /test/cases/composes-async/async-1.css: -------------------------------------------------------------------------------- 1 | :local .base { 2 | composes: composed from './async-2.css'; 3 | background: blue; 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/composes-async/async-2.css: -------------------------------------------------------------------------------- 1 | :local .composed { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/composes-async/expected/async-1.css: -------------------------------------------------------------------------------- 1 | .base { 2 | background: blue; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/composes-async/expected/dedupe.css: -------------------------------------------------------------------------------- 1 | .composed { 2 | background: green; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/composes-async/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable-next-line no-unused-expressions */ 2 | import(/* webpackChunkName: "async-1" */ './async-1.css'); 3 | /* eslint-disable-next-line no-unused-expressions */ 4 | import(/* webpackChunkName: "async-2" */ './async-2.css'); 5 | -------------------------------------------------------------------------------- /test/cases/composes-async/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | Self.loader, 11 | { 12 | loader: 'css-loader', 13 | options: { 14 | modules: { 15 | localIdentName: '[local]', 16 | }, 17 | }, 18 | }, 19 | ], 20 | }, 21 | ], 22 | }, 23 | optimization: { 24 | splitChunks: { 25 | cacheGroups: { 26 | cssDedupe: { 27 | test: /\.css$/, 28 | name: 'dedupe', 29 | chunks: 'all', 30 | minChunks: 2, 31 | enforce: true, 32 | }, 33 | }, 34 | }, 35 | }, 36 | plugins: [ 37 | new Self({ 38 | filename: '[name].css', 39 | }), 40 | ], 41 | }; 42 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/entryA.js: -------------------------------------------------------------------------------- 1 | import './styleA.css'; 2 | import './styleB.css'; 3 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/entryB.js: -------------------------------------------------------------------------------- 1 | import './styleA.css'; 2 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/entryC.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faceyspacey/extract-css-chunks-webpack-plugin/18837c2ddc4175313cfd812698e7fae4ae4a7acc/test/cases/contenthash-multiple-entries/entryC.js -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/entryD.js: -------------------------------------------------------------------------------- 1 | import './styleA.css'; 2 | import './styleB.css'; 3 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/entryE.js: -------------------------------------------------------------------------------- 1 | import './styleC.css'; 2 | import './styleD.css'; 3 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/expected/96236f7f51b351aabd20.css: -------------------------------------------------------------------------------- 1 | .styleA { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/expected/f22bc5a793a5a86ad253.css: -------------------------------------------------------------------------------- 1 | .styleA { 2 | background: red; 3 | } 4 | 5 | .styleB { 6 | background: blue; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/styleA.css: -------------------------------------------------------------------------------- 1 | .styleA { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/styleB.css: -------------------------------------------------------------------------------- 1 | .styleB { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/styleC.css: -------------------------------------------------------------------------------- 1 | .styleA { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/styleD.css: -------------------------------------------------------------------------------- 1 | .styleB { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/contenthash-multiple-entries/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: { 5 | entryA: './entryA.js', 6 | entryB: './entryB.js', 7 | entryC: './entryC.js', 8 | entryD: './entryD.js', 9 | entryE: './entryE.js', 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.css$/, 15 | use: [Self.loader, 'css-loader'], 16 | }, 17 | ], 18 | }, 19 | output: { 20 | filename: '[name]-[contenthash].js', 21 | }, 22 | plugins: [ 23 | new Self({ 24 | filename: '[contenthash].css', 25 | }), 26 | ], 27 | }; 28 | -------------------------------------------------------------------------------- /test/cases/contenthash/expected/1.main.c4d90d38e7a606ae4d4c.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/contenthash/expected/2.main.64d1032b1547f22458a7.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/contenthash/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-unresolved 2 | import './style.css'; 3 | -------------------------------------------------------------------------------- /test/cases/contenthash/style1.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/contenthash/style2.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/contenthash/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = [1, 2].map((n) => { 4 | return { 5 | entry: './index.js', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.css$/, 10 | use: [Self.loader, 'css-loader'], 11 | }, 12 | ], 13 | }, 14 | output: { 15 | filename: `${n}.[name].js`, 16 | }, 17 | resolve: { 18 | alias: { 19 | './style.css': `./style${n}.css`, 20 | }, 21 | }, 22 | plugins: [ 23 | new Self({ 24 | filename: `${n}.[name].[contenthash].css`, 25 | }), 26 | ], 27 | }; 28 | }); 29 | -------------------------------------------------------------------------------- /test/cases/css-import/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/css-import/b.css: -------------------------------------------------------------------------------- 1 | .b { 2 | background: red; 3 | } 4 | 5 | @import url('https://some/external/css'); 6 | 7 | .b { 8 | color: yellow; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/css-import/c.css: -------------------------------------------------------------------------------- 1 | .c { 2 | background: red; 3 | } 4 | @import './a.css'; 5 | @import url('https://some/other/external/css'); 6 | 7 | .c { 8 | color: yellow; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/css-import/expected/main.css: -------------------------------------------------------------------------------- 1 | @import url(https://some/other/external/css); 2 | @import url(https://some/external/css); 3 | body { 4 | background: red; 5 | } 6 | 7 | .c { 8 | background: red; 9 | } 10 | 11 | .c { 12 | color: yellow; 13 | } 14 | 15 | .b { 16 | background: red; 17 | } 18 | 19 | .b { 20 | color: yellow; 21 | } 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/cases/css-import/index.css: -------------------------------------------------------------------------------- 1 | @import './c.css'; 2 | @import './b.css'; 3 | -------------------------------------------------------------------------------- /test/cases/css-import/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.css', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/default-options/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/default-options/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/default-options/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/default-options/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [new Self()], 14 | }; 15 | -------------------------------------------------------------------------------- /test/cases/es-module-syntax/expected/main.css: -------------------------------------------------------------------------------- 1 | .foo__style__a { 2 | background: red; 3 | } 4 | 5 | .foo__style__b { 6 | color: green; 7 | } 8 | 9 | .c { 10 | color: blue; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/cases/es-module-syntax/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/es-module-syntax/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | background: red; 3 | } 4 | 5 | :local(.b) { 6 | color: green; 7 | } 8 | 9 | :global(.c) { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/es-module-syntax/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | { 11 | loader: Self.loader, 12 | options: { esModule: true }, 13 | }, 14 | { 15 | loader: 'css-loader', 16 | options: { 17 | modules: { 18 | mode: 'local', 19 | localIdentName: 'foo__[name]__[local]', 20 | }, 21 | }, 22 | }, 23 | ], 24 | }, 25 | ], 26 | }, 27 | plugins: [ 28 | new Self({ 29 | filename: '[name].css', 30 | }), 31 | ], 32 | }; 33 | -------------------------------------------------------------------------------- /test/cases/filename-with-template/async.css: -------------------------------------------------------------------------------- 1 | .async { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/filename-with-template/expected/async.css: -------------------------------------------------------------------------------- 1 | .async { 2 | color: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/filename-with-template/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/filename-with-template/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | 3 | /* eslint-disable-next-line no-unused-expressions */ 4 | import(/* webpackChunkName: "async" */ './async.css'); 5 | -------------------------------------------------------------------------------- /test/cases/filename-with-template/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/filename-with-template/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/filename-without-template/async.css: -------------------------------------------------------------------------------- 1 | .async { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/filename-without-template/expected/1.main.css: -------------------------------------------------------------------------------- 1 | .async { 2 | color: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/filename-without-template/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/filename-without-template/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | 3 | /* eslint-disable-next-line no-unused-expressions */ 4 | import(/* webpackChunkName: "async" */ './async.css'); 5 | -------------------------------------------------------------------------------- /test/cases/filename-without-template/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/filename-without-template/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: 'main.css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/hmr/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/hmr/b.css: -------------------------------------------------------------------------------- 1 | .b { 2 | background: red; 3 | } 4 | 5 | @import url('https://some/external/css'); 6 | 7 | .b { 8 | color: yellow; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/hmr/c.css: -------------------------------------------------------------------------------- 1 | .c { 2 | background: red; 3 | } 4 | @import './a.css'; 5 | @import url('https://some/other/external/css'); 6 | 7 | .c { 8 | color: yellow; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/hmr/expected/webpack-4/main.css: -------------------------------------------------------------------------------- 1 | @import url(https://some/other/external/css); 2 | @import url(https://some/external/css); 3 | body { 4 | background: red; 5 | } 6 | 7 | .c { 8 | background: red; 9 | } 10 | 11 | .c { 12 | color: yellow; 13 | } 14 | 15 | .b { 16 | background: red; 17 | } 18 | 19 | .b { 20 | color: yellow; 21 | } 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/cases/hmr/expected/webpack-4/main.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 40 | /******/ } 41 | /******/ }; 42 | /******/ 43 | /******/ // define __esModule on exports 44 | /******/ __webpack_require__.r = function(exports) { 45 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 46 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 47 | /******/ } 48 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 49 | /******/ }; 50 | /******/ 51 | /******/ // create a fake namespace object 52 | /******/ // mode & 1: value is a module id, require it 53 | /******/ // mode & 2: merge all properties of value into the ns 54 | /******/ // mode & 4: return value when already ns object 55 | /******/ // mode & 8|1: behave like require 56 | /******/ __webpack_require__.t = function(value, mode) { 57 | /******/ if(mode & 1) value = __webpack_require__(value); 58 | /******/ if(mode & 8) return value; 59 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 60 | /******/ var ns = Object.create(null); 61 | /******/ __webpack_require__.r(ns); 62 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 63 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 64 | /******/ return ns; 65 | /******/ }; 66 | /******/ 67 | /******/ // getDefaultExport function for compatibility with non-harmony modules 68 | /******/ __webpack_require__.n = function(module) { 69 | /******/ var getter = module && module.__esModule ? 70 | /******/ function getDefault() { return module['default']; } : 71 | /******/ function getModuleExports() { return module; }; 72 | /******/ __webpack_require__.d(getter, 'a', getter); 73 | /******/ return getter; 74 | /******/ }; 75 | /******/ 76 | /******/ // Object.prototype.hasOwnProperty.call 77 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 78 | /******/ 79 | /******/ // __webpack_public_path__ 80 | /******/ __webpack_require__.p = ""; 81 | /******/ 82 | /******/ 83 | /******/ // Load entry module and return exports 84 | /******/ return __webpack_require__(__webpack_require__.s = 0); 85 | /******/ }) 86 | /************************************************************************/ 87 | /******/ ([ 88 | /* 0 */ 89 | /***/ (function(module, exports, __webpack_require__) { 90 | 91 | // extracted by extract-css-chunks-webpack-plugin 92 | if(false) { var cssReload; } 93 | 94 | 95 | /***/ }) 96 | /******/ ]); -------------------------------------------------------------------------------- /test/cases/hmr/expected/webpack-5/main.css: -------------------------------------------------------------------------------- 1 | @import url(https://some/other/external/css); 2 | @import url(https://some/external/css); 3 | body { 4 | background: red; 5 | } 6 | 7 | .c { 8 | background: red; 9 | } 10 | 11 | .c { 12 | color: yellow; 13 | } 14 | 15 | .b { 16 | background: red; 17 | } 18 | 19 | .b { 20 | color: yellow; 21 | } 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/cases/hmr/expected/webpack-5/main.js: -------------------------------------------------------------------------------- 1 | /******/ (() => { // webpackBootstrap 2 | /******/ /************************************************************************/ 3 | // extracted by extract-css-chunks-webpack-plugin 4 | if(false) { var cssReload; } 5 | 6 | /******/ })() 7 | ; 8 | -------------------------------------------------------------------------------- /test/cases/hmr/index.css: -------------------------------------------------------------------------------- 1 | @import './c.css'; 2 | @import './b.css'; 3 | -------------------------------------------------------------------------------- /test/cases/hmr/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.css', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | { 11 | loader: Self.loader, 12 | options: { 13 | hmr: true, 14 | }, 15 | }, 16 | 'css-loader', 17 | ], 18 | }, 19 | ], 20 | }, 21 | plugins: [ 22 | new Self({ 23 | filename: '[name].css', 24 | }), 25 | ], 26 | }; 27 | -------------------------------------------------------------------------------- /test/cases/ignoreOrder/e1.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e1'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrder/e2.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e2'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrder/expected/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e2'; 3 | } 4 | 5 | body { 6 | content: 'e1'; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/ignoreOrder/index.js: -------------------------------------------------------------------------------- 1 | import './e2.css'; 2 | import './e1.css'; 3 | -------------------------------------------------------------------------------- /test/cases/ignoreOrder/index2.js: -------------------------------------------------------------------------------- 1 | import './e1.css'; 2 | import './e2.css'; 3 | -------------------------------------------------------------------------------- /test/cases/ignoreOrder/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: { 5 | entry1: './index.js', 6 | entry2: './index2.js', 7 | }, 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.css$/, 12 | use: [Self.loader, 'css-loader'], 13 | }, 14 | ], 15 | }, 16 | optimization: { 17 | splitChunks: { 18 | cacheGroups: { 19 | styles: { 20 | name: 'styles', 21 | chunks: 'all', 22 | test: /\.css$/, 23 | enforce: true, 24 | }, 25 | }, 26 | }, 27 | }, 28 | plugins: [ 29 | new Self({ 30 | ignoreOrder: true, 31 | }), 32 | ], 33 | }; 34 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/e1.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e1'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/e2.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e2'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/e3.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e3'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/expected/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e2'; 3 | } 4 | 5 | body { 6 | content: 'e1'; 7 | } 8 | 9 | body { 10 | content: 'e3'; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/index.js: -------------------------------------------------------------------------------- 1 | import './e2.css'; 2 | import './e1.css'; 3 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/index2.js: -------------------------------------------------------------------------------- 1 | import './e1.css'; 2 | import './e2.css'; 3 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/index3.js: -------------------------------------------------------------------------------- 1 | import './e2.css'; 2 | import './e3.css'; 3 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/warnings.js: -------------------------------------------------------------------------------- 1 | const cssLoaderPath = require.resolve('css-loader').replace(/\\/g, '/'); 2 | 3 | module.exports = [ 4 | '', 5 | 'WARNING in chunk styles [extract-css-chunks-webpack-plugin]', 6 | 'Conflicting order. Following module has been added:', 7 | ` * css ${cssLoaderPath}!./e2.css`, 8 | 'despite it was not able to fulfill desired ordering with these modules:', 9 | ` * css ${cssLoaderPath}!./e1.css`, 10 | " - couldn't fulfill desired order of chunk group(s) entry2", 11 | ' - while fulfilling desired order of chunk group(s) entry1', 12 | ].join('\n'); 13 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalse/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: { 5 | entry1: './index.js', 6 | entry2: './index2.js', 7 | entry3: './index3.js', 8 | }, 9 | module: { 10 | rules: [ 11 | { 12 | test: /\.css$/, 13 | use: [Self.loader, 'css-loader'], 14 | }, 15 | ], 16 | }, 17 | optimization: { 18 | splitChunks: { 19 | cacheGroups: { 20 | styles: { 21 | name: 'styles', 22 | chunks: 'all', 23 | test: /\.css$/, 24 | enforce: true, 25 | }, 26 | }, 27 | }, 28 | }, 29 | plugins: [ 30 | new Self({ 31 | ignoreOrder: false, 32 | }), 33 | ], 34 | }; 35 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/e1.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e1'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/e2.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e2'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/e3.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e3'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/e4.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e4'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/expected/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e1'; 3 | } 4 | 5 | body { 6 | content: 'e4'; 7 | } 8 | 9 | body { 10 | content: 'e2'; 11 | } 12 | 13 | body { 14 | content: 'e3'; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/index.js: -------------------------------------------------------------------------------- 1 | import './e1.css'; 2 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/index2.js: -------------------------------------------------------------------------------- 1 | import './e2.css'; 2 | import './e1.css'; 3 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/index3.js: -------------------------------------------------------------------------------- 1 | import './e3.css'; 2 | import './e4.css'; 3 | import './e2.css'; 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/index4.js: -------------------------------------------------------------------------------- 1 | import './e4.css'; 2 | import './e2.css'; 3 | import './e3.css'; 4 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/warnings.js: -------------------------------------------------------------------------------- 1 | const cssLoaderPath = require.resolve('css-loader').replace(/\\/g, '/'); 2 | 3 | module.exports = [ 4 | '', 5 | 'WARNING in chunk styles [extract-css-chunks-webpack-plugin]', 6 | 'Conflicting order. Following module has been added:', 7 | ` * css ${cssLoaderPath}!./e1.css`, 8 | 'despite it was not able to fulfill desired ordering with these modules:', 9 | ` * css ${cssLoaderPath}!./e2.css`, 10 | " - couldn't fulfill desired order of chunk group(s) entry2", 11 | '', 12 | 'WARNING in chunk styles [extract-css-chunks-webpack-plugin]', 13 | 'Conflicting order. Following module has been added:', 14 | ` * css ${cssLoaderPath}!./e4.css`, 15 | 'despite it was not able to fulfill desired ordering with these modules:', 16 | ` * css ${cssLoaderPath}!./e3.css`, 17 | " - couldn't fulfill desired order of chunk group(s) entry3", 18 | ' - while fulfilling desired order of chunk group(s) entry4', 19 | '', 20 | 'WARNING in chunk styles [extract-css-chunks-webpack-plugin]', 21 | 'Conflicting order. Following module has been added:', 22 | ` * css ${cssLoaderPath}!./e2.css`, 23 | 'despite it was not able to fulfill desired ordering with these modules:', 24 | ` * css ${cssLoaderPath}!./e3.css`, 25 | " - couldn't fulfill desired order of chunk group(s) entry3", 26 | ' - while fulfilling desired order of chunk group(s) entry4', 27 | ].join('\n'); 28 | -------------------------------------------------------------------------------- /test/cases/ignoreOrderFalseWithoutGoodChunks/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: { 5 | entry1: './index.js', 6 | entry2: './index2.js', 7 | entry3: './index3.js', 8 | entry4: './index4.js', 9 | }, 10 | module: { 11 | rules: [ 12 | { 13 | test: /\.css$/, 14 | use: [Self.loader, 'css-loader'], 15 | }, 16 | ], 17 | }, 18 | optimization: { 19 | splitChunks: { 20 | cacheGroups: { 21 | styles: { 22 | name: 'styles', 23 | chunks: 'all', 24 | test: /\.css$/, 25 | enforce: true, 26 | }, 27 | }, 28 | }, 29 | }, 30 | plugins: [ 31 | new Self({ 32 | ignoreOrder: false, 33 | }), 34 | ], 35 | }; 36 | -------------------------------------------------------------------------------- /test/cases/import/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/import/b.css: -------------------------------------------------------------------------------- 1 | .b { 2 | background: red; 3 | } 4 | 5 | @import url('https://some/external/css'); 6 | 7 | .b { 8 | color: yellow; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/import/c.css: -------------------------------------------------------------------------------- 1 | .c { 2 | background: red; 3 | } 4 | 5 | @import url('https://some/other/external/css'); 6 | 7 | .c { 8 | color: yellow; 9 | } 10 | -------------------------------------------------------------------------------- /test/cases/import/expected/main.css: -------------------------------------------------------------------------------- 1 | @import url(https://some/other/external/css); 2 | @import url(https://some/external/css); 3 | .c { 4 | background: red; 5 | } 6 | 7 | .c { 8 | color: yellow; 9 | } 10 | 11 | body { 12 | background: red; 13 | } 14 | 15 | .b { 16 | background: red; 17 | } 18 | 19 | .b { 20 | color: yellow; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /test/cases/import/index.js: -------------------------------------------------------------------------------- 1 | import './c.css'; 2 | import './a.css'; 3 | import './b.css'; 4 | -------------------------------------------------------------------------------- /test/cases/import/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/insert-function/expected/1.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/insert-function/expected/1.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[ 2 | /* 0 */, 3 | /* 1 */ 4 | /***/ (function(module, exports, __webpack_require__) { 5 | 6 | // extracted by extract-css-chunks-webpack-plugin 7 | 8 | /***/ }) 9 | ]]); -------------------------------------------------------------------------------- /test/cases/insert-function/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | extract-css-chunks-webpack-plugin testcase 7 | 8 | 9 | 21 | 26 | 27 | 28 | 29 |
30 | Initial CSS: Must be green 31 |
32 |
33 |

Hot Module Replacement

34 |
RED
35 |
GREEN
36 |
BLUE
37 |
38 |
39 |

Hot Module Replacement + CSS modules

40 |
RED
41 |
GREEN
42 |
BLUE
43 |
44 |
45 |

46 | Lazy CSS: Must be red, but turn green when 47 | . 48 |

49 |

50 | But turn orange, when 51 | . Additional 52 | clicks have no effect. 53 |

54 |

55 | Refresh and press buttons in reverse order: This should turn green 56 | instead. 57 |

58 |
59 |
60 |

61 | Lazy CSS: Turn off the network and 62 | . 63 |

64 |

An error should have appeared.

65 |

66 | Now if you turn the network back on and click it again, it should turn 67 | aqua. 68 |

69 |
70 |
71 |

Preloaded CSS: Must be green.

72 |

73 | displays 74 | an alert and should turn red. 75 |

76 |
77 |
78 |

Preloaded inlined CSS: Must be green.

79 |

80 | displays 81 | an alert and should turn red. 82 |

83 |
84 |
85 |

CrossOriginLoading Option: Must be red.

86 |

87 | loads chunks with crossorigin 88 | attribute and should turn green. 89 |

90 |
91 |
92 | 93 | 94 | zack 95 | 96 | -------------------------------------------------------------------------------- /test/cases/insert-function/src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | // eslint-disable-next-line 4 | import('./inject.css'); 5 | -------------------------------------------------------------------------------- /test/cases/insert-function/src/inject.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/insert-function/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/insert-function/webpack.config.e2e.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const Self = require('../../../'); 4 | 5 | const ENABLE_HMR = 6 | typeof process.env.ENABLE_HMR !== 'undefined' 7 | ? Boolean(process.env.ENABLE_HMR) 8 | : false; 9 | 10 | const ENABLE_ES_MODULE = 11 | typeof process.env.ES_MODULE !== 'undefined' 12 | ? Boolean(process.env.ES_MODULE) 13 | : false; 14 | 15 | module.exports = { 16 | mode: 'development', 17 | output: { 18 | path: path.resolve(__dirname, 'expected'), 19 | chunkFilename: '[contenthash].js', 20 | publicPath: '/', 21 | crossOriginLoading: 'anonymous', 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.css$/, 27 | exclude: [/\.module\.css$/i], 28 | use: [ 29 | { 30 | loader: Self.loader, 31 | options: { 32 | hmr: ENABLE_HMR, 33 | }, 34 | }, 35 | { 36 | loader: 'css-loader', 37 | options: { 38 | esModule: ENABLE_ES_MODULE, 39 | }, 40 | }, 41 | ], 42 | }, 43 | { 44 | test: /\.module\.css$/i, 45 | use: [ 46 | { 47 | loader: Self.loader, 48 | options: { 49 | hmr: ENABLE_HMR, 50 | }, 51 | }, 52 | { 53 | loader: 'css-loader', 54 | options: { 55 | modules: true, 56 | esModule: ENABLE_ES_MODULE, 57 | }, 58 | }, 59 | ], 60 | }, 61 | ], 62 | }, 63 | plugins: [ 64 | new Self({ 65 | filename: '[name].css', 66 | chunkFilename: '[id].css', 67 | insert: (linkTag) => { 68 | document.head.appendChild(linkTag) 69 | }, 70 | }), 71 | ], 72 | devServer: { 73 | contentBase: __dirname, 74 | port: 3001, 75 | headers: { 76 | 'Access-Control-Allow-Origin': '*', 77 | }, 78 | }, 79 | }; 80 | -------------------------------------------------------------------------------- /test/cases/insert-function/webpack.config.js: -------------------------------------------------------------------------------- 1 | /* global document */ 2 | 3 | const Self = require('../../../'); 4 | 5 | const ENABLE_HMR = 6 | typeof process.env.ENABLE_HMR !== 'undefined' 7 | ? Boolean(process.env.ENABLE_HMR) 8 | : false; 9 | 10 | const ENABLE_ES_MODULE = 11 | typeof process.env.ES_MODULE !== 'undefined' 12 | ? Boolean(process.env.ES_MODULE) 13 | : false; 14 | 15 | module.exports = { 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.css$/, 20 | exclude: [/\.module\.css$/i], 21 | use: [ 22 | { 23 | loader: Self.loader, 24 | options: { 25 | hmr: ENABLE_HMR, 26 | }, 27 | }, 28 | { 29 | loader: 'css-loader', 30 | options: { 31 | esModule: ENABLE_ES_MODULE, 32 | }, 33 | }, 34 | ], 35 | }, 36 | { 37 | test: /\.module\.css$/i, 38 | use: [ 39 | { 40 | loader: Self.loader, 41 | options: { 42 | hmr: ENABLE_HMR, 43 | }, 44 | }, 45 | { 46 | loader: 'css-loader', 47 | options: { 48 | modules: true, 49 | esModule: ENABLE_ES_MODULE, 50 | }, 51 | }, 52 | ], 53 | }, 54 | ], 55 | }, 56 | plugins: [ 57 | new Self({ 58 | filename: '[name].css', 59 | chunkFilename: '[id].css', 60 | insert: function insert(linkTag) { 61 | const reference = document.querySelector('.hot-reload'); 62 | if (reference) { 63 | reference.parentNode.insertBefore(linkTag, reference); 64 | } 65 | }, 66 | }), 67 | ], 68 | }; 69 | -------------------------------------------------------------------------------- /test/cases/insert-string/expected/1.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/insert-string/expected/1.js: -------------------------------------------------------------------------------- 1 | (window["webpackJsonp"] = window["webpackJsonp"] || []).push([[1],[ 2 | /* 0 */, 3 | /* 1 */ 4 | /***/ (function(module, exports, __webpack_require__) { 5 | 6 | // extracted by extract-css-chunks-webpack-plugin 7 | 8 | /***/ }) 9 | ]]); -------------------------------------------------------------------------------- /test/cases/insert-string/expected/main.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // install a JSONP callback for chunk loading 3 | /******/ function webpackJsonpCallback(data) { 4 | /******/ var chunkIds = data[0]; 5 | /******/ var moreModules = data[1]; 6 | /******/ 7 | /******/ 8 | /******/ // add "moreModules" to the modules object, 9 | /******/ // then flag all "chunkIds" as loaded and fire callback 10 | /******/ var moduleId, chunkId, i = 0, resolves = []; 11 | /******/ for(;i < chunkIds.length; i++) { 12 | /******/ chunkId = chunkIds[i]; 13 | /******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) { 14 | /******/ resolves.push(installedChunks[chunkId][0]); 15 | /******/ } 16 | /******/ installedChunks[chunkId] = 0; 17 | /******/ } 18 | /******/ for(moduleId in moreModules) { 19 | /******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { 20 | /******/ modules[moduleId] = moreModules[moduleId]; 21 | /******/ } 22 | /******/ } 23 | /******/ if(parentJsonpFunction) parentJsonpFunction(data); 24 | /******/ 25 | /******/ while(resolves.length) { 26 | /******/ resolves.shift()(); 27 | /******/ } 28 | /******/ 29 | /******/ }; 30 | /******/ 31 | /******/ 32 | /******/ // The module cache 33 | /******/ var installedModules = {}; 34 | /******/ 35 | /******/ // object to store loaded CSS chunks 36 | /******/ var installedCssChunks = { 37 | /******/ 0: 0 38 | /******/ } 39 | /******/ 40 | /******/ // object to store loaded and loading chunks 41 | /******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched 42 | /******/ // Promise = chunk loading, 0 = chunk loaded 43 | /******/ var installedChunks = { 44 | /******/ 0: 0 45 | /******/ }; 46 | /******/ 47 | /******/ 48 | /******/ 49 | /******/ // script path function 50 | /******/ function jsonpScriptSrc(chunkId) { 51 | /******/ return __webpack_require__.p + "" + ({}[chunkId]||chunkId) + ".js" 52 | /******/ } 53 | /******/ 54 | /******/ // The require function 55 | /******/ function __webpack_require__(moduleId) { 56 | /******/ 57 | /******/ // Check if module is in cache 58 | /******/ if(installedModules[moduleId]) { 59 | /******/ return installedModules[moduleId].exports; 60 | /******/ } 61 | /******/ // Create a new module (and put it into the cache) 62 | /******/ var module = installedModules[moduleId] = { 63 | /******/ i: moduleId, 64 | /******/ l: false, 65 | /******/ exports: {} 66 | /******/ }; 67 | /******/ 68 | /******/ // Execute the module function 69 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 70 | /******/ 71 | /******/ // Flag the module as loaded 72 | /******/ module.l = true; 73 | /******/ 74 | /******/ // Return the exports of the module 75 | /******/ return module.exports; 76 | /******/ } 77 | /******/ 78 | /******/ // This file contains only the entry chunk. 79 | /******/ // The chunk loading function for additional chunks 80 | /******/ __webpack_require__.e = function requireEnsure(chunkId) { 81 | /******/ var promises = []; 82 | /******/ 83 | /******/ 84 | /******/ // extract-css-chunks-webpack-plugin CSS loading 85 | /******/ var supportsPreload = (function() { try { return document.createElement("link").relList.supports("preload"); } catch(e) { return false; }}()); 86 | /******/ var cssChunks = {"1":1}; 87 | /******/ if(installedCssChunks[chunkId]) promises.push(installedCssChunks[chunkId]); 88 | /******/ else if(installedCssChunks[chunkId] !== 0 && cssChunks[chunkId]) { 89 | /******/ promises.push(installedCssChunks[chunkId] = new Promise(function(resolve, reject) { 90 | /******/ var href = "" + chunkId + ".css"; 91 | /******/ var fullhref = __webpack_require__.p + href; 92 | /******/ var existingLinkTags = document.getElementsByTagName("link"); 93 | /******/ for(var i = 0; i < existingLinkTags.length; i++) { 94 | /******/ var tag = existingLinkTags[i]; 95 | /******/ var dataHref = tag.getAttribute("data-href") || tag.getAttribute("href"); 96 | /******/ if((tag.rel === "stylesheet" || tag.rel === "preload") && (dataHref === href || dataHref === fullhref)) return resolve(); 97 | /******/ } 98 | /******/ var existingStyleTags = document.getElementsByTagName("style"); 99 | /******/ for(var i = 0; i < existingStyleTags.length; i++) { 100 | /******/ var tag = existingStyleTags[i]; 101 | /******/ var dataHref = tag.getAttribute("data-href"); 102 | /******/ if(dataHref === href || dataHref === fullhref) return resolve(); 103 | /******/ } 104 | /******/ var linkTag = document.createElement("link"); 105 | /******/ linkTag.rel = supportsPreload ? "preload": "stylesheet"; 106 | /******/ supportsPreload ? linkTag.as = "style" : linkTag.type = "text/css"; 107 | /******/ linkTag.onload = resolve; 108 | /******/ linkTag.onerror = function(event) { 109 | /******/ var request = event && event.target && event.target.src || fullhref; 110 | /******/ var err = new Error("Loading CSS chunk " + chunkId + " failed.\n(" + request + ")"); 111 | /******/ err.code = "CSS_CHUNK_LOAD_FAILED"; 112 | /******/ err.request = request; 113 | /******/ delete installedCssChunks[chunkId] 114 | /******/ linkTag.parentNode.removeChild(linkTag) 115 | /******/ reject(err); 116 | /******/ }; 117 | /******/ linkTag.href = fullhref; 118 | /******/ 119 | /******/ var insert = body; 120 | /******/ insert(linkTag); 121 | /******/ }).then(function() { 122 | /******/ installedCssChunks[chunkId] = 0; 123 | /******/ if(supportsPreload) { 124 | /******/ var execLinkTag = document.createElement("link"); 125 | /******/ execLinkTag.href = __webpack_require__.p + "" + chunkId + ".css"; 126 | /******/ execLinkTag.rel = "stylesheet"; 127 | /******/ execLinkTag.type = "text/css"; 128 | /******/ document.body.appendChild(execLinkTag); 129 | /******/ } 130 | /******/ })); 131 | /******/ } 132 | /******/ 133 | /******/ // JSONP chunk loading for javascript 134 | /******/ 135 | /******/ var installedChunkData = installedChunks[chunkId]; 136 | /******/ if(installedChunkData !== 0) { // 0 means "already installed". 137 | /******/ 138 | /******/ // a Promise means "currently loading". 139 | /******/ if(installedChunkData) { 140 | /******/ promises.push(installedChunkData[2]); 141 | /******/ } else { 142 | /******/ // setup Promise in chunk cache 143 | /******/ var promise = new Promise(function(resolve, reject) { 144 | /******/ installedChunkData = installedChunks[chunkId] = [resolve, reject]; 145 | /******/ }); 146 | /******/ promises.push(installedChunkData[2] = promise); 147 | /******/ 148 | /******/ // start chunk loading 149 | /******/ var script = document.createElement('script'); 150 | /******/ var onScriptComplete; 151 | /******/ 152 | /******/ script.charset = 'utf-8'; 153 | /******/ script.timeout = 120; 154 | /******/ if (__webpack_require__.nc) { 155 | /******/ script.setAttribute("nonce", __webpack_require__.nc); 156 | /******/ } 157 | /******/ script.src = jsonpScriptSrc(chunkId); 158 | /******/ 159 | /******/ // create error before stack unwound to get useful stacktrace later 160 | /******/ var error = new Error(); 161 | /******/ onScriptComplete = function (event) { 162 | /******/ // avoid mem leaks in IE. 163 | /******/ script.onerror = script.onload = null; 164 | /******/ clearTimeout(timeout); 165 | /******/ var chunk = installedChunks[chunkId]; 166 | /******/ if(chunk !== 0) { 167 | /******/ if(chunk) { 168 | /******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type); 169 | /******/ var realSrc = event && event.target && event.target.src; 170 | /******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; 171 | /******/ error.name = 'ChunkLoadError'; 172 | /******/ error.type = errorType; 173 | /******/ error.request = realSrc; 174 | /******/ chunk[1](error); 175 | /******/ } 176 | /******/ installedChunks[chunkId] = undefined; 177 | /******/ } 178 | /******/ }; 179 | /******/ var timeout = setTimeout(function(){ 180 | /******/ onScriptComplete({ type: 'timeout', target: script }); 181 | /******/ }, 120000); 182 | /******/ script.onerror = script.onload = onScriptComplete; 183 | /******/ document.head.appendChild(script); 184 | /******/ } 185 | /******/ } 186 | /******/ return Promise.all(promises); 187 | /******/ }; 188 | /******/ 189 | /******/ // expose the modules object (__webpack_modules__) 190 | /******/ __webpack_require__.m = modules; 191 | /******/ 192 | /******/ // expose the module cache 193 | /******/ __webpack_require__.c = installedModules; 194 | /******/ 195 | /******/ // define getter function for harmony exports 196 | /******/ __webpack_require__.d = function(exports, name, getter) { 197 | /******/ if(!__webpack_require__.o(exports, name)) { 198 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 199 | /******/ } 200 | /******/ }; 201 | /******/ 202 | /******/ // define __esModule on exports 203 | /******/ __webpack_require__.r = function(exports) { 204 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 205 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 206 | /******/ } 207 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 208 | /******/ }; 209 | /******/ 210 | /******/ // create a fake namespace object 211 | /******/ // mode & 1: value is a module id, require it 212 | /******/ // mode & 2: merge all properties of value into the ns 213 | /******/ // mode & 4: return value when already ns object 214 | /******/ // mode & 8|1: behave like require 215 | /******/ __webpack_require__.t = function(value, mode) { 216 | /******/ if(mode & 1) value = __webpack_require__(value); 217 | /******/ if(mode & 8) return value; 218 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 219 | /******/ var ns = Object.create(null); 220 | /******/ __webpack_require__.r(ns); 221 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 222 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 223 | /******/ return ns; 224 | /******/ }; 225 | /******/ 226 | /******/ // getDefaultExport function for compatibility with non-harmony modules 227 | /******/ __webpack_require__.n = function(module) { 228 | /******/ var getter = module && module.__esModule ? 229 | /******/ function getDefault() { return module['default']; } : 230 | /******/ function getModuleExports() { return module; }; 231 | /******/ __webpack_require__.d(getter, 'a', getter); 232 | /******/ return getter; 233 | /******/ }; 234 | /******/ 235 | /******/ // Object.prototype.hasOwnProperty.call 236 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 237 | /******/ 238 | /******/ // __webpack_public_path__ 239 | /******/ __webpack_require__.p = ""; 240 | /******/ 241 | /******/ // on error function for async loading 242 | /******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; 243 | /******/ 244 | /******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; 245 | /******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); 246 | /******/ jsonpArray.push = webpackJsonpCallback; 247 | /******/ jsonpArray = jsonpArray.slice(); 248 | /******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]); 249 | /******/ var parentJsonpFunction = oldJsonpFunction; 250 | /******/ 251 | /******/ 252 | /******/ // Load entry module and return exports 253 | /******/ return __webpack_require__(__webpack_require__.s = 0); 254 | /******/ }) 255 | /************************************************************************/ 256 | /******/ ([ 257 | /* 0 */ 258 | /***/ (function(module, exports, __webpack_require__) { 259 | 260 | /* eslint-env browser */ 261 | 262 | // eslint-disable-next-line 263 | __webpack_require__.e(/* import() */ 1).then(__webpack_require__.t.bind(null, 1, 7)); 264 | 265 | 266 | /***/ }) 267 | /******/ ]); -------------------------------------------------------------------------------- /test/cases/insert-string/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | extract-css-chunks-webpack-plugin testcase 7 | 8 | 9 | 21 | 26 | 27 | 28 | 29 |
30 | Initial CSS: Must be green 31 |
32 |
33 |

Hot Module Replacement

34 |
RED
35 |
GREEN
36 |
BLUE
37 |
38 |
39 |

Hot Module Replacement + CSS modules

40 |
RED
41 |
GREEN
42 |
BLUE
43 |
44 |
45 |

46 | Lazy CSS: Must be red, but turn green when 47 | . 48 |

49 |

50 | But turn orange, when 51 | . Additional 52 | clicks have no effect. 53 |

54 |

55 | Refresh and press buttons in reverse order: This should turn green 56 | instead. 57 |

58 |
59 |
60 |

61 | Lazy CSS: Turn off the network and 62 | . 63 |

64 |

An error should have appeared.

65 |

66 | Now if you turn the network back on and click it again, it should turn 67 | aqua. 68 |

69 |
70 |
71 |

Preloaded CSS: Must be green.

72 |

73 | displays 74 | an alert and should turn red. 75 |

76 |
77 |
78 |

Preloaded inlined CSS: Must be green.

79 |

80 | displays 81 | an alert and should turn red. 82 |

83 |
84 |
85 |

CrossOriginLoading Option: Must be red.

86 |

87 | loads chunks with crossorigin 88 | attribute and should turn green. 89 |

90 |
91 |
92 | 93 | 94 | zack 95 | 96 | -------------------------------------------------------------------------------- /test/cases/insert-string/src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | // eslint-disable-next-line 4 | import('./inject.css'); 5 | -------------------------------------------------------------------------------- /test/cases/insert-string/src/inject.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/insert-string/webpack.config.e2e.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const Self = require('../../../'); 4 | 5 | const ENABLE_HMR = 6 | typeof process.env.ENABLE_HMR !== 'undefined' 7 | ? Boolean(process.env.ENABLE_HMR) 8 | : false; 9 | 10 | const ENABLE_ES_MODULE = 11 | typeof process.env.ES_MODULE !== 'undefined' 12 | ? Boolean(process.env.ES_MODULE) 13 | : false; 14 | 15 | module.exports = { 16 | mode: 'development', 17 | output: { 18 | path: path.resolve(__dirname, 'expected'), 19 | chunkFilename: '[contenthash].js', 20 | publicPath: '/', 21 | crossOriginLoading: 'anonymous', 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.css$/, 27 | exclude: [/\.module\.css$/i], 28 | use: [ 29 | { 30 | loader: Self.loader, 31 | options: { 32 | hmr: ENABLE_HMR, 33 | }, 34 | }, 35 | { 36 | loader: 'css-loader', 37 | options: { 38 | esModule: ENABLE_ES_MODULE, 39 | }, 40 | }, 41 | ], 42 | }, 43 | { 44 | test: /\.module\.css$/i, 45 | use: [ 46 | { 47 | loader: Self.loader, 48 | options: { 49 | hmr: ENABLE_HMR, 50 | }, 51 | }, 52 | { 53 | loader: 'css-loader', 54 | options: { 55 | modules: true, 56 | esModule: ENABLE_ES_MODULE, 57 | }, 58 | }, 59 | ], 60 | }, 61 | ], 62 | }, 63 | plugins: [ 64 | new Self({ 65 | filename: '[name].css', 66 | chunkFilename: '[id].css', 67 | insert: '(linkTag) => { document.head.appendChild(linkTag) }', 68 | }), 69 | ], 70 | devServer: { 71 | contentBase: __dirname, 72 | port: 5000, 73 | headers: { 74 | 'Access-Control-Allow-Origin': '*', 75 | }, 76 | }, 77 | }; 78 | -------------------------------------------------------------------------------- /test/cases/insert-string/webpack.config.js: -------------------------------------------------------------------------------- 1 | const Self = require('../../../'); 2 | 3 | const ENABLE_HMR = 4 | typeof process.env.ENABLE_HMR !== 'undefined' 5 | ? Boolean(process.env.ENABLE_HMR) 6 | : false; 7 | 8 | const ENABLE_ES_MODULE = 9 | typeof process.env.ES_MODULE !== 'undefined' 10 | ? Boolean(process.env.ES_MODULE) 11 | : false; 12 | 13 | module.exports = { 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.css$/, 18 | exclude: [/\.module\.css$/i], 19 | use: [ 20 | { 21 | loader: Self.loader, 22 | options: { 23 | hmr: ENABLE_HMR, 24 | }, 25 | }, 26 | { 27 | loader: 'css-loader', 28 | options: { 29 | esModule: ENABLE_ES_MODULE, 30 | }, 31 | }, 32 | ], 33 | }, 34 | { 35 | test: /\.module\.css$/i, 36 | use: [ 37 | { 38 | loader: Self.loader, 39 | options: { 40 | hmr: ENABLE_HMR, 41 | }, 42 | }, 43 | { 44 | loader: 'css-loader', 45 | options: { 46 | modules: true, 47 | esModule: ENABLE_ES_MODULE, 48 | }, 49 | }, 50 | ], 51 | }, 52 | ], 53 | }, 54 | plugins: [ 55 | new Self({ 56 | filename: '[name].css', 57 | chunkFilename: '[id].css', 58 | insert: 'body', 59 | }), 60 | ], 61 | }; 62 | -------------------------------------------------------------------------------- /test/cases/js-hash/expected/style.922798e08e96756adb4a.1.css: -------------------------------------------------------------------------------- 1 | .wX52cuPepLZcpDx5S3yYO { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/js-hash/expected/style.fe78b7a6c50df391f00c.2.css: -------------------------------------------------------------------------------- 1 | 2 | .wX52cuPepLZcpDx5S3yYO { 3 | background: green; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/cases/js-hash/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable-next-line no-unused-expressions */ 2 | import(/* webpackChunkName: "style" */ './style.css'); 3 | -------------------------------------------------------------------------------- /test/cases/js-hash/loader.js: -------------------------------------------------------------------------------- 1 | module.exports = function loader(source) { 2 | const { number } = this.query; 3 | return source.split(/\/\* break \*\//)[number - 1]; 4 | }; 5 | -------------------------------------------------------------------------------- /test/cases/js-hash/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | background: red; 3 | } 4 | /* break */ 5 | .a { 6 | background: green; 7 | } 8 | -------------------------------------------------------------------------------- /test/cases/js-hash/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = [1, 2].map((n) => { 4 | return { 5 | entry: './index.js', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.css$/, 10 | use: [ 11 | { 12 | loader: Self.loader, 13 | options: { 14 | hmr: false, 15 | }, 16 | }, 17 | { 18 | loader: 'css-loader', 19 | options: { 20 | modules: true, 21 | }, 22 | }, 23 | { 24 | loader: './loader', 25 | ident: 'my-loader', 26 | options: { 27 | number: n, 28 | }, 29 | }, 30 | ], 31 | }, 32 | ], 33 | }, 34 | output: { 35 | filename: `[name].[contenthash].${n}.js`, 36 | }, 37 | plugins: [ 38 | new Self({ 39 | filename: `[name].[contenthash].${n}.css`, 40 | }), 41 | ], 42 | }; 43 | }); 44 | -------------------------------------------------------------------------------- /test/cases/moduleFilename/expected/demo/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: purple; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/moduleFilename/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/moduleFilename/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: purple; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/moduleFilename/webpack.config.js: -------------------------------------------------------------------------------- 1 | const Self = require('../../../'); 2 | 3 | module.exports = { 4 | entry: { 5 | 'demo/js/main': './index.js', 6 | }, 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.css$/, 11 | use: [Self.loader, 'css-loader'], 12 | }, 13 | ], 14 | }, 15 | output: { 16 | filename: '[name].js', 17 | }, 18 | plugins: [ 19 | new Self({ 20 | moduleFilename: ({ name }) => `${name.replace('/js/', '/css/')}.css`, 21 | }), 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /test/cases/moduleFilenameMutableFilename/expected/mutated.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: palegreen; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/moduleFilenameMutableFilename/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/moduleFilenameMutableFilename/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: palegreen; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/moduleFilenameMutableFilename/webpack.config.js: -------------------------------------------------------------------------------- 1 | const Self = require('../../../'); 2 | 3 | module.exports = { 4 | entry: { 5 | main: './index.js', 6 | }, 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.css$/, 11 | use: [Self.loader, 'css-loader'], 12 | }, 13 | ], 14 | }, 15 | output: { 16 | filename: '[name].js', 17 | }, 18 | plugins: [ 19 | (() => { 20 | const self = new Self({ filename: 'constructed.css' }); 21 | 22 | self.options.filename = 'mutated.css'; 23 | 24 | return self; 25 | })(), 26 | ], 27 | }; 28 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/async-one.js: -------------------------------------------------------------------------------- 1 | import './c.css'; 2 | import './d.css'; 3 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/async-two.js: -------------------------------------------------------------------------------- 1 | import './d.css'; 2 | import './c.css'; 3 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/c.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/d.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: yellow; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/expected/async-one.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: blue; 3 | } 4 | 5 | body { 6 | background: yellow; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/expected/async-two.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: yellow; 3 | } 4 | 5 | body { 6 | background: blue; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/expected/main-one.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | body { 6 | background: green; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/expected/main-two.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | } 4 | 5 | body { 6 | background: red; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/index-one.js: -------------------------------------------------------------------------------- 1 | import './a.css'; 2 | import './b.css'; 3 | 4 | /* eslint-disable-next-line no-unused-expressions */ 5 | import(/* webpackChunkName: 'async-one' */ './async-one'); 6 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/index-two.js: -------------------------------------------------------------------------------- 1 | import './b.css'; 2 | import './a.css'; 3 | 4 | /* eslint-disable-next-line no-unused-expressions */ 5 | import(/* webpackChunkName: 'async-two' */ './async-two'); 6 | -------------------------------------------------------------------------------- /test/cases/multiple-entry/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: { 5 | 'main-one': './index-one.js', 6 | 'main-two': './index-two.js', 7 | }, 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.css$/, 12 | use: [Self.loader, 'css-loader'], 13 | }, 14 | ], 15 | }, 16 | plugins: [ 17 | new Self({ 18 | filename: '[name].css', 19 | }), 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /test/cases/nested/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/nested/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/nested/component.css: -------------------------------------------------------------------------------- 1 | .component { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/nested/component.js: -------------------------------------------------------------------------------- 1 | import './component.css'; 2 | -------------------------------------------------------------------------------- /test/cases/nested/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | .component { 6 | background: blue; 7 | } 8 | 9 | body { 10 | background: green; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/cases/nested/index.js: -------------------------------------------------------------------------------- 1 | import './a.css'; 2 | import './component'; 3 | import './b.css'; 4 | -------------------------------------------------------------------------------- /test/cases/nested/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/no-source-map/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/no-source-map/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/no-source-map/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/no-source-map/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | // Required to disable source maps in webpack@4 6 | devtool: false, 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.css$/, 11 | use: [ 12 | { 13 | loader: Self.loader, 14 | }, 15 | { 16 | loader: 'css-loader', 17 | options: { 18 | sourceMap: false, 19 | }, 20 | }, 21 | ], 22 | }, 23 | ], 24 | }, 25 | plugins: [ 26 | new Self({ 27 | filename: '[name].css', 28 | }), 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /test/cases/publicpath-emptystring/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | background-image: url(c9e192c015437a21dea1faa1d30f4941.svg); 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/cases/publicpath-emptystring/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/publicpath-emptystring/react.svg: -------------------------------------------------------------------------------- 1 | logo-on-dark-bg -------------------------------------------------------------------------------- /test/cases/publicpath-emptystring/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | background-image: url(./react.svg); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/publicpath-emptystring/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | { 11 | loader: Self.loader, 12 | options: { 13 | publicPath: '', 14 | }, 15 | }, 16 | 'css-loader', 17 | ], 18 | }, 19 | { 20 | test: /\.(svg|png)$/, 21 | use: [ 22 | { 23 | loader: 'file-loader', 24 | options: { 25 | filename: '[name].[ext]', 26 | }, 27 | }, 28 | ], 29 | }, 30 | ], 31 | }, 32 | plugins: [ 33 | new Self({ 34 | filename: '[name].css', 35 | }), 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /test/cases/publicpath-function/expected/nested/again/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | background-image: url(../../c9e192c015437a21dea1faa1d30f4941.svg); 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/cases/publicpath-function/expected/nested/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | background-image: url(../c9e192c015437a21dea1faa1d30f4941.svg); 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/cases/publicpath-function/nested/again/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | background-image: url(../../react.svg); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/publicpath-function/nested/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | background-image: url(../react.svg); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/publicpath-function/react.svg: -------------------------------------------------------------------------------- 1 | logo-on-dark-bg -------------------------------------------------------------------------------- /test/cases/publicpath-function/webpack.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import Self from '../../../src'; 4 | 5 | module.exports = { 6 | entry: { 7 | // Specific CSS entry point, with output to a nested folder 8 | 'nested/style': './nested/style.css', 9 | // Note that relative nesting of output is the same as that of the input 10 | 'nested/again/style': './nested/again/style.css', 11 | }, 12 | module: { 13 | rules: [ 14 | { 15 | test: /\.css$/, 16 | use: [ 17 | { 18 | loader: Self.loader, 19 | options: { 20 | // Compute publicPath relative to the CSS output 21 | publicPath: (resourcePath, context) => 22 | `${path 23 | .relative(path.dirname(resourcePath), context) 24 | .replace(/\\/g, '/')}/`, 25 | }, 26 | }, 27 | 'css-loader', 28 | ], 29 | }, 30 | { 31 | test: /\.(svg|png)$/, 32 | use: [ 33 | { 34 | loader: 'file-loader', 35 | options: { 36 | filename: '[name].[ext]', 37 | }, 38 | }, 39 | ], 40 | }, 41 | ], 42 | }, 43 | plugins: [ 44 | new Self({ 45 | filename: '[name].css', 46 | }), 47 | ], 48 | }; 49 | -------------------------------------------------------------------------------- /test/cases/publicpath-trailing-slash/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | background-image: url(/static/img/c9e192c015437a21dea1faa1d30f4941.svg); 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/cases/publicpath-trailing-slash/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/publicpath-trailing-slash/react.svg: -------------------------------------------------------------------------------- 1 | logo-on-dark-bg -------------------------------------------------------------------------------- /test/cases/publicpath-trailing-slash/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | background-image: url(./react.svg); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/publicpath-trailing-slash/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | { 11 | loader: Self.loader, 12 | options: { 13 | publicPath: '/static/img', 14 | }, 15 | }, 16 | 'css-loader', 17 | ], 18 | }, 19 | { 20 | test: /\.(svg|png)$/, 21 | use: [ 22 | { 23 | loader: 'file-loader', 24 | options: { 25 | filename: '[name].[ext]', 26 | }, 27 | }, 28 | ], 29 | }, 30 | ], 31 | }, 32 | plugins: [ 33 | new Self({ 34 | filename: '[name].css', 35 | }), 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /test/cases/shared-import/a.css: -------------------------------------------------------------------------------- 1 | @import './shared.css'; 2 | 3 | .shared { 4 | color: red; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/shared-import/b.css: -------------------------------------------------------------------------------- 1 | @import './shared.css'; 2 | 3 | .shared { 4 | background-color: red; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/shared-import/c.css: -------------------------------------------------------------------------------- 1 | @import './shared.css'; 2 | 3 | .shared { 4 | border: 1px solid red; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/shared-import/expected/1.css: -------------------------------------------------------------------------------- 1 | .shared { 2 | border: 1px solid red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/shared-import/expected/main.css: -------------------------------------------------------------------------------- 1 | .shared { 2 | color: green; 3 | background-color: green; 4 | border: 1px solid green; 5 | } 6 | 7 | .shared { 8 | color: red; 9 | } 10 | 11 | .shared { 12 | background-color: red; 13 | } 14 | 15 | -------------------------------------------------------------------------------- /test/cases/shared-import/index.js: -------------------------------------------------------------------------------- 1 | import './a.css'; 2 | import './b.css'; 3 | 4 | /* eslint-disable-next-line no-unused-expressions */ 5 | import('./c.css'); 6 | -------------------------------------------------------------------------------- /test/cases/shared-import/shared.css: -------------------------------------------------------------------------------- 1 | .shared { 2 | color: green; 3 | background-color: green; 4 | border: 1px solid green; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/shared-import/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/async-one.js: -------------------------------------------------------------------------------- 1 | import './c.css'; 2 | import './d.css'; 3 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/async-two.js: -------------------------------------------------------------------------------- 1 | import './e.css'; 2 | import './f.css'; 3 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/c.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/d.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: yellow; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/e.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: purple; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/expected/async-one.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: blue; 3 | } 4 | 5 | body { 6 | background: yellow; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/expected/async-two.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: purple; 3 | } 4 | 5 | body { 6 | background: indigo; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | body { 6 | background: green; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/f.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: indigo; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/index.js: -------------------------------------------------------------------------------- 1 | import './a.css'; 2 | import './b.css'; 3 | 4 | /* eslint-disable-next-line no-unused-expressions */ 5 | import(/* webpackChunkName: 'async-one' */ './async-one'); 6 | /* eslint-disable-next-line no-unused-expressions */ 7 | import(/* webpackChunkName: 'async-two' */ './async-two'); 8 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css-fallback/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: { 5 | main: './index.js', 6 | }, 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.css$/, 11 | use: [Self.loader, 'css-loader'], 12 | }, 13 | ], 14 | }, 15 | plugins: [ 16 | function Plugin() { 17 | this.hooks.compilation.tap('Test', (compilation) => { 18 | compilation.hooks.beforeChunkAssets.tap('Test', () => { 19 | for (const chunkGroup of compilation.chunkGroups) { 20 | // remove getModuleIndex2 to enforce using fallback 21 | // eslint-disable-next-line no-undefined 22 | chunkGroup.getModuleIndex2 = undefined; 23 | } 24 | }); 25 | }); 26 | }, 27 | new Self({ 28 | filename: '[name].css', 29 | }), 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/async-one.js: -------------------------------------------------------------------------------- 1 | import './c.css'; 2 | import './d.css'; 3 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/async-two.js: -------------------------------------------------------------------------------- 1 | import './e.css'; 2 | import './f.css'; 3 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/c.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/d.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: yellow; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/e.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: purple; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/expected/async-one.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: blue; 3 | } 4 | 5 | body { 6 | background: yellow; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/expected/async-two.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: purple; 3 | } 4 | 5 | body { 6 | background: indigo; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | body { 6 | background: green; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/f.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: indigo; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/index.js: -------------------------------------------------------------------------------- 1 | import './a.css'; 2 | import './b.css'; 3 | 4 | /* eslint-disable-next-line no-unused-expressions */ 5 | import(/* webpackChunkName: 'async-one' */ './async-one'); 6 | /* eslint-disable-next-line no-unused-expressions */ 7 | import(/* webpackChunkName: 'async-two' */ './async-two'); 8 | -------------------------------------------------------------------------------- /test/cases/simple-async-load-css/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: { 5 | main: './index.js', 6 | }, 7 | module: { 8 | rules: [ 9 | { 10 | test: /\.css$/, 11 | use: [Self.loader, 'css-loader'], 12 | }, 13 | ], 14 | }, 15 | plugins: [ 16 | new Self({ 17 | filename: '[name].css', 18 | }), 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/async.css: -------------------------------------------------------------------------------- 1 | .async { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/async.js: -------------------------------------------------------------------------------- 1 | import './in-async.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/expected/1.css: -------------------------------------------------------------------------------- 1 | .in-async { 2 | background: green; 3 | } 4 | 5 | 6 | /*# sourceMappingURL=1.css.map*/ -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/expected/1.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///./in-async.css"],"names":[],"mappings":"AAAA;AACA;AACA","file":"1.css","sourcesContent":[".in-async {\n background: green;\n}\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/expected/2.css: -------------------------------------------------------------------------------- 1 | .async { 2 | background: blue; 3 | } 4 | 5 | 6 | /*# sourceMappingURL=2.css.map*/ -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/expected/2.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///./async.css"],"names":[],"mappings":"AAAA;AACA;AACA","file":"2.css","sourcesContent":[".async {\n background: blue;\n}\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | 6 | /*# sourceMappingURL=main.css.map*/ -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/expected/main.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///./main.css"],"names":[],"mappings":"AAAA;AACA;AACA","file":"main.css","sourcesContent":["body {\n background: red;\n}\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/in-async.css: -------------------------------------------------------------------------------- 1 | .in-async { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/index.js: -------------------------------------------------------------------------------- 1 | import './main.css'; 2 | 3 | /* eslint-disable-next-line no-unused-expressions */ 4 | import('./async'); 5 | 6 | /* eslint-disable-next-line no-unused-expressions */ 7 | import('./async.css'); 8 | -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async-source-map/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | devtool: 'source-map', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.css$/, 10 | use: [Self.loader, 'css-loader'], 11 | }, 12 | ], 13 | }, 14 | plugins: [ 15 | new Self({ 16 | filename: '[name].css', 17 | }), 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /test/cases/simple-async/async.css: -------------------------------------------------------------------------------- 1 | .async { 2 | background: blue; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async/async.js: -------------------------------------------------------------------------------- 1 | import './in-async.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple-async/expected/1.css: -------------------------------------------------------------------------------- 1 | .in-async { 2 | background: green; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/simple-async/expected/2.css: -------------------------------------------------------------------------------- 1 | .async { 2 | background: blue; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/simple-async/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/simple-async/in-async.css: -------------------------------------------------------------------------------- 1 | .in-async { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async/index.js: -------------------------------------------------------------------------------- 1 | import './main.css'; 2 | 3 | /* eslint-disable-next-line no-unused-expressions */ 4 | import('./async'); 5 | 6 | /* eslint-disable-next-line no-unused-expressions */ 7 | import('./async.css'); 8 | -------------------------------------------------------------------------------- /test/cases/simple-async/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-async/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/simple-commonjs-syntax/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/simple-commonjs-syntax/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple-commonjs-syntax/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-commonjs-syntax/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | Self.loader, 11 | { 12 | loader: 'css-loader', 13 | // TODO Uncomment after `css-loader` release the `esModule` option 14 | // options: { esModule: false }, 15 | }, 16 | ], 17 | }, 18 | ], 19 | }, 20 | plugins: [ 21 | new Self({ 22 | filename: '[name].css', 23 | }), 24 | ], 25 | }; 26 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-global/expected/main.css: -------------------------------------------------------------------------------- 1 | .a { 2 | background: red; 3 | } 4 | 5 | .foo__style__b { 6 | color: green; 7 | } 8 | 9 | .c { 10 | color: blue; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-global/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-global/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | background: red; 3 | } 4 | 5 | :local(.b) { 6 | color: green; 7 | } 8 | 9 | :global(.c) { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-global/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | Self.loader, 11 | { 12 | loader: 'css-loader', 13 | options: { 14 | modules: { 15 | mode: 'global', 16 | localIdentName: 'foo__[name]__[local]', 17 | }, 18 | }, 19 | }, 20 | ], 21 | }, 22 | ], 23 | }, 24 | plugins: [ 25 | new Self({ 26 | filename: '[name].css', 27 | }), 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-local/expected/main.css: -------------------------------------------------------------------------------- 1 | .foo__style__a { 2 | background: red; 3 | } 4 | 5 | .foo__style__b { 6 | color: green; 7 | } 8 | 9 | .c { 10 | color: blue; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-local/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-local/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | background: red; 3 | } 4 | 5 | :local(.b) { 6 | color: green; 7 | } 8 | 9 | :global(.c) { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-local/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | Self.loader, 11 | { 12 | loader: 'css-loader', 13 | options: { 14 | modules: { 15 | mode: 'local', 16 | localIdentName: 'foo__[name]__[local]', 17 | }, 18 | }, 19 | }, 20 | ], 21 | }, 22 | ], 23 | }, 24 | plugins: [ 25 | new Self({ 26 | filename: '[name].css', 27 | }), 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-pure/expected/main.css: -------------------------------------------------------------------------------- 1 | .foo__style__a { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-pure/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-pure/style.css: -------------------------------------------------------------------------------- 1 | .a { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-css-modules-mode-pure/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | Self.loader, 11 | { 12 | loader: 'css-loader', 13 | options: { 14 | modules: { 15 | mode: 'pure', 16 | localIdentName: 'foo__[name]__[local]', 17 | }, 18 | }, 19 | }, 20 | ], 21 | }, 22 | ], 23 | }, 24 | plugins: [ 25 | new Self({ 26 | filename: '[name].css', 27 | }), 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /test/cases/simple-es-module-syntax/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/simple-es-module-syntax/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple-es-module-syntax/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-es-module-syntax/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | Self.loader, 11 | { 12 | loader: 'css-loader', 13 | // TODO Uncomment after `css-loader` release the `esModule` option 14 | // options: { esModule: true }, 15 | }, 16 | ], 17 | }, 18 | ], 19 | }, 20 | plugins: [ 21 | new Self({ 22 | filename: '[name].css', 23 | }), 24 | ], 25 | }; 26 | -------------------------------------------------------------------------------- /test/cases/simple-multiple/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-multiple/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: green; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-multiple/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | body { 6 | background: green; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /test/cases/simple-multiple/index.js: -------------------------------------------------------------------------------- 1 | import './a.css'; 2 | import './b.css'; 3 | -------------------------------------------------------------------------------- /test/cases/simple-multiple/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/simple-publicpath/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | background-image: url(/static/img/c9e192c015437a21dea1faa1d30f4941.svg); 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/cases/simple-publicpath/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple-publicpath/react.svg: -------------------------------------------------------------------------------- 1 | logo-on-dark-bg -------------------------------------------------------------------------------- /test/cases/simple-publicpath/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | background-image: url(./react.svg); 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/simple-publicpath/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [ 10 | { 11 | loader: Self.loader, 12 | options: { 13 | publicPath: '/static/img/', 14 | }, 15 | }, 16 | 'css-loader', 17 | ], 18 | }, 19 | { 20 | test: /\.(svg|png)$/, 21 | use: [ 22 | { 23 | loader: 'file-loader', 24 | options: { 25 | filename: '[name].[ext]', 26 | }, 27 | }, 28 | ], 29 | }, 30 | ], 31 | }, 32 | plugins: [ 33 | new Self({ 34 | filename: '[name].css', 35 | }), 36 | ], 37 | }; 38 | -------------------------------------------------------------------------------- /test/cases/simple/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/simple/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/simple/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | plugins: [ 14 | new Self({ 15 | filename: '[name].css', 16 | }), 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /test/cases/source-map/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | 6 | /*# sourceMappingURL=main.css.map*/ -------------------------------------------------------------------------------- /test/cases/source-map/index.js: -------------------------------------------------------------------------------- 1 | import './style.css'; 2 | -------------------------------------------------------------------------------- /test/cases/source-map/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/source-map/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | devtool: 'source-map', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.css$/, 10 | use: [ 11 | { 12 | loader: Self.loader, 13 | }, 14 | { 15 | loader: 'css-loader', 16 | options: { 17 | sourceMap: true, 18 | }, 19 | }, 20 | ], 21 | }, 22 | ], 23 | }, 24 | plugins: [ 25 | new Self({ 26 | filename: '[name].css', 27 | }), 28 | ], 29 | }; 30 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'a'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'b'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/c.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'c'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/chunk1.js: -------------------------------------------------------------------------------- 1 | import './c.css'; 2 | import './d.css'; 3 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/chunk2.js: -------------------------------------------------------------------------------- 1 | import './d.css'; 2 | import './h.css'; 3 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/d.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'd'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/e1.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e1'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/e2.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'e2'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/entry1.js: -------------------------------------------------------------------------------- 1 | import './a.css'; 2 | import './e1.css'; 3 | import './e2.css'; 4 | import './f.css'; 5 | 6 | /* eslint-disable-next-line no-unused-expressions */ 7 | import('./chunk1'); 8 | /* eslint-disable-next-line no-unused-expressions */ 9 | import('./chunk2'); 10 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/entry2.js: -------------------------------------------------------------------------------- 1 | import './b.css'; 2 | import './e2.css'; 3 | import './e1.css'; 4 | import './g.css'; 5 | import './h.css'; 6 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/expected/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'a'; 3 | } 4 | 5 | body { 6 | content: 'b'; 7 | } 8 | 9 | body { 10 | content: 'c'; 11 | } 12 | 13 | body { 14 | content: 'd'; 15 | } 16 | 17 | body { 18 | content: 'e1'; 19 | } 20 | 21 | body { 22 | content: 'e2'; 23 | } 24 | 25 | body { 26 | content: 'f'; 27 | } 28 | 29 | body { 30 | content: 'g'; 31 | } 32 | 33 | body { 34 | content: 'h'; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/f.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'f'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/g.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'g'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/h.css: -------------------------------------------------------------------------------- 1 | body { 2 | content: 'h'; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks-single/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: { 5 | entry1: './entry1.js', 6 | entry2: './entry2.js', 7 | }, 8 | module: { 9 | rules: [ 10 | { 11 | test: /\.css$/, 12 | use: [Self.loader, 'css-loader'], 13 | }, 14 | ], 15 | }, 16 | optimization: { 17 | splitChunks: { 18 | cacheGroups: { 19 | styles: { 20 | name: 'styles', 21 | chunks: 'all', 22 | test: /\.css$/, 23 | enforce: true, 24 | }, 25 | }, 26 | }, 27 | }, 28 | plugins: [ 29 | new Self({ 30 | filename: '[name].css', 31 | }), 32 | ], 33 | }; 34 | -------------------------------------------------------------------------------- /test/cases/split-chunks/expected/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | 5 | -------------------------------------------------------------------------------- /test/cases/split-chunks/expected/vendors.css: -------------------------------------------------------------------------------- 1 | /* This could be bootstrap.css */ 2 | body { 3 | background: green; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /test/cases/split-chunks/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | import 'bootstrap.css'; 3 | import './style.css'; 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/split-chunks/webpack.config.js: -------------------------------------------------------------------------------- 1 | import Self from '../../../src'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | rules: [ 7 | { 8 | test: /\.css$/, 9 | use: [Self.loader, 'css-loader'], 10 | }, 11 | ], 12 | }, 13 | optimization: { 14 | splitChunks: { 15 | chunks: 'all', 16 | cacheGroups: { 17 | vendors: { 18 | name: 'vendors', 19 | test: /node_modules/, 20 | enforce: true, 21 | }, 22 | }, 23 | }, 24 | }, 25 | plugins: [ 26 | new Self({ 27 | filename: '[name].css', 28 | }), 29 | ], 30 | }; 31 | -------------------------------------------------------------------------------- /test/cjs.test.js: -------------------------------------------------------------------------------- 1 | import MiniCssExtractPlugin from '../src'; 2 | import CJSMiniCssExtractPlugin from '../src/cjs'; 3 | 4 | describe('CJS', () => { 5 | it('should exported plugin', () => { 6 | expect(CJSMiniCssExtractPlugin).toEqual(MiniCssExtractPlugin); 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /test/fixtures/simple.css: -------------------------------------------------------------------------------- 1 | .foo { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/simple.js: -------------------------------------------------------------------------------- 1 | import './simple.css'; 2 | -------------------------------------------------------------------------------- /test/helpers/compile.js: -------------------------------------------------------------------------------- 1 | export default (compiler) => { 2 | return 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 | }; 12 | -------------------------------------------------------------------------------- /test/helpers/getCompiler.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import webpack from 'webpack'; 4 | import { createFsFromVolume, Volume } from 'memfs'; 5 | 6 | import MiniCssExtractPlugin from '../../src'; 7 | 8 | export default (fixture, loaderOptions = {}, config = {}) => { 9 | const fullConfig = { 10 | mode: 'development', 11 | devtool: config.devtool || false, 12 | context: path.resolve(__dirname, '../fixtures'), 13 | entry: path.resolve(__dirname, '../fixtures', fixture), 14 | output: { 15 | path: path.resolve(__dirname, '../outputs'), 16 | filename: '[name].bundle.js', 17 | chunkFilename: '[name].chunk.js', 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.css$/i, 23 | rules: [ 24 | { 25 | loader: MiniCssExtractPlugin.loader, 26 | options: loaderOptions || {}, 27 | }, 28 | { 29 | loader: 'css-loader', 30 | }, 31 | ], 32 | }, 33 | ], 34 | }, 35 | plugins: [ 36 | new MiniCssExtractPlugin({ 37 | // Options similar to the same options in webpackOptions.output 38 | // both options are optional 39 | filename: '[name].css', 40 | chunkFilename: '[id].css', 41 | }), 42 | ], 43 | ...config, 44 | }; 45 | 46 | const compiler = webpack(fullConfig); 47 | 48 | if (!config.outputFileSystem) { 49 | const outputFileSystem = createFsFromVolume(new Volume()); 50 | // Todo remove when we drop webpack@4 support 51 | outputFileSystem.join = path.join.bind(path); 52 | 53 | compiler.outputFileSystem = outputFileSystem; 54 | } 55 | 56 | return compiler; 57 | }; 58 | -------------------------------------------------------------------------------- /test/helpers/index.js: -------------------------------------------------------------------------------- 1 | import compile from './compile'; 2 | import getCompiler from './getCompiler'; 3 | 4 | export { compile, getCompiler }; 5 | -------------------------------------------------------------------------------- /test/ignoreOrder-option.test.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import webpack from 'webpack'; 4 | 5 | describe('IgnoreOrder', () => { 6 | it('should emit warnings', (done) => { 7 | const casesDirectory = path.resolve(__dirname, 'cases'); 8 | const directoryForCase = path.resolve(casesDirectory, 'ignoreOrderFalse'); 9 | // eslint-disable-next-line import/no-dynamic-require, global-require 10 | const webpackConfig = require(path.resolve( 11 | directoryForCase, 12 | 'webpack.config.js' 13 | )); 14 | const compiler = webpack({ 15 | ...webpackConfig, 16 | mode: 'development', 17 | context: directoryForCase, 18 | cache: false, 19 | }); 20 | compiler.run((err1, stats) => { 21 | expect(stats.hasWarnings()).toBe(true); 22 | done(); 23 | }); 24 | }); 25 | 26 | it('should not emit warnings', (done) => { 27 | const casesDirectory = path.resolve(__dirname, 'cases'); 28 | const directoryForCase = path.resolve(casesDirectory, 'ignoreOrder'); 29 | // eslint-disable-next-line import/no-dynamic-require, global-require 30 | const webpackConfig = require(path.resolve( 31 | directoryForCase, 32 | 'webpack.config.js' 33 | )); 34 | const compiler = webpack({ 35 | ...webpackConfig, 36 | mode: 'development', 37 | context: directoryForCase, 38 | cache: false, 39 | }); 40 | compiler.run((err1, stats) => { 41 | expect(stats.hasWarnings()).toBe(false); 42 | done(); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/inject-option.test.js: -------------------------------------------------------------------------------- 1 | /* global page, document, getComputedStyle */ 2 | 3 | const { setup: setupDevServer } = require('jest-dev-server'); 4 | 5 | describe('insert-options', () => { 6 | describe('insert-string', () => { 7 | beforeAll(async () => { 8 | await setupDevServer({ 9 | command: 10 | 'webpack-dev-server test/cases/insert-string/src/index.js --config test/cases/insert-string/webpack.config.e2e.js', 11 | port: 5000, 12 | launchTimeout: 10000, 13 | }); 14 | await page.goto('http://localhost:5000/'); 15 | }); 16 | it('style preload was injected into body', async () => { 17 | // preloaded1 + main + inject 18 | await expect(await page.$$eval('[type="text/css"]', links => links.length)).toEqual(3); 19 | // inject 20 | await expect(await page.$$eval('[rel="preload"]', preloads => preloads.length)).toEqual(1); 21 | }); 22 | 23 | it('body background style was not set', async () => { 24 | const bodyStyle = await page.evaluate(() => 25 | getComputedStyle(document.body).getPropertyValue('background-color') 26 | ); 27 | 28 | await expect(bodyStyle).toBe('rgb(255, 0, 0)'); 29 | }); 30 | }); 31 | 32 | describe('insert-function', () => { 33 | beforeAll(async () => { 34 | await setupDevServer({ 35 | command: `webpack-dev-server test/cases/insert-function/src/index.js --config test/cases/insert-function/webpack.config.e2e.js`, 36 | port: 3001, 37 | launchTimeout: 15000, 38 | }); 39 | await page.goto('http://localhost:3001/'); 40 | }); 41 | it('style preload was injected into body', async () => { 42 | // preloaded1 + main + inject 43 | await expect(await page.$$eval('[type="text/css"]', links => links.length)).toEqual(3); 44 | // inject 45 | await expect(await page.$$eval('[rel="preload"]', preloads => preloads.length)).toEqual(1); 46 | }); 47 | 48 | it('body background style was not set', async () => { 49 | await page.waitFor(4000); 50 | const bodyStyle = await page.evaluate(() => 51 | getComputedStyle(document.body).getPropertyValue('background-color') 52 | ); 53 | 54 | await expect(bodyStyle).toBe('rgb(255, 0, 0)'); 55 | }); 56 | }); 57 | 58 | afterAll(() => { 59 | // eslint-disable-next-line 60 | const childProcess = require('child_process').exec; 61 | childProcess(`kill $(lsof -t -i:3001)`); 62 | childProcess(`kill $(lsof -t -i:5000)`); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /test/manual/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/faceyspacey/extract-css-chunks-webpack-plugin/18837c2ddc4175313cfd812698e7fae4ae4a7acc/test/manual/README.md -------------------------------------------------------------------------------- /test/manual/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | extract-css-chunks-webpack-plugin testcase 7 | 8 | 9 | 21 | 26 | 27 | 28 | 29 |
30 | Initial CSS: Must be green 31 |
32 |
33 |

Hot Module Replacement

34 |
RED
35 |
GREEN
36 |
BLUE
37 |
38 |
39 |

Hot Module Replacement + CSS modules

40 |
RED
41 |
GREEN
42 |
BLUE
43 |
44 |
45 |

Lazy CSS: Must be red, but turn green when .

46 |

But turn orange, when . Additional clicks have no effect.

47 |

Refresh and press buttons in reverse order: This should turn green instead.

48 |
49 |
50 |

Lazy CSS: Turn off the network and .

51 |

An error should have appeared.

52 |

Now if you turn the network back on and click it again, it should turn aqua.

53 |
54 |
55 |

Preloaded CSS: Must be green.

56 |

displays an alert and should turn red.

57 |
58 |
59 |

Preloaded inlined CSS: Must be green.

60 |

displays an alert and should turn red.

61 |
62 |
63 |

CrossOriginLoading Option: Must be red.

64 |

loads chunks with crossorigin attribute and should turn green.

65 |
66 |
67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /test/manual/src/crossorigin.css: -------------------------------------------------------------------------------- 1 | .crossorigin { 2 | background: lightgreen; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/crossorigin.js: -------------------------------------------------------------------------------- 1 | import './crossorigin.css'; 2 | -------------------------------------------------------------------------------- /test/manual/src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | /* global __webpack_public_path__ */ 3 | /* eslint-disable no-console, camelcase, no-global-assign */ 4 | 5 | import './initial.css'; 6 | import './simple.css'; 7 | import classes from './simple.module.css'; 8 | 9 | console.log('___CLASSES__'); 10 | console.log(classes); 11 | 12 | function replaceClass(originalClass, newClass) { 13 | const nodes = document.querySelectorAll(`.${originalClass}`); 14 | 15 | nodes.forEach((node) => { 16 | const { classList } = node; 17 | classList.remove(originalClass); 18 | classList.add(newClass); 19 | }); 20 | } 21 | 22 | Object.keys(classes).forEach((localClass) => { 23 | replaceClass(localClass, classes[localClass]); 24 | }); 25 | 26 | const handleError = (err) => { 27 | document.querySelector('.errors').textContent += `\n${err.toString()}`; 28 | console.error(err); 29 | }; 30 | 31 | const makeButton = (className, fn, shouldDisable = true) => { 32 | const button = document.querySelector(className); 33 | button.addEventListener('click', () => { 34 | if (shouldDisable) { 35 | button.disabled = true; 36 | } 37 | fn() 38 | .then(() => { 39 | button.disabled = false; 40 | }) 41 | .catch(handleError); 42 | }); 43 | }; 44 | 45 | makeButton('.lazy-button', () => import('./lazy.js')); 46 | makeButton('.lazy-button2', () => import('./lazy2.css')); 47 | 48 | makeButton('.preloaded-button1', () => 49 | import(/* webpackChunkName: "preloaded1" */ './preloaded1') 50 | ); 51 | makeButton('.preloaded-button2', () => 52 | import(/* webpackChunkName: "preloaded2" */ './preloaded2') 53 | ); 54 | 55 | makeButton('.lazy-failure-button', () => import('./lazy-failure.js'), false); 56 | 57 | makeButton('.crossorigin', () => { 58 | const originalPublicPath = __webpack_public_path__; 59 | __webpack_public_path__ = 'http://0.0.0.0:8080/dist/'; 60 | const promise = import('./crossorigin').then(() => { 61 | const lastTwoElements = Array.from(document.head.children).slice(-2); 62 | const hasCrossorigin = lastTwoElements.every( 63 | (element) => element.crossOrigin === 'anonymous' 64 | ); 65 | if (!hasCrossorigin) { 66 | throw new Error('Chunks miss crossorigin="anonymous" attribute.'); 67 | } 68 | }); 69 | __webpack_public_path__ = originalPublicPath; 70 | return promise; 71 | }); 72 | -------------------------------------------------------------------------------- /test/manual/src/initial.css: -------------------------------------------------------------------------------- 1 | .initial-css { 2 | background: lightgreen; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/lazy-failure.css: -------------------------------------------------------------------------------- 1 | .lazy-failure-css { 2 | background: aqua; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/lazy-failure.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import './lazy-failure.css'; 4 | -------------------------------------------------------------------------------- /test/manual/src/lazy.css: -------------------------------------------------------------------------------- 1 | .lazy-css { 2 | background: lightgreen; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/lazy.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | 3 | import './lazy.css'; 4 | -------------------------------------------------------------------------------- /test/manual/src/lazy2.css: -------------------------------------------------------------------------------- 1 | .lazy-css { 2 | background: peru; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/preloaded1.css: -------------------------------------------------------------------------------- 1 | .preloaded-css1 { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/preloaded1.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | /* eslint-disable no-alert */ 3 | 4 | import './preloaded1.css'; 5 | 6 | alert('Ok'); 7 | -------------------------------------------------------------------------------- /test/manual/src/preloaded2.css: -------------------------------------------------------------------------------- 1 | .preloaded-css2 { 2 | background: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/manual/src/preloaded2.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | /* eslint-disable no-alert */ 3 | 4 | import './preloaded2.css'; 5 | 6 | alert('Ok'); 7 | -------------------------------------------------------------------------------- /test/manual/src/simple.css: -------------------------------------------------------------------------------- 1 | .r { 2 | color: red; 3 | } 4 | 5 | .g { 6 | color: green; 7 | } 8 | 9 | .b { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /test/manual/src/simple.module.css: -------------------------------------------------------------------------------- 1 | .rr { 2 | color: red; 3 | } 4 | 5 | .gg { 6 | color: green; 7 | } 8 | 9 | .bb { 10 | color: blue; 11 | } 12 | -------------------------------------------------------------------------------- /test/manual/webpack.config.js: -------------------------------------------------------------------------------- 1 | const Self = require('../../'); 2 | 3 | const ENABLE_HMR = 4 | typeof process.env.ENABLE_HMR !== 'undefined' 5 | ? Boolean(process.env.ENABLE_HMR) 6 | : false; 7 | 8 | const ENABLE_ES_MODULE = 9 | typeof process.env.ES_MODULE !== 'undefined' 10 | ? Boolean(process.env.ES_MODULE) 11 | : false; 12 | 13 | module.exports = { 14 | mode: 'development', 15 | output: { 16 | chunkFilename: '[contenthash].js', 17 | publicPath: '/dist/', 18 | crossOriginLoading: 'anonymous', 19 | }, 20 | module: { 21 | rules: [ 22 | { 23 | test: /\.css$/, 24 | exclude: [/\.module\.css$/i], 25 | use: [ 26 | { 27 | loader: Self.loader, 28 | options: { 29 | hmr: ENABLE_HMR, 30 | }, 31 | }, 32 | { 33 | loader: 'css-loader', 34 | options: { 35 | esModule: ENABLE_ES_MODULE, 36 | }, 37 | }, 38 | ], 39 | }, 40 | { 41 | test: /\.module\.css$/i, 42 | use: [ 43 | { 44 | loader: Self.loader, 45 | options: { 46 | hmr: ENABLE_HMR, 47 | }, 48 | }, 49 | { 50 | loader: 'css-loader', 51 | options: { 52 | modules: true, 53 | esModule: ENABLE_ES_MODULE, 54 | }, 55 | }, 56 | ], 57 | }, 58 | ], 59 | }, 60 | plugins: [ 61 | new Self({ 62 | filename: '[name].css', 63 | chunkFilename: '[contenthash].css', 64 | }), 65 | ], 66 | devServer: { 67 | contentBase: __dirname, 68 | headers: { 69 | 'Access-Control-Allow-Origin': '*', 70 | }, 71 | }, 72 | }; 73 | -------------------------------------------------------------------------------- /test/validate-loader-options.test.js: -------------------------------------------------------------------------------- 1 | import { getCompiler, compile } from './helpers'; 2 | 3 | describe('validate options', () => { 4 | const tests = { 5 | publicPath: { 6 | success: ['/public/path/to/'], 7 | failure: [true], 8 | }, 9 | esModule: { 10 | success: [true, false], 11 | failure: [1], 12 | }, 13 | hmr: { 14 | success: [true, false], 15 | failure: [1], 16 | }, 17 | reloadAll: { 18 | success: [true, false], 19 | failure: [1], 20 | }, 21 | unknown: { 22 | success: [], 23 | // TODO failed in next release 24 | // failure: [1, true, false, 'test', /test/, [], {}, { foo: 'bar' }], 25 | }, 26 | }; 27 | 28 | function stringifyValue(value) { 29 | if ( 30 | Array.isArray(value) || 31 | (value && typeof value === 'object' && value.constructor === Object) 32 | ) { 33 | return JSON.stringify(value); 34 | } 35 | 36 | return value; 37 | } 38 | 39 | async function createTestCase(key, value, type) { 40 | it(`should ${ 41 | type === 'success' ? 'successfully validate' : 'throw an error on' 42 | } the "${key}" option with "${stringifyValue(value)}" value`, async () => { 43 | const compiler = getCompiler('simple.js', { [key]: value }); 44 | 45 | let stats; 46 | 47 | try { 48 | stats = await compile(compiler); 49 | } finally { 50 | if (type === 'success') { 51 | expect(stats.hasErrors()).toBe(false); 52 | } else if (type === 'failure') { 53 | const { 54 | compilation: { errors }, 55 | } = stats; 56 | 57 | expect(errors).toHaveLength(1); 58 | expect(() => { 59 | throw new Error(errors[0].error.message); 60 | }).toThrowErrorMatchingSnapshot(); 61 | } 62 | } 63 | }); 64 | } 65 | 66 | for (const [key, values] of Object.entries(tests)) { 67 | for (const type of Object.keys(values)) { 68 | for (const value of values[type]) { 69 | createTestCase(key, value, type); 70 | } 71 | } 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /test/validate-plugin-options.test.js: -------------------------------------------------------------------------------- 1 | import ExtractCssChunks from '../src'; 2 | 3 | describe('validate options', () => { 4 | const tests = { 5 | filename: { 6 | success: ['[name].css'], 7 | failure: [true], 8 | }, 9 | chunkFilename: { 10 | success: ['[id].css'], 11 | failure: [true], 12 | }, 13 | moduleFilename: { 14 | success: [({ name }) => `${name.replace('/js/', '/css/')}.css`], 15 | failure: [true], 16 | }, 17 | ignoreOrder: { 18 | success: [true, false], 19 | failure: [1], 20 | }, 21 | inject: { success: [true, true] }, 22 | unknown: { 23 | success: [], 24 | // TODO failed in next release 25 | // failure: [1, true, false, 'test', /test/, [], {}, { foo: 'bar' }], 26 | }, 27 | }; 28 | 29 | function stringifyValue(value) { 30 | if ( 31 | Array.isArray(value) || 32 | (value && typeof value === 'object' && value.constructor === Object) 33 | ) { 34 | return JSON.stringify(value); 35 | } 36 | 37 | return value; 38 | } 39 | 40 | async function createTestCase(key, value, type) { 41 | it(`should ${ 42 | type === 'success' ? 'successfully validate' : 'throw an error on' 43 | } the "${key}" option with "${stringifyValue(value)}" value`, async () => { 44 | let error; 45 | 46 | try { 47 | // eslint-disable-next-line no-new 48 | new ExtractCssChunks({ [key]: value }); 49 | } catch (errorFromPlugin) { 50 | if (errorFromPlugin.name !== 'ValidationError') { 51 | throw errorFromPlugin; 52 | } 53 | 54 | error = errorFromPlugin; 55 | } finally { 56 | if (type === 'success') { 57 | expect(error).toBeUndefined(); 58 | } else if (type === 'failure') { 59 | expect(() => { 60 | throw error; 61 | }).toThrowErrorMatchingSnapshot(); 62 | } 63 | } 64 | }); 65 | } 66 | 67 | for (const [key, values] of Object.entries(tests)) { 68 | for (const type of Object.keys(values)) { 69 | for (const value of values[type]) { 70 | createTestCase(key, value, type); 71 | } 72 | } 73 | } 74 | }); 75 | --------------------------------------------------------------------------------