diff --git a/src/index.js b/src/index.js index b12fd68f..467853a8 100644 --- a/src/index.js +++ b/src/index.js @@ -49,6 +49,8 @@ class MiniCssExtractPlugin { identifier, identifierIndex, content, + layer, + supports, media, sourceMap, assets, @@ -61,6 +63,8 @@ class MiniCssExtractPlugin { this._identifier = identifier; this._identifierIndex = identifierIndex; this.content = content; + this.layer = layer; + this.supports = supports; this.media = media; this.sourceMap = sourceMap; this.assets = assets; @@ -108,6 +112,8 @@ class MiniCssExtractPlugin { updateCacheModule(module) { if ( this.content !== module.content || + this.layer !== module.layer || + this.supports !== module.supports || this.media !== module.media || this.sourceMap !== module.sourceMap || this.assets !== module.assets || @@ -116,6 +122,8 @@ class MiniCssExtractPlugin { this._needBuild = true; this.content = module.content; + this.layer = module.layer; + this.supports = module.supports; this.media = module.media; this.sourceMap = module.sourceMap; this.assets = module.assets; @@ -150,6 +158,12 @@ class MiniCssExtractPlugin { const hash = webpack.util.createHash(hashFunction); hash.update(this.content); + + if (this.layer) { + hash.update(this.layer); + } + + hash.update(this.supports || ""); hash.update(this.media || ""); hash.update(this.sourceMap || ""); @@ -169,6 +183,8 @@ class MiniCssExtractPlugin { write(this._identifier); write(this._identifierIndex); write(this.content); + write(this.layer); + write(this.supports); write(this.media); write(this.sourceMap); write(this.assets); @@ -203,16 +219,19 @@ class MiniCssExtractPlugin { const identifier = read(); const identifierIndex = read(); const content = read(); + const layer = read(); + const supports = read(); const media = read(); const sourceMap = read(); const assets = read(); const assetsInfo = read(); - const dep = new CssModule({ context: contextModule, identifier, identifierIndex, content, + layer, + supports, media, sourceMap, assets, @@ -239,7 +258,7 @@ class MiniCssExtractPlugin { // eslint-disable-next-line no-shadow class CssDependency extends webpack.Dependency { constructor( - { identifier, content, media, sourceMap }, + { identifier, content, layer, supports, media, sourceMap }, context, identifierIndex ) { @@ -248,6 +267,8 @@ class MiniCssExtractPlugin { this.identifier = identifier; this.identifierIndex = identifierIndex; this.content = content; + this.layer = layer; + this.supports = supports; this.media = media; this.sourceMap = sourceMap; this.context = context; @@ -271,6 +292,8 @@ class MiniCssExtractPlugin { write(this.identifier); write(this.content); + write(this.layer); + write(this.supports); write(this.media); write(this.sourceMap); write(this.context); @@ -302,6 +325,8 @@ class MiniCssExtractPlugin { { identifier: read(), content: read(), + layer: read(), + supports: read(), media: read(), sourceMap: read(), }, @@ -1024,10 +1049,22 @@ class MiniCssExtractPlugin { source.add(header); } + if (module.supports) { + source.add(`@supports (${module.supports}) {\n`); + } + if (module.media) { source.add(`@media ${module.media} {\n`); } + const needLayer = typeof module.layer !== "undefined"; + + if (needLayer) { + source.add( + `@layer${module.layer.length > 0 ? ` ${module.layer}` : ""} {\n` + ); + } + const { path: filename } = compilation.getPathWithInfo( filenameTemplate, pathData @@ -1056,9 +1093,17 @@ class MiniCssExtractPlugin { source.add("\n"); + if (needLayer) { + source.add("}\n"); + } + if (module.media) { source.add("}\n"); } + + if (module.supports) { + source.add("}\n"); + } } } diff --git a/src/loader.js b/src/loader.js index a8be70c6..d509562f 100644 --- a/src/loader.js +++ b/src/loader.js @@ -119,29 +119,33 @@ export function pitch(request) { if (!Array.isArray(exports)) { dependencies = [[null, exports]]; } else { - dependencies = exports.map(([id, content, media, sourceMap]) => { - let identifier = id; - let context; - if (compilation) { - const module = findModuleById(compilation, id); - identifier = module.identifier(); - ({ context } = module); - } else { - // TODO check if this context is used somewhere - context = this.rootContext; - } + dependencies = exports.map( + ([id, content, media, sourceMap, supports, layer]) => { + let identifier = id; + let context; + if (compilation) { + const module = findModuleById(compilation, id); + identifier = module.identifier(); + ({ context } = module); + } else { + // TODO check if this context is used somewhere + context = this.rootContext; + } - return { - identifier, - context, - content: Buffer.from(content), - media, - sourceMap: sourceMap - ? Buffer.from(JSON.stringify(sourceMap)) - : // eslint-disable-next-line no-undefined - undefined, - }; - }); + return { + identifier, + context, + content: Buffer.from(content), + media, + supports, + layer, + sourceMap: sourceMap + ? Buffer.from(JSON.stringify(sourceMap)) + : // eslint-disable-next-line no-undefined + undefined, + }; + } + ); } addDependencies(dependencies); diff --git a/test/TestCache.test.js b/test/TestCache.test.js index 2ad3c3fd..177dd007 100644 --- a/test/TestCache.test.js +++ b/test/TestCache.test.js @@ -309,6 +309,111 @@ describe("TestCache", () => { }); }); + it('should work with the "filesystem" cache #2', async () => { + const casesDirectory = path.resolve(__dirname, "cases"); + const directoryForCase = path.resolve(casesDirectory, "at-import-layer"); + // eslint-disable-next-line import/no-dynamic-require, global-require + const webpackConfig = require(path.resolve( + directoryForCase, + "webpack.config.js" + )); + const outputPath = path.resolve(__dirname, "js/cache-filesystem-1"); + const fileSystemCacheDirectory = path.resolve( + __dirname, + "./js/.cache/type-filesystem-1" + ); + + await del([outputPath, fileSystemCacheDirectory]); + + const compiler1 = webpack({ + ...webpackConfig, + mode: "development", + context: directoryForCase, + cache: { + type: "filesystem", + cacheDirectory: fileSystemCacheDirectory, + idleTimeout: 0, + idleTimeoutForInitialStore: 0, + }, + output: { + path: outputPath, + }, + }); + + await new Promise((resolve, reject) => { + compiler1.run((error, stats) => { + if (error) { + reject(error); + + return; + } + + compiler1.close(() => { + expect(Object.keys(stats.compilation.assets).sort()) + .toMatchInlineSnapshot(` + Array [ + "main.css", + "main.js", + ] + `); + expect(Array.from(stats.compilation.emittedAssets).sort()) + .toMatchInlineSnapshot(` + Array [ + "main.css", + "main.js", + ] + `); + expect(stats.compilation.warnings).toHaveLength(0); + expect(stats.compilation.errors).toHaveLength(0); + + resolve(); + }); + }); + }); + + const compiler2 = webpack({ + ...webpackConfig, + mode: "development", + context: directoryForCase, + cache: { + type: "filesystem", + cacheDirectory: fileSystemCacheDirectory, + idleTimeout: 0, + idleTimeoutForInitialStore: 0, + }, + output: { + path: outputPath, + }, + }); + + await new Promise((resolve, reject) => { + compiler2.run((error, stats) => { + if (error) { + reject(error); + + return; + } + + compiler2.close(() => { + expect(Object.keys(stats.compilation.assets).sort()) + .toMatchInlineSnapshot(` + Array [ + "main.css", + "main.js", + ] + `); + expect( + Array.from(stats.compilation.emittedAssets).sort() + ).toMatchInlineSnapshot(`Array []`); + expect(stats.compilation.warnings).toHaveLength(0); + expect(stats.compilation.errors).toHaveLength(0); + + resolve(); + }); + }); + }); + }); + it('should work with the "filesystem" cache and asset modules', async () => { const casesDirectory = path.resolve(__dirname, "cases"); const directoryForCase = path.resolve(casesDirectory, "asset-modules"); diff --git a/test/cases/at-import-layer/expected/main.css b/test/cases/at-import-layer/expected/main.css new file mode 100644 index 00000000..3e7d1c11 --- /dev/null +++ b/test/cases/at-import-layer/expected/main.css @@ -0,0 +1,48 @@ +@layer framework { +@layer base {.bottom { + background: blue; +} +} +} +@layer framework { +@layer base { + .middle { + background: green; + } +} + +} +@layer { +.color { + color: red; +} + +} +@supports (display: flex) { +@media (prefers-color-scheme: dark) { +@layer default { +@supports (display: grid) {@media screen and (min-width: 900px) {@layer base {.foo { + color: red; +} +}}} +} +} +} +@supports (display: flex) { +@media (prefers-color-scheme: dark) { +@layer default { +/* prettier-ignore */ + +.bar { + color: blue; +} + +} +} +} +/* prettier-ignore */ + +.top { + background: red; +} + diff --git a/test/cases/at-import-layer/import-with-layer-and-supports-and-media.css b/test/cases/at-import-layer/import-with-layer-and-supports-and-media.css new file mode 100644 index 00000000..bafd2b49 --- /dev/null +++ b/test/cases/at-import-layer/import-with-layer-and-supports-and-media.css @@ -0,0 +1,6 @@ +/* prettier-ignore */ +@import url("./zzz.css") layer(base) supports(display: grid) screen and (min-width: 900px); + +.bar { + color: blue; +} diff --git a/test/cases/at-import-layer/import-with-layer.css b/test/cases/at-import-layer/import-with-layer.css new file mode 100644 index 00000000..171269b8 --- /dev/null +++ b/test/cases/at-import-layer/import-with-layer.css @@ -0,0 +1,7 @@ +@import url("./test.css") layer(base); + +@layer base { + .middle { + background: green; + } +} diff --git a/test/cases/at-import-layer/index.js b/test/cases/at-import-layer/index.js new file mode 100644 index 00000000..4fe51c72 --- /dev/null +++ b/test/cases/at-import-layer/index.js @@ -0,0 +1 @@ +import "./style.css"; diff --git a/test/cases/at-import-layer/style.css b/test/cases/at-import-layer/style.css new file mode 100644 index 00000000..f2579baa --- /dev/null +++ b/test/cases/at-import-layer/style.css @@ -0,0 +1,8 @@ +@import url("./import-with-layer.css") layer(framework); +@import url("./unnamed-layer.css") layer; +/* prettier-ignore */ +@import url("./import-with-layer-and-supports-and-media.css") layer(default) supports(display: flex) (prefers-color-scheme: dark); + +.top { + background: red; +} diff --git a/test/cases/at-import-layer/test.css b/test/cases/at-import-layer/test.css new file mode 100644 index 00000000..b704b96d --- /dev/null +++ b/test/cases/at-import-layer/test.css @@ -0,0 +1,3 @@ +.bottom { + background: blue; +} diff --git a/test/cases/at-import-layer/unnamed-layer.css b/test/cases/at-import-layer/unnamed-layer.css new file mode 100644 index 00000000..d005c9f3 --- /dev/null +++ b/test/cases/at-import-layer/unnamed-layer.css @@ -0,0 +1,3 @@ +.color { + color: red; +} diff --git a/test/cases/at-import-layer/webpack.config.js b/test/cases/at-import-layer/webpack.config.js new file mode 100644 index 00000000..cf55c08c --- /dev/null +++ b/test/cases/at-import-layer/webpack.config.js @@ -0,0 +1,18 @@ +import Self from "../../../src"; + +module.exports = { + entry: "./index.js", + module: { + rules: [ + { + test: /\.css$/, + use: [Self.loader, "css-loader"], + }, + ], + }, + plugins: [ + new Self({ + filename: "[name].css", + }), + ], +}; diff --git a/test/cases/at-import-layer/zzz.css b/test/cases/at-import-layer/zzz.css new file mode 100644 index 00000000..a15c877a --- /dev/null +++ b/test/cases/at-import-layer/zzz.css @@ -0,0 +1,3 @@ +.foo { + color: red; +} diff --git a/test/cases/at-import-supports/expected/main.css b/test/cases/at-import-supports/expected/main.css new file mode 100644 index 00000000..49fa513e --- /dev/null +++ b/test/cases/at-import-supports/expected/main.css @@ -0,0 +1,16 @@ +@supports (display: flex) { +@supports (display: grid) {.bottom { + background: blue; +} +} +} +@supports (display: flex) { +.middle { + background: green; +} + +} +.top { + background: red; +} + diff --git a/test/cases/at-import-supports/import-with-supports.css b/test/cases/at-import-supports/import-with-supports.css new file mode 100644 index 00000000..43479c3d --- /dev/null +++ b/test/cases/at-import-supports/import-with-supports.css @@ -0,0 +1,5 @@ +@import url("./test.css") supports(display: grid); + +.middle { + background: green; +} diff --git a/test/cases/at-import-supports/index.js b/test/cases/at-import-supports/index.js new file mode 100644 index 00000000..4fe51c72 --- /dev/null +++ b/test/cases/at-import-supports/index.js @@ -0,0 +1 @@ +import "./style.css"; diff --git a/test/cases/at-import-supports/style.css b/test/cases/at-import-supports/style.css new file mode 100644 index 00000000..e8300c20 --- /dev/null +++ b/test/cases/at-import-supports/style.css @@ -0,0 +1,5 @@ +@import url("./import-with-supports.css") supports(display: flex); + +.top { + background: red; +} diff --git a/test/cases/at-import-supports/test.css b/test/cases/at-import-supports/test.css new file mode 100644 index 00000000..b704b96d --- /dev/null +++ b/test/cases/at-import-supports/test.css @@ -0,0 +1,3 @@ +.bottom { + background: blue; +} diff --git a/test/cases/at-import-supports/webpack.config.js b/test/cases/at-import-supports/webpack.config.js new file mode 100644 index 00000000..cf55c08c --- /dev/null +++ b/test/cases/at-import-supports/webpack.config.js @@ -0,0 +1,18 @@ +import Self from "../../../src"; + +module.exports = { + entry: "./index.js", + module: { + rules: [ + { + test: /\.css$/, + use: [Self.loader, "css-loader"], + }, + ], + }, + plugins: [ + new Self({ + filename: "[name].css", + }), + ], +};