├── test ├── cases │ ├── nested │ │ ├── a.txt │ │ ├── b.txt │ │ ├── c.txt │ │ ├── d.txt │ │ ├── expected │ │ │ └── file.css │ │ ├── index.js │ │ ├── file.js │ │ └── webpack.config.js │ ├── simple │ │ ├── a.txt │ │ ├── b.txt │ │ ├── expected │ │ │ └── file.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── common-async │ │ ├── a.txt │ │ ├── b.txt │ │ ├── c.txt │ │ ├── expected │ │ │ └── file.css │ │ ├── a.js │ │ ├── b.js │ │ ├── index.js │ │ └── webpack.config.js │ ├── merging-chunk │ │ ├── a.txt │ │ ├── b.txt │ │ ├── c.txt │ │ ├── expected │ │ │ └── file.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── optimize-tree │ │ ├── a.txt │ │ ├── b.txt │ │ ├── index.txt │ │ ├── expected │ │ │ └── main.txt │ │ ├── a.js │ │ ├── b.js │ │ ├── index.js │ │ └── webpack.config.js │ ├── splitted-chunk │ │ ├── a.txt │ │ ├── b.txt │ │ ├── expected │ │ │ └── file.css │ │ ├── index.js │ │ └── webpack.config.js │ ├── hashed-module-ids │ │ ├── a.txt │ │ ├── b.txt │ │ ├── index.txt │ │ ├── expected │ │ │ └── main.txt │ │ ├── a.js │ │ ├── b.js │ │ ├── index.js │ │ └── webpack.config.js │ ├── multiple-entries │ │ ├── a.txt │ │ ├── b.txt │ │ ├── c.txt │ │ ├── expected │ │ │ ├── a.txt │ │ │ └── b.txt │ │ ├── a.js │ │ ├── b.js │ │ └── webpack.config.js │ ├── merging-multiple-entries │ │ ├── a.txt │ │ ├── b.txt │ │ ├── c.txt │ │ ├── expected │ │ │ ├── a.txt │ │ │ └── b.txt │ │ ├── a.js │ │ ├── b.js │ │ └── webpack.config.js │ ├── multiple-entries-filename │ │ ├── a.txt │ │ ├── b.txt │ │ ├── c.txt │ │ ├── expected │ │ │ ├── a.txt │ │ │ ├── b.txt │ │ │ └── txt │ │ │ │ ├── a.txt │ │ │ │ ├── b.txt │ │ │ │ └── .DS_Store │ │ ├── a.js │ │ ├── b.js │ │ └── webpack.config.js │ ├── splitted-mix-multiple-entries │ │ ├── a.txt │ │ ├── b.txt │ │ ├── c.txt │ │ ├── expected │ │ │ ├── a.txt │ │ │ └── b.txt │ │ ├── c.js │ │ ├── a.js │ │ ├── b.js │ │ └── webpack.config.js │ ├── splitted-multiple-entries │ │ ├── a.txt │ │ ├── b.txt │ │ ├── c.txt │ │ ├── expected │ │ │ ├── a.txt │ │ │ └── b.txt │ │ ├── a.js │ │ ├── b.js │ │ └── webpack.config.js │ ├── merging-multiple-entries-shared │ │ ├── a.txt │ │ ├── expected │ │ │ ├── a.txt │ │ │ └── b.txt │ │ ├── a.js │ │ ├── b.js │ │ └── webpack.config.js │ ├── order-undefined-error │ │ ├── c1.css │ │ ├── c2.css │ │ ├── index.js │ │ ├── a.css │ │ ├── b.css │ │ ├── expected │ │ │ └── file.css │ │ └── webpack.config.js │ ├── chunk-modules-css-wrong-order │ │ ├── b.js │ │ ├── index.js │ │ ├── a.css │ │ ├── b.css │ │ ├── a.js │ │ ├── expected │ │ │ └── file.css │ │ └── webpack.config.js │ ├── simple-query-object │ │ ├── a.css │ │ ├── b.css │ │ ├── index.js │ │ ├── expected │ │ │ └── file.css │ │ └── webpack.config.js │ ├── simple-queryless-object │ │ ├── a.css │ │ ├── b.css │ │ ├── index.js │ │ ├── expected │ │ │ └── file.css │ │ └── webpack.config.js │ ├── chunk-modules-ordered-by-id │ │ ├── a.css │ │ ├── b.css │ │ ├── index.js │ │ ├── expected │ │ │ └── file.css │ │ └── webpack.config.js │ ├── string-export-with-optimize-module-order │ │ ├── expected │ │ │ └── main.txt │ │ ├── module.js │ │ ├── a.js │ │ ├── b.js │ │ ├── loader.js │ │ └── webpack.config.js │ └── chunk-modules-nested-ordered-by-id │ │ ├── a.txt │ │ ├── b.txt │ │ ├── a.js │ │ ├── index.js │ │ ├── c.txt │ │ ├── expected │ │ └── file.css │ │ └── webpack.config.js ├── schema-validation.test.js ├── extract-text-plugin.test.js ├── define-fallback.test.js ├── webpack-integration.test.js ├── __snapshots__ │ └── webpack-integration.test.js.snap └── define-loader.test.js ├── example ├── dep2.js ├── style6.css ├── dep.js ├── style5.1.css ├── style5.2.css ├── entry.js ├── style.css ├── common.css ├── image.png ├── style2.css ├── style5.css ├── style3.css ├── style4.css ├── entry2.js ├── base.css ├── index.html └── webpack.config.js ├── src ├── cjs.js ├── lib │ ├── OrderUndefinedError.js │ ├── ExtractTextPluginCompilation.js │ ├── helpers.js │ └── ExtractedModule.js ├── loader.js └── index.js ├── .eslintignore ├── .gitattributes ├── .eslintrc ├── .gitignore ├── .editorconfig ├── .github ├── PULL_REQUEST_TEMPLATE.md └── ISSUE_TEMPLATE.md ├── schema ├── loader.json └── plugin.json ├── .babelrc ├── appveyor.yml ├── LICENSE ├── .travis.yml ├── package.json ├── logo.svg ├── CHANGELOG.md └── README.md /test/cases/nested/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/nested/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/nested/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/cases/nested/d.txt: -------------------------------------------------------------------------------- 1 | d 2 | -------------------------------------------------------------------------------- /test/cases/simple/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/simple/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/common-async/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/common-async/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/common-async/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/cases/merging-chunk/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/merging-chunk/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/merging-chunk/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/cases/optimize-tree/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/optimize-tree/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/splitted-chunk/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/splitted-chunk/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/hashed-module-ids/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/hashed-module-ids/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/multiple-entries/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/multiple-entries/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/multiple-entries/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /example/dep2.js: -------------------------------------------------------------------------------- 1 | require("./style3.css"); 2 | -------------------------------------------------------------------------------- /test/cases/hashed-module-ids/index.txt: -------------------------------------------------------------------------------- 1 | index 2 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/cases/optimize-tree/index.txt: -------------------------------------------------------------------------------- 1 | index 2 | -------------------------------------------------------------------------------- /test/cases/simple/expected/file.css: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/a.txt: -------------------------------------------------------------------------------- 1 | a -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/b.txt: -------------------------------------------------------------------------------- 1 | b -------------------------------------------------------------------------------- /test/cases/splitted-multiple-entries/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/splitted-multiple-entries/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/cases/splitted-multiple-entries/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/cases/splitted-chunk/expected/file.css: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/cases/splitted-multiple-entries/expected/a.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/cases/splitted-multiple-entries/expected/b.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries-shared/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/multiple-entries/expected/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | -------------------------------------------------------------------------------- /test/cases/multiple-entries/expected/b.txt: -------------------------------------------------------------------------------- 1 | a 2 | c 3 | -------------------------------------------------------------------------------- /test/cases/order-undefined-error/c1.css: -------------------------------------------------------------------------------- 1 | .c1 { 2 | } 3 | -------------------------------------------------------------------------------- /test/cases/order-undefined-error/c2.css: -------------------------------------------------------------------------------- 1 | .c2 { 2 | } 3 | -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/expected/a.txt: -------------------------------------------------------------------------------- 1 | a -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/expected/b.txt: -------------------------------------------------------------------------------- 1 | b -------------------------------------------------------------------------------- /example/style6.css: -------------------------------------------------------------------------------- 1 | body { 2 | order: 1; 3 | correct: b; 4 | } -------------------------------------------------------------------------------- /src/cjs.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./index').default; 2 | -------------------------------------------------------------------------------- /test/cases/common-async/expected/file.css: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | c 4 | -------------------------------------------------------------------------------- /test/cases/merging-chunk/expected/file.css: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | c 4 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries-shared/expected/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries-shared/expected/b.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries/expected/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries/expected/b.txt: -------------------------------------------------------------------------------- 1 | a 2 | c 3 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/expected/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/expected/b.txt: -------------------------------------------------------------------------------- 1 | a 2 | c 3 | -------------------------------------------------------------------------------- /test/cases/nested/expected/file.css: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | c 4 | d 5 | -------------------------------------------------------------------------------- /example/dep.js: -------------------------------------------------------------------------------- 1 | require("./style2.css"); 2 | require(["./dep2"]); 3 | -------------------------------------------------------------------------------- /example/style5.1.css: -------------------------------------------------------------------------------- 1 | body { 2 | order: 2; 3 | correct: b; 4 | } -------------------------------------------------------------------------------- /example/style5.2.css: -------------------------------------------------------------------------------- 1 | body { 2 | order: 3; 3 | correct: b; 4 | } -------------------------------------------------------------------------------- /test/cases/hashed-module-ids/expected/main.txt: -------------------------------------------------------------------------------- 1 | index 2 | a 3 | b 4 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/expected/txt/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/expected/txt/b.txt: -------------------------------------------------------------------------------- 1 | a 2 | c 3 | -------------------------------------------------------------------------------- /test/cases/optimize-tree/expected/main.txt: -------------------------------------------------------------------------------- 1 | index 2 | a 3 | b 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /dist 3 | /test/cases 4 | /test/js 5 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-css-wrong-order/b.js: -------------------------------------------------------------------------------- 1 | require('./b.css'); 2 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-css-wrong-order/index.js: -------------------------------------------------------------------------------- 1 | require('./a.js'); 2 | -------------------------------------------------------------------------------- /test/cases/simple-query-object/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | correct: a; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-query-object/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | correct: b; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/c.js: -------------------------------------------------------------------------------- 1 | require('./c.txt'); 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | package-lock.json -diff 4 | bin/* eol=lf -------------------------------------------------------------------------------- /test/cases/common-async/a.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require('./b.txt'); 3 | -------------------------------------------------------------------------------- /test/cases/simple-queryless-object/a.css: -------------------------------------------------------------------------------- 1 | body { 2 | correct: a; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-queryless-object/b.css: -------------------------------------------------------------------------------- 1 | body { 2 | correct: b; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple/index.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require('./b.txt'); 3 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-css-wrong-order/a.css: -------------------------------------------------------------------------------- 1 | .App { 2 | color: black; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-ordered-by-id/a.css: -------------------------------------------------------------------------------- 1 | .block { 2 | color: tomato; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-ordered-by-id/b.css: -------------------------------------------------------------------------------- 1 | .App { 2 | color: black; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/common-async/b.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require('./c.txt'); 3 | 4 | -------------------------------------------------------------------------------- /test/cases/multiple-entries/a.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require('./b.txt'); 3 | -------------------------------------------------------------------------------- /test/cases/multiple-entries/b.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require('./c.txt'); 3 | -------------------------------------------------------------------------------- /test/cases/string-export-with-optimize-module-order/expected/main.txt: -------------------------------------------------------------------------------- 1 | a 2 | b 3 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-css-wrong-order/b.css: -------------------------------------------------------------------------------- 1 | .block { 2 | color: tomato; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-nested-ordered-by-id/a.txt: -------------------------------------------------------------------------------- 1 | .App { 2 | color: black; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/simple-query-object/index.js: -------------------------------------------------------------------------------- 1 | require('./a.css'); 2 | require('./b.css'); 3 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-css-wrong-order/a.js: -------------------------------------------------------------------------------- 1 | require('./b.js'); 2 | require('./a.css'); 3 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-nested-ordered-by-id/b.txt: -------------------------------------------------------------------------------- 1 | .block { 2 | color: tomato; 3 | } 4 | -------------------------------------------------------------------------------- /test/cases/order-undefined-error/index.js: -------------------------------------------------------------------------------- 1 | require('./a.css'); 2 | require('./b.css'); 3 | -------------------------------------------------------------------------------- /test/cases/simple-queryless-object/index.js: -------------------------------------------------------------------------------- 1 | require('./a.css'); 2 | require('./b.css'); 3 | -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/a.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require('./c.js'); 3 | -------------------------------------------------------------------------------- /test/cases/string-export-with-optimize-module-order/module.js: -------------------------------------------------------------------------------- 1 | module.exports = () => {}; 2 | -------------------------------------------------------------------------------- /example/entry.js: -------------------------------------------------------------------------------- 1 | require("./common.css"); 2 | require("./style.css"); 3 | require("./dep"); 4 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-nested-ordered-by-id/a.js: -------------------------------------------------------------------------------- 1 | require('./b.txt'); 2 | require('./a.txt'); 3 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-ordered-by-id/index.js: -------------------------------------------------------------------------------- 1 | require('./a.css'); 2 | require('./b.css'); 3 | -------------------------------------------------------------------------------- /test/cases/nested/index.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require('./file'); 3 | require('./b.txt'); 4 | -------------------------------------------------------------------------------- /example/style.css: -------------------------------------------------------------------------------- 1 | @import "base.css"; 2 | body { 3 | background: url(image.png); 4 | correct: a; 5 | } -------------------------------------------------------------------------------- /test/cases/chunk-modules-nested-ordered-by-id/index.js: -------------------------------------------------------------------------------- 1 | require('./c.txt'); 2 | require('./a.js'); 3 | -------------------------------------------------------------------------------- /example/common.css: -------------------------------------------------------------------------------- 1 | @import url(http://this.should.be/in/c); 2 | body { 3 | height: 100%; 4 | correct: c; 5 | } -------------------------------------------------------------------------------- /example/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack-contrib/extract-text-webpack-plugin/HEAD/example/image.png -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries-shared/a.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries-shared/b.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | }); 4 | -------------------------------------------------------------------------------- /test/cases/optimize-tree/a.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | }); 4 | 5 | a = {}; 6 | -------------------------------------------------------------------------------- /test/cases/optimize-tree/b.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./b.txt'); 3 | }); 4 | 5 | b = {}; 6 | -------------------------------------------------------------------------------- /test/cases/string-export-with-optimize-module-order/a.js: -------------------------------------------------------------------------------- 1 | require("./module")(); 2 | module.exports = "a\n"; 3 | -------------------------------------------------------------------------------- /test/cases/string-export-with-optimize-module-order/b.js: -------------------------------------------------------------------------------- 1 | require("./module")(); 2 | module.exports = "b\n"; 3 | -------------------------------------------------------------------------------- /example/style2.css: -------------------------------------------------------------------------------- 1 | @import "base.css"; 2 | @import "style3.css"; 3 | body { 4 | color: red; 5 | correct: a; 6 | } -------------------------------------------------------------------------------- /example/style5.css: -------------------------------------------------------------------------------- 1 | @import "style5.1.css"; 2 | @import "style5.2.css"; 3 | body { 4 | order: 4; 5 | correct: b; 6 | } -------------------------------------------------------------------------------- /test/cases/hashed-module-ids/a.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | }); 4 | 5 | a = {}; 6 | -------------------------------------------------------------------------------- /test/cases/hashed-module-ids/b.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./b.txt'); 3 | }); 4 | 5 | b = {}; 6 | -------------------------------------------------------------------------------- /example/style3.css: -------------------------------------------------------------------------------- 1 | @import url(http://this.should.be/in/a); 2 | .xyz { 3 | color: url(image.png); 4 | correct: a; 5 | } -------------------------------------------------------------------------------- /example/style4.css: -------------------------------------------------------------------------------- 1 | @import "base.css"; 2 | .secondary { 3 | order: 0; 4 | color: url(image.png); 5 | correct: b; 6 | } -------------------------------------------------------------------------------- /test/cases/nested/file.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require('./b.txt'); 3 | require('./c.txt'); 4 | require('./d.txt'); 5 | -------------------------------------------------------------------------------- /test/cases/splitted-chunk/index.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require.ensure([], () => { 3 | require('./b.txt'); 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/order-undefined-error/a.css: -------------------------------------------------------------------------------- 1 | .a { 2 | composes: c1 from './c1.css'; 3 | composes: c2 from './c2.css'; 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/order-undefined-error/b.css: -------------------------------------------------------------------------------- 1 | .b { 2 | composes: c2 from './c2.css'; 3 | composes: c1 from './c1.css'; 4 | } 5 | -------------------------------------------------------------------------------- /test/cases/string-export-with-optimize-module-order/loader.js: -------------------------------------------------------------------------------- 1 | module.exports = function(source) { 2 | return source; 3 | } 4 | -------------------------------------------------------------------------------- /example/entry2.js: -------------------------------------------------------------------------------- 1 | require("./common.css"); 2 | require("./style4.css"); 3 | require("./style6.css"); 4 | require("./style5.css"); 5 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-ordered-by-id/expected/file.css: -------------------------------------------------------------------------------- 1 | .block { 2 | color: tomato; 3 | } 4 | .App { 5 | color: black; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries/a.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | require('./b.txt'); 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries/b.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | require('./c.txt'); 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/a.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | require('./b.txt'); 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/b.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | require('./c.txt'); 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/splitted-multiple-entries/a.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | require('./b.txt'); 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/splitted-multiple-entries/b.js: -------------------------------------------------------------------------------- 1 | require.ensure([], () => { 2 | require('./a.txt'); 3 | require('./c.txt'); 4 | }); 5 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-css-wrong-order/expected/file.css: -------------------------------------------------------------------------------- 1 | .block { 2 | color: tomato; 3 | } 4 | .App { 5 | color: black; 6 | } 7 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-nested-ordered-by-id/c.txt: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/b.js: -------------------------------------------------------------------------------- 1 | 2 | require('./b.txt'); 3 | require.ensure([], (require) => { 4 | require('./c.js'); 5 | }); -------------------------------------------------------------------------------- /test/cases/merging-chunk/index.js: -------------------------------------------------------------------------------- 1 | require('./a.txt'); 2 | require.ensure([], () => { 3 | require('./b.txt'); 4 | require('./c.txt'); 5 | }); 6 | -------------------------------------------------------------------------------- /test/cases/simple-query-object/expected/file.css: -------------------------------------------------------------------------------- 1 | body { 2 | correct: a; 3 | } 4 | body { 5 | correct: b; 6 | } 7 | 8 | /*# sourceMappingURL=file.css.map*/ -------------------------------------------------------------------------------- /test/cases/simple-queryless-object/expected/file.css: -------------------------------------------------------------------------------- 1 | body { 2 | correct: a; 3 | } 4 | body { 5 | correct: b; 6 | } 7 | 8 | /*# sourceMappingURL=file.css.map*/ -------------------------------------------------------------------------------- /example/base.css: -------------------------------------------------------------------------------- 1 | @import url(http://this.should.be/in/c); 2 | @import url("http://this.should.also.be/in/c"); 3 | body { 4 | border: 1px solid black; 5 | correct: c; 6 | } -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/expected/txt/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webpack-contrib/extract-text-webpack-plugin/HEAD/test/cases/multiple-entries-filename/expected/txt/.DS_Store -------------------------------------------------------------------------------- /test/cases/order-undefined-error/expected/file.css: -------------------------------------------------------------------------------- 1 | ._3QiTkN7JPds2C9Bq73MpOX { 2 | } 3 | ._3dtwYD12yqGZUJ-uPQLu9z { 4 | } 5 | ._32HVXkGocfWZMaHoXEFkED { 6 | } 7 | .hbZI7DlQBB9VYNCgL20kL { 8 | } 9 | -------------------------------------------------------------------------------- /test/cases/nested/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | plugins: [ 6 | new ExtractTextPlugin('file.css'), 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /test/cases/simple/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | plugins: [ 6 | new ExtractTextPlugin('file.css'), 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-nested-ordered-by-id/expected/file.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: sans-serif; 5 | } 6 | .block { 7 | color: tomato; 8 | } 9 | .App { 10 | color: black; 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/optimize-tree/index.js: -------------------------------------------------------------------------------- 1 | require('./index.txt'); 2 | 3 | require.ensure([], () => { 4 | require('./a.js'); 5 | }, 'a-chunk'); 6 | 7 | require.ensure([], () => { 8 | require('./b.js'); 9 | }, 'b-chunk'); 10 | -------------------------------------------------------------------------------- /test/cases/hashed-module-ids/index.js: -------------------------------------------------------------------------------- 1 | require('./index.txt'); 2 | 3 | require.ensure([], () => { 4 | require('./a.js'); 5 | }, 'a-chunk'); 6 | 7 | require.ensure([], () => { 8 | require('./b.js'); 9 | }, 'b-chunk'); 10 | -------------------------------------------------------------------------------- /test/cases/common-async/index.js: -------------------------------------------------------------------------------- 1 | require.ensure( 2 | [], 3 | () => { 4 | require('./a.js'); 5 | }, 6 | 'async-chunk-a', 7 | ); 8 | require.ensure( 9 | [], 10 | () => { 11 | require('./b.js'); 12 | }, 13 | 'async-chunk-b', 14 | ); 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "webpack", 3 | "rules": { 4 | "prefer-destructuring": 1, 5 | "prefer-rest-params": 0, 6 | "class-methods-use-this": 1, 7 | "no-plusplus": 1, 8 | "consistent-return": 0, 9 | "no-param-reassign": 0 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/cases/multiple-entries/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: { 5 | a: './a', 6 | b: './b', 7 | }, 8 | plugins: [ 9 | new ExtractTextPlugin('[name].txt'), 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-nested-ordered-by-id/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | plugins: [ 6 | new ExtractTextPlugin({ 7 | filename: 'file.css', 8 | }), 9 | ], 10 | }; 11 | -------------------------------------------------------------------------------- /test/cases/merging-chunk/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | plugins: [ 6 | new ExtractTextPlugin({ 7 | filename: 'file.css', 8 | allChunks: true, 9 | }), 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /test/cases/splitted-chunk/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | plugins: [ 6 | new ExtractTextPlugin({ 7 | filename: 'file.css', 8 | allChunks: false, 9 | }), 10 | ], 11 | }; 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | 3 | /example/assets 4 | 5 | /test/js 6 | /coverage 7 | 8 | /.idea 9 | 10 | .DS_Store 11 | 12 | logs 13 | *.log 14 | npm-debug.log* 15 | .eslintcache 16 | /dist 17 | /local 18 | /reports 19 | Thumbs.db 20 | .idea 21 | .vscode 22 | *.sublime-project 23 | *.sublime-workspace -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: { 5 | a: './a', 6 | b: './b', 7 | }, 8 | plugins: [ 9 | new ExtractTextPlugin({ 10 | filename: '[name].txt', 11 | allChunks: true, 12 | }), 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /test/cases/splitted-multiple-entries/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: { 5 | a: './a', 6 | b: './b', 7 | }, 8 | plugins: [ 9 | new ExtractTextPlugin({ 10 | filename: '[name].txt', 11 | allChunks: false, 12 | }), 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /test/cases/merging-multiple-entries-shared/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: { 5 | a: './a', 6 | b: './b', 7 | }, 8 | plugins: [ 9 | new ExtractTextPlugin({ 10 | filename: '[name].txt', 11 | allChunks: true, 12 | }), 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /test/cases/splitted-mix-multiple-entries/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: { 5 | a: './a', 6 | b: './b', 7 | }, 8 | plugins: [ 9 | new ExtractTextPlugin({ 10 | filename: '[name].txt', 11 | allChunks: false, 12 | }), 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [{package.json,.travis.yml}] 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [.md] 17 | insert_final_newline = false 18 | trim_trailing_whitespace = false 19 | -------------------------------------------------------------------------------- /test/cases/multiple-entries-filename/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: { 5 | 'js/a': './a', 6 | 'js/b': './b', 7 | }, 8 | plugins: [ 9 | new ExtractTextPlugin({ 10 | filename: getPath => getPath('txt/[name].txt').replace('txt/js', ''), 11 | allChunks: true, 12 | }), 13 | ], 14 | }; 15 | -------------------------------------------------------------------------------- /src/lib/OrderUndefinedError.js: -------------------------------------------------------------------------------- 1 | function OrderUndefinedError(module) { 2 | Error.call(this); 3 | Error.captureStackTrace(this, OrderUndefinedError); 4 | this.name = 'OrderUndefinedError'; 5 | this.message = 'Order in extracted chunk undefined'; 6 | this.module = module; 7 | } 8 | export default OrderUndefinedError; 9 | 10 | OrderUndefinedError.prototype = Object.create(Error.prototype); 11 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /test/cases/hashed-module-ids/webpack.config.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import ExtractTextPlugin from '../../../src/index'; 3 | 4 | module.exports = { 5 | entry: './index.js', 6 | plugins: [ 7 | new ExtractTextPlugin({ 8 | filename: '[name].txt', 9 | allChunks: true, 10 | }), 11 | new webpack.HashedModuleIdsPlugin(), 12 | new webpack.optimize.CommonsChunkPlugin({ 13 | children: true, 14 | minChunks: 2, 15 | }), 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /test/cases/common-async/webpack.config.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import ExtractTextPlugin from '../../../src/index'; 3 | 4 | module.exports = { 5 | entry: './index', 6 | plugins: [ 7 | new webpack.optimize.CommonsChunkPlugin({ 8 | name: 'common', 9 | filename: 'common.js', 10 | chunks: ['async-chunk-a', 'async-chunk-b'], 11 | }), 12 | new ExtractTextPlugin({ 13 | filename: 'file.css', 14 | allChunks: true, 15 | }), 16 | ], 17 | }; 18 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-css-wrong-order/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | module: { 6 | loaders: [ 7 | { 8 | test: /\.css$/, 9 | use: ExtractTextPlugin.extract({ 10 | fallback: { loader: 'style-loader' }, 11 | use: { loader: 'css-loader', }, 12 | }) 13 | }, 14 | ], 15 | }, 16 | plugins: [ 17 | new ExtractTextPlugin('file.css'), 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /test/cases/chunk-modules-ordered-by-id/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | module: { 6 | loaders: [ 7 | { 8 | test: /\.css$/, 9 | use: ExtractTextPlugin.extract({ 10 | fallback: { loader: 'style-loader' }, 11 | use: { loader: 'css-loader', }, 12 | }) 13 | }, 14 | ], 15 | }, 16 | plugins: [ 17 | new ExtractTextPlugin('file.css'), 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /test/cases/optimize-tree/webpack.config.js: -------------------------------------------------------------------------------- 1 | import webpack from 'webpack'; 2 | import ExtractTextPlugin from '../../../src/index'; 3 | 4 | module.exports = { 5 | entry: './index.js', 6 | plugins: [ 7 | new ExtractTextPlugin({ 8 | filename: '[name].txt', 9 | allChunks: true, 10 | }), 11 | new webpack.optimize.CommonsChunkPlugin({ 12 | children: true, 13 | minChunks: 2, 14 | }), 15 | new webpack.optimize.MinChunkSizePlugin({ 16 | minChunkSize: 51200, // 50ko 17 | }), 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /test/cases/simple-query-object/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | module: { 6 | loaders: [ 7 | { test: /\.css$/, 8 | use: ExtractTextPlugin.extract({ 9 | fallback: 'style-loader', 10 | use: { loader: 'css-loader', 11 | options: { 12 | sourceMap: true, 13 | } }, 14 | }) }, 15 | ], 16 | }, 17 | devtool: 'source-map', 18 | plugins: [ 19 | new ExtractTextPlugin('file.css'), 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /schema/loader.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": false, 4 | "properties": { 5 | "allChunks": { 6 | "type": "boolean" 7 | }, 8 | "disable": { 9 | "type": "boolean" 10 | }, 11 | "omit": { 12 | "type": "boolean" 13 | }, 14 | "remove": { 15 | "type": "boolean" 16 | }, 17 | "fallback": { 18 | "type": ["string", "array", "object"] 19 | }, 20 | "filename": { 21 | "type": "string" 22 | }, 23 | "use": { 24 | "type": ["string", "array", "object"] 25 | }, 26 | "publicPath": { 27 | "type": "string" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test/cases/simple-queryless-object/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index', 5 | module: { 6 | loaders: [ 7 | { test: /\.css$/, 8 | use: ExtractTextPlugin.extract({ 9 | fallback: { loader: 'style-loader' }, 10 | use: { loader: 'css-loader', 11 | options: { 12 | sourceMap: true, 13 | } }, 14 | }) }, 15 | ], 16 | }, 17 | devtool: 'source-map', 18 | plugins: [ 19 | new ExtractTextPlugin('file.css'), 20 | ], 21 | }; 22 | -------------------------------------------------------------------------------- /test/cases/order-undefined-error/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | module: { 6 | loaders: [ 7 | { 8 | test: /\.css$/, 9 | use: ExtractTextPlugin.extract({ 10 | fallback: 'style-loader', 11 | use: { 12 | loader: 'css-loader', 13 | options: { 14 | modules: true, 15 | }, 16 | }, 17 | }), 18 | }, 19 | ], 20 | }, 21 | plugins: [ 22 | new ExtractTextPlugin({ 23 | filename: 'file.css', 24 | ignoreOrder: true, 25 | }), 26 | ], 27 | }; 28 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "useBuiltIns": true, 7 | "targets": { 8 | "node": "4.8" 9 | }, 10 | "exclude": [ 11 | "transform-async-to-generator", 12 | "transform-regenerator" 13 | ] 14 | } 15 | ] 16 | ], 17 | "plugins": [ 18 | [ 19 | "transform-object-rest-spread", 20 | { 21 | "useBuiltIns": true 22 | } 23 | ] 24 | ], 25 | "env": { 26 | "test": { 27 | "presets": [ 28 | "env" 29 | ], 30 | "sourceMap": "inline", 31 | "plugins": [ 32 | "transform-object-rest-spread" 33 | ] 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/schema-validation.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions */ 2 | import ExtractTextPlugin from '../src'; 3 | 4 | 5 | describe('json schema validation', () => { 6 | it('does not throw if a filename is specified', () => { 7 | expect(() => { 8 | ExtractTextPlugin.extract('file.css'); 9 | }).doesNotThrow; 10 | }); 11 | 12 | it('does not throw if a correct config object is passed in', () => { 13 | expect(() => { 14 | ExtractTextPlugin.extract({ use: 'css-loader' }); 15 | }).doesNotThrow; 16 | }); 17 | 18 | it('throws if an incorrect config is passed in', () => { 19 | expect(() => { 20 | ExtractTextPlugin.extract({ style: 'file.css' }); 21 | }).toThrow(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | init: 5 | - git config --global core.autocrlf input 6 | environment: 7 | matrix: 8 | - nodejs_version: '8' 9 | webpack_version: latest 10 | job_part: test 11 | - nodejs_version: '6' 12 | webpack_version: latest 13 | job_part: test 14 | - nodejs_version: '4.8' 15 | webpack_version: latest 16 | job_part: test 17 | build: 'off' 18 | matrix: 19 | fast_finish: true 20 | install: 21 | - ps: Install-Product node $env:nodejs_version x64 22 | - npm i -g npm@latest 23 | - npm install 24 | before_test: 25 | - cmd: npm install webpack@%webpack_version% 26 | test_script: 27 | - node --version 28 | - npm --version 29 | - cmd: npm run appveyor:%job_part% 30 | -------------------------------------------------------------------------------- /test/extract-text-plugin.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-unused-expressions */ 2 | import ExtractTextPlugin from '../src'; 3 | 4 | const loader = require.resolve('../src/loader'); 5 | 6 | describe('ExtractTextPlugin', () => { 7 | it('throws if given multiple arguments', () => { 8 | expect(() => { 9 | ExtractTextPlugin.extract('style-loader', 'css-loader'); 10 | }).throws; 11 | }); 12 | 13 | it('passes additional options to its own loader', () => { 14 | expect(ExtractTextPlugin.extract({ 15 | fallback: 'style-loader', 16 | use: 'css-loader', 17 | publicPath: '/test', 18 | })).toEqual([ 19 | { loader, options: { omit: 1, remove: true, publicPath: '/test' } }, 20 | { loader: 'style-loader' }, 21 | { loader: 'css-loader' }, 22 | ]); 23 | }); 24 | }); 25 | 26 | -------------------------------------------------------------------------------- /test/cases/string-export-with-optimize-module-order/webpack.config.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../../../src/index'; 2 | 3 | function moduleOrderOptimizer() { 4 | this.plugin("after-plugins", compiler => 5 | compiler.plugin("compilation", compilation => 6 | compilation.plugin("optimize-module-order", modules => { 7 | const index = modules.findIndex(module => module.rawRequest == "./module"); 8 | [modules[0], modules[index]] = [modules[index], modules[0]]; 9 | }))); 10 | } 11 | 12 | module.exports = { 13 | entry: ['./a', './b'], 14 | module: { 15 | loaders: [ 16 | { 17 | test: /(a|b)\.js$/, 18 | use: ExtractTextPlugin.extract('./loader') 19 | }, 20 | ], 21 | }, 22 | plugins: [ 23 | moduleOrderOptimizer, 24 | new ExtractTextPlugin('[name].txt'), 25 | ], 26 | }; 27 | -------------------------------------------------------------------------------- /example/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var ExtractTextPlugin = require("../"); 3 | module.exports = { 4 | entry: { 5 | a: "./entry.js", 6 | b: "./entry2.js" 7 | }, 8 | output: { 9 | filename: "[name].js?[hash]-[chunkhash]", 10 | chunkFilename: "[name].js?[hash]-[chunkhash]", 11 | path: __dirname + "/assets", 12 | publicPath: "/assets/" 13 | }, 14 | module: { 15 | loaders: [ 16 | { test: /\.css$/, use: ExtractTextPlugin.extract({ 17 | fallback: "style-loader", 18 | use: { 19 | loader: "css-loader", 20 | options: { 21 | sourceMap: true 22 | } 23 | }, 24 | publicPath: "../" 25 | }) }, 26 | { test: /\.png$/, loader: "file-loader" } 27 | ] 28 | }, 29 | devtool: "source-map", 30 | plugins: [ 31 | new ExtractTextPlugin({ 32 | filename: "css/[name].css?[hash]-[chunkhash]-[contenthash]-[name]", 33 | disable: false, 34 | allChunks: true 35 | }), 36 | new webpack.optimize.CommonsChunkPlugin({ name: "c", filename: "c.js" }) 37 | ] 38 | }; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright JS Foundation and other contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | 'Software'), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /schema/plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "additionalProperties": false, 4 | "properties": { 5 | "allChunks": { 6 | "description": "", 7 | "type": "boolean" 8 | }, 9 | "disable": { 10 | "description": "", 11 | "type": "boolean" 12 | }, 13 | "fallback": { 14 | "description": "A loader that webpack can fall back to if the original one fails.", 15 | "modes": { 16 | "type": ["string", "object", "array"] 17 | } 18 | }, 19 | "filename": { 20 | "description": "The filename and path that ExtractTextPlugin will extract to", 21 | "modes": { 22 | "type": ["string", "function"] 23 | } 24 | }, 25 | "ignoreOrder": { 26 | "description": "Ignore dependency order (useful for CSS Modules)", 27 | "type": "boolean" 28 | }, 29 | "loader": { 30 | "description": "The loader that ExtractTextPlugin will attempt to load through.", 31 | "modes": { 32 | "type": ["string", "object", "array"] 33 | } 34 | }, 35 | "publicPath": { 36 | "description": "", 37 | "type": "string" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: trusty 3 | language: node_js 4 | branches: 5 | only: 6 | - master 7 | jobs: 8 | fast_finish: true 9 | allow_failures: 10 | - env: WEBPACK_VERSION=canary 11 | include: 12 | - &test-latest 13 | stage: Webpack latest 14 | node_js: 6 15 | env: WEBPACK_VERSION=latest JOB_PART=test 16 | script: npm run travis:$JOB_PART 17 | - <<: *test-latest 18 | node_js: 4.8 19 | env: WEBPACK_VERSION=latest JOB_PART=test 20 | script: npm run travis:$JOB_PART 21 | - <<: *test-latest 22 | node_js: 8 23 | env: WEBPACK_VERSION=latest JOB_PART=lint 24 | script: npm run travis:$JOB_PART 25 | - <<: *test-latest 26 | node_js: 8 27 | env: WEBPACK_VERSION=latest JOB_PART=coverage 28 | script: npm run travis:$JOB_PART 29 | after_success: 'bash <(curl -s https://codecov.io/bash)' 30 | - stage: Webpack canary 31 | before_script: npm i --no-save git://github.com/webpack/webpack.git#master 32 | script: npm run travis:$JOB_PART 33 | node_js: 8 34 | env: WEBPACK_VERSION=canary JOB_PART=test 35 | before_install: 36 | - 'if [[ `npm -v` != 5* ]]; then npm i -g npm@^5.0.0; fi' 37 | - nvm --version 38 | - node --version 39 | - npm --version 40 | before_script: 41 | - |- 42 | if [ "$WEBPACK_VERSION" ]; then 43 | npm i --no-save webpack@$WEBPACK_VERSION 44 | fi 45 | script: 46 | - 'npm run travis:$JOB_PART' 47 | after_success: 48 | - 'bash <(curl -s https://codecov.io/bash)' 49 | -------------------------------------------------------------------------------- /src/lib/ExtractTextPluginCompilation.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-multi-assign */ 2 | import ExtractedModule from './ExtractedModule'; 3 | 4 | class ExtractTextPluginCompilation { 5 | constructor() { 6 | this.modulesByIdentifier = {}; 7 | } 8 | 9 | addModule( 10 | identifier, 11 | originalModule, 12 | source, 13 | additionalInformation, 14 | sourceMap, 15 | prevModules) { 16 | let m; 17 | if (!this.modulesByIdentifier[identifier]) { 18 | m = this.modulesByIdentifier[identifier] = 19 | new ExtractedModule(identifier, originalModule, source, sourceMap, additionalInformation, prevModules); 20 | } else { 21 | m = this.modulesByIdentifier[identifier]; 22 | m.addPrevModules(prevModules); 23 | if (originalModule.index2 < m.getOriginalModule().index2) { 24 | m.setOriginalModule(originalModule); 25 | } 26 | } 27 | return m; 28 | } 29 | 30 | addResultToChunk(identifier, result, originalModule, extractedChunk) { 31 | if (!Array.isArray(result)) { 32 | result = [[identifier, result]]; 33 | } 34 | const counterMap = {}; 35 | const prevModules = []; 36 | result.forEach((item) => { 37 | const c = counterMap[item[0]]; 38 | const module = this.addModule.call(this, item[0] + (c || ''), originalModule, item[1], item[2], item[3], prevModules.slice()); 39 | extractedChunk.addModule(module); 40 | module.addChunk(extractedChunk); 41 | counterMap[item[0]] = (c || 0) + 1; 42 | prevModules.push(module); 43 | }, this); 44 | } 45 | } 46 | 47 | export default ExtractTextPluginCompilation; 48 | -------------------------------------------------------------------------------- /src/lib/helpers.js: -------------------------------------------------------------------------------- 1 | export function isInitialOrHasNoParents(chunk) { 2 | return chunk.isInitial() || chunk.parents.length === 0; 3 | } 4 | 5 | export function isInvalidOrder(a, b) { 6 | const bBeforeA = a.getPrevModules().indexOf(b) >= 0; 7 | const aBeforeB = b.getPrevModules().indexOf(a) >= 0; 8 | return aBeforeB && bBeforeA; 9 | } 10 | 11 | export function getOrder(a, b) { 12 | const aOrder = a.getOrder(); 13 | const bOrder = b.getOrder(); 14 | if (aOrder < bOrder) return -1; 15 | if (aOrder > bOrder) return 1; 16 | const aIndex = a.getOriginalModule().index2; 17 | const bIndex = b.getOriginalModule().index2; 18 | if (aIndex < bIndex) return -1; 19 | if (aIndex > bIndex) return 1; 20 | const bBeforeA = a.getPrevModules().indexOf(b) >= 0; 21 | const aBeforeB = b.getPrevModules().indexOf(a) >= 0; 22 | if (aBeforeB && !bBeforeA) return -1; 23 | if (!aBeforeB && bBeforeA) return 1; 24 | const ai = a.identifier(); 25 | const bi = b.identifier(); 26 | if (ai < bi) return -1; 27 | if (ai > bi) return 1; 28 | return 0; 29 | } 30 | 31 | export function getLoaderObject(loader) { 32 | if (isString(loader)) { 33 | return { loader }; 34 | } 35 | return loader; 36 | } 37 | 38 | export function mergeOptions(a, b) { 39 | if (!b) return a; 40 | Object.keys(b).forEach((key) => { 41 | a[key] = b[key]; 42 | }); 43 | return a; 44 | } 45 | 46 | export function isString(a) { 47 | return typeof a === 'string'; 48 | } 49 | 50 | export function isFunction(a) { 51 | return typeof a === 'function'; 52 | } 53 | 54 | export function isType(type, obj) { 55 | return Object.prototype.toString.call(obj) === `[object ${type}]`; 56 | } 57 | -------------------------------------------------------------------------------- /src/lib/ExtractedModule.js: -------------------------------------------------------------------------------- 1 | import { SourceMapSource, RawSource } from 'webpack-sources'; 2 | 3 | class ExtractedModule { 4 | constructor( 5 | identifier, 6 | originalModule, 7 | source, 8 | sourceMap, 9 | addtitionalInformation, 10 | prevModules) { 11 | this._identifier = identifier; 12 | this._originalModule = originalModule; 13 | this._source = source; 14 | this._sourceMap = sourceMap; 15 | this._prevModules = prevModules; 16 | this.addtitionalInformation = addtitionalInformation; 17 | this.chunks = []; 18 | } 19 | 20 | getOrder() { 21 | // http://stackoverflow.com/a/14676665/1458162 22 | return /^@import url/.test(this._source) ? 0 : 1; 23 | } 24 | 25 | addChunk(chunk) { 26 | const idx = this.chunks.indexOf(chunk); 27 | if (idx < 0) { this.chunks.push(chunk); } 28 | } 29 | 30 | removeChunk(chunk) { 31 | const idx = this.chunks.indexOf(chunk); 32 | if (idx >= 0) { 33 | this.chunks.splice(idx, 1); 34 | chunk.removeModule(this); 35 | return true; 36 | } 37 | return false; 38 | } 39 | 40 | rewriteChunkInReasons(oldChunk, newChunks) { } // eslint-disable-line 41 | 42 | identifier() { 43 | return this._identifier; 44 | } 45 | 46 | source() { 47 | if (this._sourceMap) { return new SourceMapSource(this._source, null, this._sourceMap); } 48 | return new RawSource(this._source); 49 | } 50 | 51 | getOriginalModule() { 52 | return this._originalModule; 53 | } 54 | 55 | getPrevModules() { 56 | return this._prevModules; 57 | } 58 | 59 | addPrevModules(prevModules) { 60 | prevModules.forEach((m) => { 61 | if (this._prevModules.indexOf(m) < 0) { this._prevModules.push(m); } 62 | }, this); 63 | } 64 | 65 | setOriginalModule(originalModule) { 66 | this._originalModule = originalModule; 67 | } 68 | } 69 | 70 | export default ExtractedModule; 71 | -------------------------------------------------------------------------------- /test/define-fallback.test.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../src'; 2 | 3 | const loader = require.resolve('../src/loader'); 4 | 5 | 6 | describe('Define Fallback', () => { 7 | it('accepts a fallback string', () => { 8 | expect(ExtractTextPlugin.extract({ 9 | fallback: 'style-loader', 10 | use: 'css-loader', 11 | })).toEqual([ 12 | { loader, options: { omit: 1, remove: true } }, 13 | { loader: 'style-loader' }, 14 | { loader: 'css-loader' }, 15 | ]); 16 | }); 17 | 18 | it('accepts a fallback string with legacy fallbackLoader option', () => { 19 | expect(ExtractTextPlugin.extract({ 20 | fallback: 'style-loader', 21 | use: 'css-loader', 22 | })).toEqual([ 23 | { loader, options: { omit: 1, remove: true } }, 24 | { loader: 'style-loader' }, 25 | { loader: 'css-loader' }, 26 | ]); 27 | }); 28 | 29 | it('accepts a chained fallback string', () => { 30 | expect(ExtractTextPlugin.extract({ 31 | fallback: 'something-loader!style-loader', 32 | use: 'css-loader', 33 | })).toEqual([ 34 | { loader, options: { omit: 2, remove: true } }, 35 | { loader: 'something-loader' }, 36 | { loader: 'style-loader' }, 37 | { loader: 'css-loader' }, 38 | ]); 39 | }); 40 | 41 | it('accepts a fallback object', () => { 42 | expect(ExtractTextPlugin.extract({ 43 | fallback: { loader: 'style-loader' }, 44 | use: 'css-loader', 45 | })).toEqual([ 46 | { loader, options: { omit: 1, remove: true } }, 47 | { loader: 'style-loader' }, 48 | { loader: 'css-loader' }, 49 | ]); 50 | }); 51 | 52 | it('accepts an array of fallback objects', () => { 53 | expect(ExtractTextPlugin.extract({ 54 | fallback: [ 55 | { loader: 'something-loader' }, 56 | { loader: 'style-loader' }, 57 | ], 58 | use: 'css-loader', 59 | })).toEqual([ 60 | { loader, options: { omit: 2, remove: true } }, 61 | { loader: 'something-loader' }, 62 | { loader: 'style-loader' }, 63 | { loader: 'css-loader' }, 64 | ]); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /test/webpack-integration.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-dynamic-require, global-require */ 2 | import fs from 'fs'; 3 | import path from 'path'; 4 | import webpack from 'webpack'; 5 | import ExtractTextPlugin from '../src'; 6 | 7 | const cases = process.env.CASES ? process.env.CASES.split(',') : fs.readdirSync(path.join(__dirname, 'cases')); 8 | 9 | describe('Webpack Integration Tests', () => { 10 | cases.forEach((testCase) => { 11 | it(testCase, (done) => { 12 | let options = { entry: { test: './index.js' } }; 13 | const testDirectory = path.join(__dirname, 'cases', testCase); 14 | const outputDirectory = path.join(__dirname, 'js', testCase); 15 | const configFile = path.join(testDirectory, 'webpack.config.js'); 16 | 17 | if (fs.existsSync(configFile)) { 18 | options = require(configFile); 19 | } 20 | options.context = testDirectory; 21 | if (!options.module) options.module = {}; 22 | if (!options.module.loaders) { 23 | options.module.loaders = [{ test: /\.txt$/, loader: ExtractTextPlugin.extract('raw-loader') }]; 24 | } 25 | if (!options.output) options.output = { filename: '[name].js' }; 26 | if (!options.output.path) options.output.path = outputDirectory; 27 | if (process.env.CASES) { 28 | console.log(`\nwebpack.${testCase}.config.js ${JSON.stringify(options, null, 2)}`); 29 | } 30 | 31 | webpack(options, (err, stats) => { 32 | if (err) return done(err); 33 | if (stats.hasErrors()) return done(new Error(stats.toString())); 34 | const testFile = path.join(outputDirectory, 'test.js'); 35 | if (fs.existsSync(testFile)) { 36 | require(testFile)(suite); 37 | } 38 | const expectedDirectory = path.join(testDirectory, 'expected'); 39 | fs.readdirSync(expectedDirectory).forEach((file) => { 40 | const filePath = path.join(expectedDirectory, file); 41 | const actualPath = path.join(outputDirectory, file); 42 | expect(readFileOrEmpty(actualPath)).toEqual(readFileOrEmpty(filePath)); 43 | expect(readFileOrEmpty(actualPath)).toMatchSnapshot(); 44 | }); 45 | done(); 46 | }); 47 | }); 48 | }); 49 | }); 50 | 51 | function readFileOrEmpty(path) { 52 | try { 53 | return fs.readFileSync(path, 'utf-8'); 54 | } catch (e) { 55 | return ''; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extract-text-webpack-plugin", 3 | "version": "3.0.2", 4 | "author": "Tobias Koppers @sokra", 5 | "description": "Extract text from bundle into a file.", 6 | "license": "MIT", 7 | "main": "dist/cjs.js", 8 | "files": [ 9 | "dist", 10 | "schema" 11 | ], 12 | "scripts": { 13 | "start": "npm run build -- -w", 14 | "appveyor:test": "npm run test", 15 | "build": "cross-env NODE_ENV=production babel src -d dist --ignore 'src/**/*.test.js'", 16 | "build:example": "(cd example && webpack)", 17 | "clean": "del-cli dist", 18 | "lint": "eslint --cache src test", 19 | "lint-staged": "lint-staged", 20 | "prebuild": "npm run clean", 21 | "prepublish": "npm run build", 22 | "release": "standard-version", 23 | "security": "nsp check", 24 | "test": "jest", 25 | "test:watch": "jest --watch", 26 | "test:coverage": "jest --collectCoverageFrom='src/**/*.js' --coverage", 27 | "travis:lint": "npm run lint && npm run security", 28 | "travis:test": "npm run test -- --runInBand", 29 | "travis:coverage": "npm run test:coverage -- --runInBand", 30 | "webpack-defaults": "webpack-defaults" 31 | }, 32 | "dependencies": { 33 | "loader-utils": "^1.1.0", 34 | "neo-async": "^2.5.0", 35 | "schema-utils": "^0.3.0", 36 | "webpack-sources": "^1.0.1" 37 | }, 38 | "devDependencies": { 39 | "babel-cli": "^6.26.0", 40 | "babel-jest": "^21.2.0", 41 | "babel-plugin-transform-object-rest-spread": "^6.26.0", 42 | "babel-polyfill": "^6.26.0", 43 | "babel-preset-env": "^1.6.1", 44 | "cross-env": "^5.1.0", 45 | "css-loader": "^0.28.7", 46 | "del-cli": "^1.1.0", 47 | "eslint": "^4.9.0", 48 | "eslint-config-webpack": "^1.2.5", 49 | "eslint-plugin-import": "^2.8.0", 50 | "file-loader": "^1.1.5", 51 | "jest": "^21.2.1", 52 | "lint-staged": "^4.3.0", 53 | "nsp": "^2.8.1", 54 | "pre-commit": "^1.2.2", 55 | "raw-loader": "^0.5.1", 56 | "standard-version": "^4.2.0", 57 | "style-loader": "^0.19.0", 58 | "webpack": "^3.8.1", 59 | "webpack-defaults": "^1.6.0" 60 | }, 61 | "engines": { 62 | "node": ">= 4.8 < 5.0.0 || >= 5.10" 63 | }, 64 | "peerDependencies": { 65 | "webpack": "^3.1.0" 66 | }, 67 | "homepage": "http://github.com/webpack-contrib/extract-text-webpack-plugin", 68 | "repository": { 69 | "type": "git", 70 | "url": "http://github.com/webpack-contrib/extract-text-webpack-plugin.git" 71 | }, 72 | "pre-commit": "lint-staged", 73 | "lint-staged": { 74 | "*.js": [ 75 | "eslint --fix", 76 | "git add" 77 | ] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/__snapshots__/webpack-integration.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Webpack Integration Tests chunk-modules-css-wrong-order 1`] = ` 4 | ".block { 5 | color: tomato; 6 | } 7 | .App { 8 | color: black; 9 | } 10 | " 11 | `; 12 | 13 | exports[`Webpack Integration Tests chunk-modules-nested-ordered-by-id 1`] = ` 14 | "body { 15 | margin: 0; 16 | padding: 0; 17 | font-family: sans-serif; 18 | } 19 | .block { 20 | color: tomato; 21 | } 22 | .App { 23 | color: black; 24 | } 25 | " 26 | `; 27 | 28 | exports[`Webpack Integration Tests chunk-modules-ordered-by-id 1`] = ` 29 | ".block { 30 | color: tomato; 31 | } 32 | .App { 33 | color: black; 34 | } 35 | " 36 | `; 37 | 38 | exports[`Webpack Integration Tests common-async 1`] = ` 39 | "a 40 | b 41 | c 42 | " 43 | `; 44 | 45 | exports[`Webpack Integration Tests hashed-module-ids 1`] = ` 46 | "index 47 | a 48 | b 49 | " 50 | `; 51 | 52 | exports[`Webpack Integration Tests merging-chunk 1`] = ` 53 | "a 54 | b 55 | c 56 | " 57 | `; 58 | 59 | exports[`Webpack Integration Tests merging-multiple-entries 1`] = ` 60 | "a 61 | b 62 | " 63 | `; 64 | 65 | exports[`Webpack Integration Tests merging-multiple-entries 2`] = ` 66 | "a 67 | c 68 | " 69 | `; 70 | 71 | exports[`Webpack Integration Tests merging-multiple-entries-shared 1`] = ` 72 | "a 73 | " 74 | `; 75 | 76 | exports[`Webpack Integration Tests merging-multiple-entries-shared 2`] = ` 77 | "a 78 | " 79 | `; 80 | 81 | exports[`Webpack Integration Tests multiple-entries 1`] = ` 82 | "a 83 | b 84 | " 85 | `; 86 | 87 | exports[`Webpack Integration Tests multiple-entries 2`] = ` 88 | "a 89 | c 90 | " 91 | `; 92 | 93 | exports[`Webpack Integration Tests multiple-entries-filename 1`] = ` 94 | "a 95 | b 96 | " 97 | `; 98 | 99 | exports[`Webpack Integration Tests multiple-entries-filename 2`] = ` 100 | "a 101 | c 102 | " 103 | `; 104 | 105 | exports[`Webpack Integration Tests multiple-entries-filename 3`] = `""`; 106 | 107 | exports[`Webpack Integration Tests nested 1`] = ` 108 | "a 109 | b 110 | c 111 | d 112 | " 113 | `; 114 | 115 | exports[`Webpack Integration Tests optimize-tree 1`] = ` 116 | "index 117 | a 118 | b 119 | " 120 | `; 121 | 122 | exports[`Webpack Integration Tests order-undefined-error 1`] = ` 123 | "._3QiTkN7JPds2C9Bq73MpOX { 124 | } 125 | ._3dtwYD12yqGZUJ-uPQLu9z { 126 | } 127 | ._32HVXkGocfWZMaHoXEFkED { 128 | } 129 | .hbZI7DlQBB9VYNCgL20kL { 130 | } 131 | " 132 | `; 133 | 134 | exports[`Webpack Integration Tests simple 1`] = ` 135 | "a 136 | b 137 | " 138 | `; 139 | 140 | exports[`Webpack Integration Tests simple-query-object 1`] = ` 141 | "body { 142 | correct: a; 143 | } 144 | body { 145 | correct: b; 146 | } 147 | 148 | /*# sourceMappingURL=file.css.map*/" 149 | `; 150 | 151 | exports[`Webpack Integration Tests simple-queryless-object 1`] = ` 152 | "body { 153 | correct: a; 154 | } 155 | body { 156 | correct: b; 157 | } 158 | 159 | /*# sourceMappingURL=file.css.map*/" 160 | `; 161 | 162 | exports[`Webpack Integration Tests splitted-chunk 1`] = ` 163 | "a 164 | " 165 | `; 166 | 167 | exports[`Webpack Integration Tests splitted-mix-multiple-entries 1`] = `"a"`; 168 | 169 | exports[`Webpack Integration Tests splitted-mix-multiple-entries 2`] = `"b"`; 170 | 171 | exports[`Webpack Integration Tests splitted-multiple-entries 1`] = `""`; 172 | 173 | exports[`Webpack Integration Tests splitted-multiple-entries 2`] = `""`; 174 | 175 | exports[`Webpack Integration Tests string-export-with-optimize-module-order 1`] = ` 176 | "a 177 | b 178 | " 179 | `; 180 | -------------------------------------------------------------------------------- /test/define-loader.test.js: -------------------------------------------------------------------------------- 1 | import ExtractTextPlugin from '../src'; 2 | 3 | const loader = require.resolve('../src/loader'); 4 | 5 | 6 | describe('specifying loader', () => { 7 | it('accepts a loader string', () => { 8 | expect(ExtractTextPlugin.extract('css-loader')).toEqual([ 9 | { loader, options: { omit: 0, remove: true } }, 10 | { loader: 'css-loader' }, 11 | ]); 12 | }); 13 | 14 | it('accepts a chained loader string', () => { 15 | expect(ExtractTextPlugin.extract( 16 | 'css-loader!postcss-loader!sass-loader', 17 | )).toEqual([ 18 | { loader, options: { omit: 0, remove: true } }, 19 | { loader: 'css-loader' }, 20 | { loader: 'postcss-loader' }, 21 | { loader: 'sass-loader' }, 22 | ]); 23 | }); 24 | 25 | it('accepts an array of loader names', () => { 26 | expect(ExtractTextPlugin.extract( 27 | ['css-loader', 'postcss-loader', 'sass-loader'], 28 | )).toEqual([ 29 | { loader, options: { omit: 0, remove: true } }, 30 | { loader: 'css-loader' }, 31 | { loader: 'postcss-loader' }, 32 | { loader: 'sass-loader' }, 33 | ]); 34 | }); 35 | 36 | it('accepts a loader object', () => { 37 | expect(ExtractTextPlugin.extract({ use: 'css-loader' })).toEqual([ 38 | { loader, options: { omit: 0, remove: true } }, 39 | { loader: 'css-loader' }, 40 | ]); 41 | }); 42 | 43 | it('accepts an array of loader names in loader object', () => { 44 | expect(ExtractTextPlugin.extract({ 45 | use: ['css-loader', 'postcss-loader', 'sass-loader'], 46 | })).toEqual([ 47 | { loader, options: { omit: 0, remove: true } }, 48 | { loader: 'css-loader' }, 49 | { loader: 'postcss-loader' }, 50 | { loader: 'sass-loader' }, 51 | ]); 52 | }); 53 | 54 | it('accepts a loader object with an options object', () => { 55 | expect(ExtractTextPlugin.extract( 56 | { use: 'css-loader', options: { modules: true } }, 57 | )).toEqual([ 58 | { loader, options: { omit: 0, remove: true } }, 59 | { use: 'css-loader', options: { modules: true } }, 60 | ]); 61 | }); 62 | 63 | it('accepts a loader object with an options object in array of loaders', () => { 64 | expect(ExtractTextPlugin.extract({ 65 | use: [ 66 | { loader: 'css-loader', options: { modules: true } }, 67 | 'postcss-loader', 68 | ], 69 | })).toEqual([ 70 | { loader, options: { omit: 0, remove: true } }, 71 | { loader: 'css-loader', options: { modules: true } }, 72 | { loader: 'postcss-loader' }, 73 | ]); 74 | }); 75 | 76 | it('accepts a loader object with a (legacy) query object', () => { 77 | expect(ExtractTextPlugin.extract( 78 | { use: 'css-loader', query: { modules: true } }, 79 | )).toEqual([ 80 | { loader, options: { omit: 0, remove: true } }, 81 | { use: 'css-loader', query: { modules: true } }, 82 | ]); 83 | }); 84 | 85 | it('accepts a loader object with a legacy loader field', () => { 86 | expect(ExtractTextPlugin.extract( 87 | { loader: 'css-loader', query: { modules: true } }, 88 | )).toEqual([ 89 | { loader, options: { omit: 0, remove: true } }, 90 | { loader: 'css-loader', query: { modules: true } }, 91 | ]); 92 | }); 93 | 94 | it('accepts an array of loader objects', () => { 95 | expect(ExtractTextPlugin.extract([ 96 | { loader: 'css-loader' }, 97 | { loader: 'postcss-loader' }, 98 | { loader: 'sass-loader' }, 99 | ])).toEqual([ 100 | { loader, options: { omit: 0, remove: true } }, 101 | { loader: 'css-loader' }, 102 | { loader: 'postcss-loader' }, 103 | { loader: 'sass-loader' }, 104 | ]); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | Svg Vector Icons : http://www.onlinewebfonts.com/icon 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/loader.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import loaderUtils from 'loader-utils'; 4 | import NodeTemplatePlugin from 'webpack/lib/node/NodeTemplatePlugin'; 5 | import NodeTargetPlugin from 'webpack/lib/node/NodeTargetPlugin'; 6 | import LibraryTemplatePlugin from 'webpack/lib/LibraryTemplatePlugin'; 7 | import SingleEntryPlugin from 'webpack/lib/SingleEntryPlugin'; 8 | import LimitChunkCountPlugin from 'webpack/lib/optimize/LimitChunkCountPlugin'; 9 | 10 | const NS = path.dirname(fs.realpathSync(__filename)); 11 | 12 | export default source => source; 13 | 14 | export function pitch(request) { 15 | const query = loaderUtils.getOptions(this) || {}; 16 | let loaders = this.loaders.slice(this.loaderIndex + 1); 17 | // We already in child compiler, return empty bundle 18 | if (this[NS] === undefined) { // eslint-disable-line no-undefined 19 | throw new Error( 20 | '"extract-text-webpack-plugin" loader is used without the corresponding plugin, ' + 21 | 'refer to https://github.com/webpack/extract-text-webpack-plugin for the usage example', 22 | ); 23 | } else if (this[NS] === false) { 24 | return ''; 25 | } else if (this[NS](null, query)) { 26 | if (query.omit) { 27 | this.loaderIndex += +query.omit + 1; 28 | request = request.split('!').slice(+query.omit).join('!'); 29 | loaders = loaders.slice(+query.omit); 30 | } 31 | let resultSource; 32 | if (query.remove) { 33 | resultSource = '// removed by extract-text-webpack-plugin'; 34 | } else { 35 | resultSource = undefined; // eslint-disable-line no-undefined 36 | } 37 | 38 | const childFilename = 'extract-text-webpack-plugin-output-filename'; // eslint-disable-line no-path-concat 39 | const publicPath = typeof query.publicPath === 'string' ? query.publicPath : this._compilation.outputOptions.publicPath; 40 | const outputOptions = { 41 | filename: childFilename, 42 | publicPath, 43 | }; 44 | const childCompiler = this._compilation.createChildCompiler(`extract-text-webpack-plugin ${NS} ${request}`, outputOptions); 45 | childCompiler.apply(new NodeTemplatePlugin(outputOptions)); 46 | childCompiler.apply(new LibraryTemplatePlugin(null, 'commonjs2')); 47 | childCompiler.apply(new NodeTargetPlugin()); 48 | childCompiler.apply(new SingleEntryPlugin(this.context, `!!${request}`)); 49 | childCompiler.apply(new LimitChunkCountPlugin({ maxChunks: 1 })); 50 | // We set loaderContext[NS] = false to indicate we already in 51 | // a child compiler so we don't spawn another child compilers from there. 52 | childCompiler.plugin('this-compilation', (compilation) => { 53 | compilation.plugin('normal-module-loader', (loaderContext, module) => { 54 | loaderContext[NS] = false; 55 | if (module.request === request) { 56 | module.loaders = loaders.map((loader) => { 57 | return ({ 58 | loader: loader.path, 59 | options: loader.options, 60 | }); 61 | }); 62 | } 63 | }); 64 | }); 65 | 66 | let source; 67 | childCompiler.plugin('after-compile', (compilation, callback) => { 68 | source = compilation.assets[childFilename] && compilation.assets[childFilename].source(); 69 | 70 | // Remove all chunk assets 71 | compilation.chunks.forEach((chunk) => { 72 | chunk.files.forEach((file) => { 73 | delete compilation.assets[file]; 74 | }); 75 | }); 76 | 77 | callback(); 78 | }); 79 | const callback = this.async(); 80 | childCompiler.runAsChild((err, entries, compilation) => { 81 | if (err) return callback(err); 82 | 83 | if (compilation.errors.length > 0) { 84 | return callback(compilation.errors[0]); 85 | } 86 | compilation.fileDependencies.forEach((dep) => { 87 | this.addDependency(dep); 88 | }, this); 89 | compilation.contextDependencies.forEach((dep) => { 90 | this.addContextDependency(dep); 91 | }, this); 92 | if (!source) { 93 | return callback(new Error("Didn't get a result from child compiler")); 94 | } 95 | try { 96 | let text = this.exec(source, request); 97 | if (typeof text === 'string') { 98 | text = [[compilation.entries[0].identifier(), text]]; 99 | } else { 100 | text.forEach((item) => { 101 | const id = item[0]; 102 | compilation.modules.forEach((module) => { 103 | if (module.id === id) { item[0] = module.identifier(); } 104 | }); 105 | }); 106 | } 107 | this[NS](text, query); 108 | if (text.locals && typeof resultSource !== 'undefined') { 109 | resultSource += `\nmodule.exports = ${JSON.stringify(text.locals)};`; 110 | } 111 | } catch (e) { 112 | return callback(e); 113 | } 114 | if (resultSource) { callback(null, resultSource); } else { callback(); } 115 | }); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 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 | 6 | ## [3.0.2](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v3.0.1...v3.0.2) (2017-10-25) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * refer to the `entrypoint` instead of the first `module` (`module.identifier`) ([#601](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/601)) ([d5a1de2](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/d5a1de2)) 12 | 13 | 14 | 15 | 16 | ## [3.0.1](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v3.0.0...v3.0.1) (2017-10-03) 17 | 18 | 19 | ### Bug Fixes 20 | 21 | * **index:** stricter check for `shouldExtract !== wasExtracted` ([#605](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/605)) ([510704f](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/510704f)) 22 | * get real path from `__filename` instead of `__dirname` (`NS`) ([8de6558](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/8de6558)) 23 | 24 | 25 | 26 | 27 | # [3.0.0](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v2.1.2...v3.0.0) (2017-07-10) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * add missing `options.ignoreOrder` details in Error message ([#539](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/539)) ([dd43832](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/dd43832)) 33 | 34 | 35 | ### Code Refactoring 36 | 37 | * Apply webpack-defaults & webpack 3.x support ([#540](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/540)) ([7ae32d9](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/7ae32d9)) 38 | 39 | 40 | ### BREAKING CHANGES 41 | 42 | * Enforces `engines` of `"node": ">=4.3.0 < 5.0.0 || >= 5.10` 43 | 44 | - refactor: DeprecationWarning: Chunk.modules [543](https://github.com/webpack-contrib/extract-text-webpack-plugin/pull/543) 45 | * Updates to `Chunk.mapModules`. This release is not backwards compatible with `Webpack 2.x` due to breaking changes in webpack/webpack#4764 46 | 47 | - fix: css generation order issue see: webpack/webpack#5225 48 | * Enforces `peerDependencies` of `"webpack": "^3.1.0"`. 49 | 50 | 51 | 52 | 53 | # [3.0.0-rc.2](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v3.0.0-rc.1...v3.0.0-rc.2) (2017-07-10) 54 | 55 | 56 | ### Bug Fixes 57 | 58 | * Modules shouldn't be passed to sort ([#568](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/568)) ([113cabb](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/113cabb)) 59 | 60 | 61 | 62 | 63 | # [3.0.0-rc.1](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v3.0.0-rc.0...v3.0.0-rc.1) (2017-07-07) 64 | 65 | 66 | ### Bug Fixes 67 | 68 | * Module sorting ([27e3a28](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/27e3a28)) 69 | 70 | 71 | 72 | 73 | # [3.0.0-rc.0](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v3.0.0-beta.3...v3.0.0-rc.0) (2017-07-07) 74 | 75 | 76 | ### Code Refactoring 77 | 78 | * Update deprecated `chunk.modules` functions ([#553](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/553)) ([be7936d](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/be7936d)) 79 | 80 | 81 | ### BREAKING CHANGES 82 | 83 | * Updates to `Chunk.mapModules | forEachModule | getNumberOfModules`. This release is not backwards compatible with `Webpack 2.x` due to breaking changes in webpack/webpack#4764 84 | 85 | 86 | 87 | 88 | # [3.0.0-beta.3](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v3.0.0-beta.2...v3.0.0-beta.3) (2017-06-24) 89 | 90 | ### Bug Fixes 91 | 92 | * Distribute schema with package ([5d0c28f](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/5d0c28f)) 93 | 94 | 95 | 96 | # [3.0.0-beta.2](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v3.0.0-beta.1...v3.0.0-beta.2) (2017-06-24) 97 | 98 | * Skipped due to deployment issues with schema 99 | 100 | 101 | # [3.0.0-beta.1](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v3.0.0-beta.0...v3.0.0-beta.1) (2017-06-24) 102 | 103 | 104 | * Skipped due to deployment issues with schema 105 | 106 | 107 | 108 | # [3.0.0-beta.0](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v2.1.2...v3.0.0-beta.0) (2017-06-23) 109 | 110 | 111 | ### Bug Fixes 112 | 113 | * add missing `options.ignoreOrder` details in Error message ([#539](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/539)) ([dd43832](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/dd43832)) 114 | 115 | 116 | ### Code Refactoring 117 | 118 | * Apply webpack-defaults ([#542](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/542)) ([292e217](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/292e217)) 119 | * Chunk.modules deprecation warning ([28171b2](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/28171b2)) 120 | 121 | 122 | ### BREAKING CHANGES 123 | 124 | * Updates to `Chunk.mapModules`. This release is not backwards compatible with `Webpack 2.x` due to breaking changes in webpack/webpack#4764 125 | * Enforces `peerDependencies` of `"webpack": ">= 3.0.0-rc.0 || ^3.0.0"`. 126 | * Enforces `engines` of `"node": ">=4.3.0 < 5.0.0 || >= 5.10` 127 | 128 | 129 | 130 | 131 | ## [2.1.2](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v2.1.1...v2.1.2) (2017-06-08) 132 | 133 | 134 | 135 | 136 | ## [2.1.1](https://github.com/webpack-contrib/extract-text-webpack-plugin/compare/v2.1.0...v2.1.1) (2017-06-08) 137 | 138 | 139 | ### Bug Fixes 140 | 141 | * add a not null check for the content property before throwing error ([#404](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/404)) ([58dd5d3](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/58dd5d3)) 142 | * **loader:** rm unnecessary `this.cacheable` (caching) ([#530](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/530)) ([c3cb091](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/c3cb091)) 143 | * don't extract from common async chunks ([#508](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/508)) ([e595417](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/e595417)) 144 | * validation schema (`schema-utils`) ([#527](https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/527)) ([dfeb347](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/dfeb347)) 145 | 146 | 147 | 148 | 149 | # [2.1.0](https://github.com/webpack/extract-text-webpack-plugin/compare/v2.0.0...v2.1.0) (2017-03-05) 150 | 151 | ### Features 152 | 153 | * The plugin **filename** accepts a function now. [c9a19ad](https://github.com/webpack-contrib/extract-text-webpack-plugin/commit/c9a19ad), closes [#423](https://github.com/webpack-contrib/extract-text-webpack-plugin/pull/423) 154 | 155 | 156 | # [2.0.0](https://github.com/webpack/extract-text-webpack-plugin/compare/v2.0.0-rc.3...v2.0.0) (2017-02-24) 157 | 158 | 159 | # [2.0.0-rc.2](https://github.com/webpack/extract-text-webpack-plugin/compare/v2.0.0-rc.1...v2.0.0-rc.2) (2017-01-28) 160 | 161 | 162 | ### Bug Fixes 163 | 164 | * **schema:** allow `extract` to accept omit/remove flags ([8ce93d5](https://github.com/webpack/extract-text-webpack-plugin/commit/8ce93d5)), closes [#371](https://github.com/webpack/extract-text-webpack-plugin/issues/371) 165 | * **schema:** connect loader schema with the code properly ([03bb4aa](https://github.com/webpack/extract-text-webpack-plugin/commit/03bb4aa)) 166 | * **schema:** emit proper error messages ([70cbd4b](https://github.com/webpack/extract-text-webpack-plugin/commit/70cbd4b)) 167 | 168 | 169 | ### Features 170 | 171 | * **errors:** show nicer errors if there are extra fields ([76a171d](https://github.com/webpack/extract-text-webpack-plugin/commit/76a171d)) 172 | 173 | 174 | 175 | 176 | # [2.0.0-rc.1](https://github.com/webpack/extract-text-webpack-plugin/compare/v2.0.0-rc.0...v2.0.0-rc.1) (2017-01-28) 177 | 178 | 179 | ### Bug Fixes 180 | 181 | * **options:** pass proper loader options to children ([#266](https://github.com/webpack/extract-text-webpack-plugin/issues/266)) ([6abf42d](https://github.com/webpack/extract-text-webpack-plugin/commit/6abf42d)) 182 | 183 | 184 | 185 | 186 | # [2.0.0-rc.0](https://github.com/webpack/extract-text-webpack-plugin/compare/v2.0.0-beta.5...v2.0.0-rc.0) (2017-01-26) 187 | 188 | 189 | ### Bug Fixes 190 | 191 | * **readme:** Incorrect loader configuration ([e477cc7](https://github.com/webpack/extract-text-webpack-plugin/commit/e477cc7)) 192 | 193 | 194 | ### Features 195 | 196 | * **extract:** return an array of loader objects ([#343](https://github.com/webpack/extract-text-webpack-plugin/issues/343)) ([74b86e0](https://github.com/webpack/extract-text-webpack-plugin/commit/74b86e0)) 197 | 198 | 199 | 200 | # Change Log 201 | 202 | 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. 203 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import Chunk from 'webpack/lib/Chunk'; 4 | import { ConcatSource } from 'webpack-sources'; 5 | import async from 'neo-async'; 6 | import loaderUtils from 'loader-utils'; 7 | import validateOptions from 'schema-utils'; 8 | import ExtractTextPluginCompilation from './lib/ExtractTextPluginCompilation'; 9 | import OrderUndefinedError from './lib/OrderUndefinedError'; 10 | import { 11 | isInitialOrHasNoParents, 12 | isInvalidOrder, 13 | getOrder, 14 | getLoaderObject, 15 | mergeOptions, 16 | isString, 17 | isFunction, 18 | } from './lib/helpers'; 19 | 20 | const NS = path.dirname(fs.realpathSync(__filename)); 21 | 22 | let nextId = 0; 23 | 24 | class ExtractTextPlugin { 25 | constructor(options) { 26 | if (isString(options)) { 27 | options = { filename: options }; 28 | } else { 29 | validateOptions(path.resolve(__dirname, '../schema/plugin.json'), options, 'Extract Text Plugin'); 30 | } 31 | this.filename = options.filename; 32 | this.id = options.id != null ? options.id : ++nextId; 33 | this.options = {}; 34 | mergeOptions(this.options, options); 35 | delete this.options.filename; 36 | delete this.options.id; 37 | } 38 | 39 | static loader(options) { 40 | return { loader: require.resolve('./loader'), options }; 41 | } 42 | 43 | applyAdditionalInformation(source, info) { 44 | if (info) { 45 | return new ConcatSource( 46 | `@media ${info[0]} {`, 47 | source, 48 | '}', 49 | ); 50 | } 51 | return source; 52 | } 53 | 54 | loader(options) { 55 | return ExtractTextPlugin.loader(mergeOptions({ id: this.id }, options)); 56 | } 57 | 58 | mergeNonInitialChunks(chunk, intoChunk, checkedChunks) { 59 | if (!intoChunk) { 60 | checkedChunks = []; 61 | chunk.chunks.forEach((c) => { 62 | if (isInitialOrHasNoParents(c)) return; 63 | this.mergeNonInitialChunks(c, chunk, checkedChunks); 64 | }, this); 65 | } else if (checkedChunks.indexOf(chunk) < 0) { 66 | checkedChunks.push(chunk); 67 | chunk.forEachModule((module) => { 68 | intoChunk.addModule(module); 69 | module.addChunk(intoChunk); 70 | }); 71 | chunk.chunks.forEach((c) => { 72 | if (isInitialOrHasNoParents(c)) return; 73 | this.mergeNonInitialChunks(c, intoChunk, checkedChunks); 74 | }, this); 75 | } 76 | } 77 | 78 | renderExtractedChunk(chunk) { 79 | const source = new ConcatSource(); 80 | chunk.forEachModule((module) => { 81 | const moduleSource = module.source(); 82 | source.add(this.applyAdditionalInformation(moduleSource, module.additionalInformation)); 83 | }, this); 84 | return source; 85 | } 86 | 87 | extract(options) { 88 | if (Array.isArray(options) || isString(options) || typeof options.options === 'object' || typeof options.query === 'object') { 89 | options = { use: options }; 90 | } else { 91 | validateOptions(path.resolve(__dirname, '../schema/loader.json'), options, 'Extract Text Plugin (Loader)'); 92 | } 93 | let loader = options.use; 94 | let before = options.fallback || []; 95 | if (isString(loader)) { 96 | loader = loader.split('!'); 97 | } 98 | if (isString(before)) { 99 | before = before.split('!'); 100 | } else if (!Array.isArray(before)) { 101 | before = [before]; 102 | } 103 | options = mergeOptions({ omit: before.length, remove: true }, options); 104 | delete options.use; 105 | delete options.fallback; 106 | return [this.loader(options)] 107 | .concat(before, loader) 108 | .map(getLoaderObject); 109 | } 110 | 111 | apply(compiler) { 112 | const options = this.options; 113 | compiler.plugin('this-compilation', (compilation) => { 114 | const extractCompilation = new ExtractTextPluginCompilation(); 115 | compilation.plugin('normal-module-loader', (loaderContext, module) => { 116 | loaderContext[NS] = (content, opt) => { 117 | if (options.disable) { return false; } 118 | if (!Array.isArray(content) && content != null) { throw new Error(`Exported value was not extracted as an array: ${JSON.stringify(content)}`); } 119 | module[NS] = { 120 | content, 121 | options: opt || {}, 122 | }; 123 | return options.allChunks || module[`${NS}/extract`]; // eslint-disable-line no-path-concat 124 | }; 125 | }); 126 | const filename = this.filename; 127 | const id = this.id; 128 | let extractedChunks; 129 | compilation.plugin('optimize-tree', (chunks, modules, callback) => { 130 | extractedChunks = chunks.map(() => new Chunk()); 131 | chunks.forEach((chunk, i) => { 132 | const extractedChunk = extractedChunks[i]; 133 | extractedChunk.index = i; 134 | extractedChunk.originalChunk = chunk; 135 | extractedChunk.name = chunk.name; 136 | extractedChunk.entrypoints = chunk.entrypoints; 137 | chunk.chunks.forEach((c) => { 138 | extractedChunk.addChunk(extractedChunks[chunks.indexOf(c)]); 139 | }); 140 | chunk.parents.forEach((c) => { 141 | extractedChunk.addParent(extractedChunks[chunks.indexOf(c)]); 142 | }); 143 | }); 144 | async.forEach(chunks, (chunk, callback) => { // eslint-disable-line no-shadow 145 | const extractedChunk = extractedChunks[chunks.indexOf(chunk)]; 146 | const shouldExtract = !!options.allChunks; 147 | chunk.sortModules(); 148 | async.forEach(chunk.mapModules(c => c), (module, callback) => { // eslint-disable-line no-shadow 149 | let meta = module[NS]; 150 | if (meta && (!meta.options.id || meta.options.id === id)) { 151 | const wasExtracted = Array.isArray(meta.content); 152 | // A stricter `shouldExtract !== wasExtracted` check to guard against cases where a previously extracted 153 | // module would be extracted twice. Happens when a module is a dependency of an initial and a non-initial 154 | // chunk. See issue #604 155 | 156 | // check every module's chunks.parents() to decide extract or not 157 | for (let i = 0; i < module.chunks.length; i++) { 158 | if (!isInitialOrHasNoParents(module.chunks[i]) && !module.extracted) { 159 | module.extracted = true; 160 | break; 161 | } 162 | } 163 | if (shouldExtract || (!module.extracted && !wasExtracted)) { 164 | module[`${NS}/extract`] = true; // eslint-disable-line no-path-concat 165 | compilation.rebuildModule(module, (err) => { 166 | if (err) { 167 | compilation.errors.push(err); 168 | return callback(); 169 | } 170 | meta = module[NS]; 171 | // Error out if content is not an array and is not null 172 | if (!Array.isArray(meta.content) && meta.content != null) { 173 | err = new Error(`${module.identifier()} doesn't export content`); 174 | compilation.errors.push(err); 175 | return callback(); 176 | } 177 | if (meta.content) { extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk); } 178 | callback(); 179 | }); 180 | } else { 181 | if (meta.content) { extractCompilation.addResultToChunk(module.identifier(), meta.content, module, extractedChunk); } 182 | callback(); 183 | } 184 | } else callback(); 185 | }, (err) => { 186 | if (err) return callback(err); 187 | callback(); 188 | }); 189 | }, (err) => { 190 | if (err) return callback(err); 191 | extractedChunks.forEach((extractedChunk) => { 192 | if (isInitialOrHasNoParents(extractedChunk)) { this.mergeNonInitialChunks(extractedChunk); } 193 | }, this); 194 | extractedChunks.forEach((extractedChunk) => { 195 | if (!isInitialOrHasNoParents(extractedChunk)) { 196 | extractedChunk.forEachModule((module) => { 197 | extractedChunk.removeModule(module); 198 | }); 199 | } 200 | }); 201 | compilation.applyPlugins('optimize-extracted-chunks', extractedChunks); 202 | callback(); 203 | }); 204 | }); 205 | compilation.plugin('additional-assets', (callback) => { 206 | extractedChunks.forEach((extractedChunk) => { 207 | if (extractedChunk.getNumberOfModules()) { 208 | extractedChunk.sortModules((a, b) => { 209 | if (!options.ignoreOrder && isInvalidOrder(a, b)) { 210 | compilation.errors.push(new OrderUndefinedError(a.getOriginalModule())); 211 | compilation.errors.push(new OrderUndefinedError(b.getOriginalModule())); 212 | } 213 | return getOrder(a, b); 214 | }); 215 | const chunk = extractedChunk.originalChunk; 216 | const source = this.renderExtractedChunk(extractedChunk); 217 | 218 | const getPath = format => compilation.getPath(format, { 219 | chunk, 220 | }).replace(/\[(?:(\w+):)?contenthash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, function () { // eslint-disable-line func-names 221 | return loaderUtils.getHashDigest(source.source(), arguments[1], arguments[2], parseInt(arguments[3], 10)); 222 | }); 223 | 224 | const file = (isFunction(filename)) ? filename(getPath) : getPath(filename); 225 | 226 | compilation.assets[file] = source; 227 | chunk.files.push(file); 228 | } 229 | }, this); 230 | callback(); 231 | }); 232 | }); 233 | } 234 | } 235 | 236 | ExtractTextPlugin.extract = ExtractTextPlugin.prototype.extract.bind(ExtractTextPlugin); 237 | 238 | export default ExtractTextPlugin; 239 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | 3 | Please use: https://github.com/webpack-contrib/mini-css-extract-plugin 4 | 5 | If you have problem(s) with migration on MiniCssExtractPlugin feel free to open issue with reproducible test repo, thanks 6 | 7 | --- 8 | 9 | [![npm][npm]][npm-url] 10 | [![node][node]][node-url] 11 | [![deps][deps]][deps-url] 12 | [![tests][tests]][tests-url] 13 | [![coverage][cover]][cover-url] 14 | [![chat][chat]][chat-url] 15 | 16 |
17 | 19 | 20 | 22 | 23 |

Extract Text Plugin

24 |

Extract text from a bundle, or bundles, into a separate file.

25 |
26 | 27 |

Install

28 | 29 | ```bash 30 | # for webpack 3 31 | npm install --save-dev extract-text-webpack-plugin 32 | # for webpack 2 33 | npm install --save-dev extract-text-webpack-plugin@2.1.2 34 | # for webpack 1 35 | npm install --save-dev extract-text-webpack-plugin@1.0.1 36 | ``` 37 | 38 |

Usage

39 | 40 | > :warning: Since webpack v4 the `extract-text-webpack-plugin` should not be used for css. Use [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) instead. 41 | 42 | > :warning: For webpack v1, see [the README in the webpack-1 branch](https://github.com/webpack/extract-text-webpack-plugin/blob/webpack-1/README.md). 43 | 44 | ```js 45 | const ExtractTextPlugin = require("extract-text-webpack-plugin"); 46 | 47 | module.exports = { 48 | module: { 49 | rules: [ 50 | { 51 | test: /\.css$/, 52 | use: ExtractTextPlugin.extract({ 53 | fallback: "style-loader", 54 | use: "css-loader" 55 | }) 56 | } 57 | ] 58 | }, 59 | plugins: [ 60 | new ExtractTextPlugin("styles.css"), 61 | ] 62 | } 63 | ``` 64 | 65 | It moves all the required `*.css` modules in entry chunks into a separate CSS file. So your styles are no longer inlined into the JS bundle, but in a separate CSS file (`styles.css`). If your total stylesheet volume is big, it will be faster because the CSS bundle is loaded in parallel to the JS bundle. 66 | 67 | |Advantages|Caveats| 68 | |:---------|:------| 69 | | Fewer style tags (older IE has a limit) | Additional HTTP request | 70 | | CSS SourceMap (with `devtool: "source-map"` and `extract-text-webpack-plugin?sourceMap`) | Longer compilation time | 71 | | CSS requested in parallel | No runtime public path modification | 72 | | CSS cached separate | No Hot Module Replacement | 73 | | Faster runtime (less code and DOM operations) | ... | 74 | 75 |

Options

76 | 77 | ```js 78 | new ExtractTextPlugin(options: filename | object) 79 | ``` 80 | 81 | |Name|Type|Description| 82 | |:--:|:--:|:----------| 83 | |**`id`**|`{String}`|Unique ident for this plugin instance. (For advanced usage only, by default automatically generated)| 84 | |**`filename`**|`{String\|Function}`|Name of the result file. May contain `[name]`, `[id]` and `[contenthash]`| 85 | |**`allChunks`**|`{Boolean}`|Extract from all additional chunks too (by default it extracts only from the initial chunk(s))
When using `CommonsChunkPlugin` and there are extracted chunks (from `ExtractTextPlugin.extract`) in the commons chunk, `allChunks` **must** be set to `true`| 86 | |**`disable`**|`{Boolean}`|Disables the plugin| 87 | |**`ignoreOrder`**|`{Boolean}`|Disables order check (useful for CSS Modules!), `false` by default| 88 | 89 | * `[name]` name of the chunk 90 | * `[id]` number of the chunk 91 | * `[contenthash]` hash of the content of the extracted file 92 | * `[:contenthash::]` optionally you can configure 93 | * other `hashType`s, e.g. `sha1`, `md5`, `sha256`, `sha512` 94 | * other `digestType`s, e.g. `hex`, `base26`, `base32`, `base36`, `base49`, `base52`, `base58`, `base62`, `base64` 95 | * and `length`, the length of the hash in chars 96 | 97 | > :warning: `ExtractTextPlugin` generates a file **per entry**, so you must use `[name]`, `[id]` or `[contenthash]` when using multiple entries. 98 | 99 | #### `#extract` 100 | 101 | ```js 102 | ExtractTextPlugin.extract(options: loader | object) 103 | ``` 104 | 105 | Creates an extracting loader from an existing loader. Supports loaders of type `{ loader: [name]-loader -> {String}, options: {} -> {Object} }`. 106 | 107 | |Name|Type|Description| 108 | |:--:|:--:|:----------| 109 | |**`options.use`**|`{String}`/`{Array}`/`{Object}`|Loader(s) that should be used for converting the resource to a CSS exporting module _(required)_| 110 | |**`options.fallback`**|`{String}`/`{Array}`/`{Object}`|loader(e.g `'style-loader'`) that should be used when the CSS is not extracted (i.e. in an additional chunk when `allChunks: false`)| 111 | |**`options.publicPath`**|`{String}`|Override the `publicPath` setting for this loader| 112 | 113 | 114 | #### Multiple Instances 115 | 116 | There is also an `extract` function on the instance. You should use this if you have more than one instance of `ExtractTextPlugin`. 117 | 118 | ```js 119 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 120 | 121 | // Create multiple instances 122 | const extractCSS = new ExtractTextPlugin('stylesheets/[name]-one.css'); 123 | const extractLESS = new ExtractTextPlugin('stylesheets/[name]-two.css'); 124 | 125 | module.exports = { 126 | module: { 127 | rules: [ 128 | { 129 | test: /\.css$/, 130 | use: extractCSS.extract([ 'css-loader', 'postcss-loader' ]) 131 | }, 132 | { 133 | test: /\.less$/i, 134 | use: extractLESS.extract([ 'css-loader', 'less-loader' ]) 135 | }, 136 | ] 137 | }, 138 | plugins: [ 139 | extractCSS, 140 | extractLESS 141 | ] 142 | }; 143 | ``` 144 | 145 | ### Extracting Sass or LESS 146 | 147 | The configuration is the same, switch out `sass-loader` for `less-loader` when necessary. 148 | 149 | ```js 150 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 151 | 152 | module.exports = { 153 | module: { 154 | rules: [ 155 | { 156 | test: /\.scss$/, 157 | use: ExtractTextPlugin.extract({ 158 | fallback: 'style-loader', 159 | use: ['css-loader', 'sass-loader'] 160 | }) 161 | } 162 | ] 163 | }, 164 | plugins: [ 165 | new ExtractTextPlugin('style.css') 166 | //if you want to pass in options, you can do so: 167 | //new ExtractTextPlugin({ 168 | // filename: 'style.css' 169 | //}) 170 | ] 171 | } 172 | ``` 173 | 174 | ### `url()` Resolving 175 | 176 | If you are finding that urls are not resolving properly when you run webpack. You can expand your loader functionality with options. The `url: false` property allows your paths resolved without any changes. 177 | 178 | ```js 179 | const ExtractTextPlugin = require('extract-text-webpack-plugin'); 180 | 181 | module.exports = { 182 | module: { 183 | rules: [ 184 | { 185 | test: /\.scss$/, 186 | use: ExtractTextPlugin.extract({ 187 | fallback: 'style-loader', 188 | use: [ 189 | { 190 | loader: 'css-loader', 191 | options: { 192 | // If you are having trouble with urls not resolving add this setting. 193 | // See https://github.com/webpack-contrib/css-loader#url 194 | url: false, 195 | minimize: true, 196 | sourceMap: true 197 | } 198 | }, 199 | { 200 | loader: 'sass-loader', 201 | options: { 202 | sourceMap: true 203 | } 204 | } 205 | ] 206 | }) 207 | } 208 | ] 209 | } 210 | } 211 | ``` 212 | 213 | ### Modify filename 214 | 215 | `filename` parameter could be `Function`. It passes `getPath` to process the format like `css/[name].css` and returns the real file name, `css/js/a.css`. You can replace `css/js` with `css` then you will get the new path `css/a.css`. 216 | 217 | 218 | ```js 219 | entry: { 220 | 'js/a': "./a" 221 | }, 222 | plugins: [ 223 | new ExtractTextPlugin({ 224 | filename: (getPath) => { 225 | return getPath('css/[name].css').replace('css/js', 'css'); 226 | }, 227 | allChunks: true 228 | }) 229 | ] 230 | ``` 231 | 232 |

Maintainers

233 | 234 | 235 | 236 | 237 | 243 | 249 | 255 | 261 | 262 | 263 |
238 | 240 |
241 | Juho Vepsäläinen 242 |
244 | 246 |
247 | Joshua Wiens 248 |
250 | 252 |
253 | Kees Kluskens 254 |
256 | 258 |
259 | Sean Larkin 260 |
264 | 265 | 266 | [npm]: https://img.shields.io/npm/v/extract-text-webpack-plugin.svg 267 | [npm-url]: https://npmjs.com/package/extract-text-webpack-plugin 268 | 269 | [node]: https://img.shields.io/node/v/extract-text-webpack-plugin.svg 270 | [node-url]: https://nodejs.org 271 | 272 | [deps]: https://david-dm.org/webpack-contrib/extract-text-webpack-plugin.svg 273 | [deps-url]: https://david-dm.org/webpack-contrib/extract-text-webpack-plugin 274 | 275 | [tests]: http://img.shields.io/travis/webpack-contrib/extract-text-webpack-plugin.svg 276 | [tests-url]: https://travis-ci.org/webpack-contrib/extract-text-webpack-plugin 277 | 278 | [cover]: https://coveralls.io/repos/github/webpack-contrib/extract-text-webpack-plugin/badge.svg 279 | [cover-url]: https://coveralls.io/github/webpack-contrib/extract-text-webpack-plugin 280 | 281 | [chat]: https://badges.gitter.im/webpack/webpack.svg 282 | [chat-url]: https://gitter.im/webpack/webpack 283 | --------------------------------------------------------------------------------