From 126e9bbc7a8285b269ca1c1b2b16c0a9aec79c74 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 19 Sep 2022 21:07:41 -0700 Subject: [PATCH 1/4] @parcel/css -> lightningcss --- packages/core/integration-tests/test/css.js | 2 +- packages/optimizers/css/package.json | 2 +- packages/optimizers/css/src/CSSOptimizer.js | 6 +- packages/transformers/css/package.json | 2 +- .../transformers/css/src/CSSTransformer.js | 2 +- yarn.lock | 112 +++++++++--------- 6 files changed, 63 insertions(+), 63 deletions(-) diff --git a/packages/core/integration-tests/test/css.js b/packages/core/integration-tests/test/css.js index 670488b50d47..81a1cadb06a6 100644 --- a/packages/core/integration-tests/test/css.js +++ b/packages/core/integration-tests/test/css.js @@ -479,7 +479,7 @@ describe('css', () => { ); }); - it('should support css nesting with @parcel/css', async function () { + it('should support css nesting with lightningcss', async function () { let b = await bundle( path.join(__dirname, '/integration/css-nesting/a.css'), { diff --git a/packages/optimizers/css/package.json b/packages/optimizers/css/package.json index cb3a96f2c773..baf981e75492 100644 --- a/packages/optimizers/css/package.json +++ b/packages/optimizers/css/package.json @@ -20,12 +20,12 @@ "parcel": "^2.7.0" }, "dependencies": { - "@parcel/css": "^1.12.2", "@parcel/diagnostic": "2.7.0", "@parcel/plugin": "2.7.0", "@parcel/source-map": "^2.0.0", "@parcel/utils": "2.7.0", "browserslist": "^4.6.6", + "lightningcss": "^1.16.0", "nullthrows": "^1.1.1" } } diff --git a/packages/optimizers/css/src/CSSOptimizer.js b/packages/optimizers/css/src/CSSOptimizer.js index 72f5997ae267..c24b603b9feb 100644 --- a/packages/optimizers/css/src/CSSOptimizer.js +++ b/packages/optimizers/css/src/CSSOptimizer.js @@ -6,7 +6,7 @@ import { transform, transformStyleAttribute, browserslistToTargets, -} from '@parcel/css'; +} from 'lightningcss'; import {blobToBuffer} from '@parcel/utils'; import browserslist from 'browserslist'; import nullthrows from 'nullthrows'; @@ -32,7 +32,7 @@ export default (new Optimizer({ let message; if (filename === 'package.json') { message = md` -Parcel\'s default CSS minifer changed from cssnano to @parcel/css, but a "cssnano" key was found in **package.json**. Either remove this configuration, or configure Parcel to use @parcel/optimizer-cssnano instead. +Parcel\'s default CSS minifer changed from cssnano to lightningcss, but a "cssnano" key was found in **package.json**. Either remove this configuration, or configure Parcel to use @parcel/optimizer-cssnano instead. `; let contents = await options.inputFS.readFile( configFile.filePath, @@ -42,7 +42,7 @@ Parcel\'s default CSS minifer changed from cssnano to @parcel/css, but a "cssnan {key: '/cssnano', type: 'key'}, ]); } else { - message = md`Parcel\'s default CSS minifer changed from cssnano to @parcel/css, but a __${filename}__ config file was found. Either remove this config file, or configure Parcel to use @parcel/optimizer-cssnano instead.`; + message = md`Parcel\'s default CSS minifer changed from cssnano to lightningcss, but a __${filename}__ config file was found. Either remove this config file, or configure Parcel to use @parcel/optimizer-cssnano instead.`; codeHighlights = [ { start: {line: 1, column: 1}, diff --git a/packages/transformers/css/package.json b/packages/transformers/css/package.json index 7009b2876fd8..a82b67e7e142 100644 --- a/packages/transformers/css/package.json +++ b/packages/transformers/css/package.json @@ -20,12 +20,12 @@ "parcel": "^2.7.0" }, "dependencies": { - "@parcel/css": "^1.12.2", "@parcel/diagnostic": "2.7.0", "@parcel/plugin": "2.7.0", "@parcel/source-map": "^2.0.0", "@parcel/utils": "2.7.0", "browserslist": "^4.6.6", + "lightningcss": "^1.16.0", "nullthrows": "^1.1.1" } } diff --git a/packages/transformers/css/src/CSSTransformer.js b/packages/transformers/css/src/CSSTransformer.js index 2d4b25508125..98c35241e966 100644 --- a/packages/transformers/css/src/CSSTransformer.js +++ b/packages/transformers/css/src/CSSTransformer.js @@ -7,7 +7,7 @@ import { transform, transformStyleAttribute, browserslistToTargets, -} from '@parcel/css'; +} from 'lightningcss'; import {remapSourceLocation, relativePath} from '@parcel/utils'; import browserslist from 'browserslist'; import nullthrows from 'nullthrows'; diff --git a/yarn.lock b/yarn.lock index f51dfe768450..3e68d43745e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2171,62 +2171,6 @@ dependencies: chalk "^4.1.0" -"@parcel/css-darwin-arm64@1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css-darwin-arm64/-/css-darwin-arm64-1.12.2.tgz#4215585dac699f0f75015f5b47254867ac1221d3" - integrity sha512-6VvsoYSltBiUh/uyfPzQ+I3DiTFN7tmRv6zm1LH98J7GGCDDhbYEtbQjjCs15ex6fVn1ORZK0JO+mMlsg1JwTA== - -"@parcel/css-darwin-x64@1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css-darwin-x64/-/css-darwin-x64-1.12.2.tgz#eeb4e04c512580bd531b5ffa9c34456e9799fdb9" - integrity sha512-3J0/LrDvt5vevOisnrE0q5mEcuiAY+K7OZwIv84SAnrbjlL5sshmIaaNzL869kb4thza+RClEj0mS5XTm1IUEw== - -"@parcel/css-linux-arm-gnueabihf@1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm-gnueabihf/-/css-linux-arm-gnueabihf-1.12.2.tgz#ccd813bbc9b9d845fb8f6ed9c7c22c745cda007b" - integrity sha512-OsX7I3dhBvnxEbAH++08RFe7yhjRp33ulzrCvJTMOP9YkxEEJ8qId3sNzJBHIVQzHyTlPTnBRHbSDhU3TFe/eQ== - -"@parcel/css-linux-arm64-gnu@1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm64-gnu/-/css-linux-arm64-gnu-1.12.2.tgz#7959fbcbd38c9b9c2c24c6c2def4ec2df370b705" - integrity sha512-R1Kqw+1Rsru9Q4+qvUEC6B8P21bpqhuF9rv8GmBmmnF1i2hMZ1JiY+uh/ej8IaRV0O3fAHeQGIyGBWx6qWDpcw== - -"@parcel/css-linux-arm64-musl@1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-arm64-musl/-/css-linux-arm64-musl-1.12.2.tgz#ffc3fc62db9b8a19f8be61028abbcb7c44d90fa6" - integrity sha512-nwixgM4SEgPUQata9aAiJW0A5Q9ms+xim1tXT1i+91kOei4Fu2Wr2OuofMk+mlhbgmGKCTcu4gzMPReGxUhuRA== - -"@parcel/css-linux-x64-gnu@1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-x64-gnu/-/css-linux-x64-gnu-1.12.2.tgz#15619756ba62558243ae996e257b1cca90f534eb" - integrity sha512-cJYVMHnQSGhDwQByyvjFZppjMBNlgxXl/R4cX5DwrQE0QZmK/42BYnMp92rvoprEG6LRyRoiGtCjyfYTPWajog== - -"@parcel/css-linux-x64-musl@1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css-linux-x64-musl/-/css-linux-x64-musl-1.12.2.tgz#de61e2bdec54609f7b681acfbd04e9fb57a5ef02" - integrity sha512-u9zdO/d831/74Tf+TdPUfaIuB9v6FD4Xz8UdWUDOXgQqaOlnJ9fAsAM39EkoWlMxPPljY3f4ay6irSe1a4XgSA== - -"@parcel/css-win32-x64-msvc@1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css-win32-x64-msvc/-/css-win32-x64-msvc-1.12.2.tgz#086586fce31d1e05340c2e31efc32d40aa9ee05a" - integrity sha512-kCAKr3vKqvPUv9oXBG3pGZQz5il3sEk35dpmTXFa/7eDNKR5XyLpiJs8JwWJTFfuUqroymDSXA1bCcjvNEYcAg== - -"@parcel/css@^1.12.2": - version "1.12.2" - resolved "https://registry.yarnpkg.com/@parcel/css/-/css-1.12.2.tgz#63eacc9fcdf58e4d9639db34271834394705b7b2" - integrity sha512-Sa0PvZu5u877CupQA8IjEATqjJFynBfA7LxbcyutFe2LDCRSqB5Bm08jKFScyaz56qjZNIxZxXk2SApNkOvoAA== - dependencies: - detect-libc "^1.0.3" - optionalDependencies: - "@parcel/css-darwin-arm64" "1.12.2" - "@parcel/css-darwin-x64" "1.12.2" - "@parcel/css-linux-arm-gnueabihf" "1.12.2" - "@parcel/css-linux-arm64-gnu" "1.12.2" - "@parcel/css-linux-arm64-musl" "1.12.2" - "@parcel/css-linux-x64-gnu" "1.12.2" - "@parcel/css-linux-x64-musl" "1.12.2" - "@parcel/css-win32-x64-msvc" "1.12.2" - "@parcel/diagnostic@2.5.0": version "2.5.0" resolved "https://packages.atlassian.com/api/npm/npm-remote/@parcel/diagnostic/-/diagnostic-2.5.0.tgz#8c6891924e04b625d50176aae141d24dc8dddf87" @@ -8578,6 +8522,62 @@ liftoff@^3.1.0: rechoir "^0.6.2" resolve "^1.1.7" +lightningcss-darwin-arm64@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.16.0.tgz#f3318a2e64ca160610977675ee1a7e611f4a3617" + integrity sha512-gIhz6eZFwsC4oVMjBGQ3QWDdLQY7vcXFyM/x91PilgHqu63B9uBa10EZA75YoTEkbKhoz0uDCqyHh/EoF1GrkQ== + +lightningcss-darwin-x64@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.16.0.tgz#46361b701b572ce9ec29730624000b439c2184bb" + integrity sha512-kLPi+OEpDj3UGY6DC8TfjbcULJDKMP+TVKSlrEkNGn8t1YRzi2g4oy7UVTSB5AnSbT0CusUItzdVjHQ49EdoNA== + +lightningcss-linux-arm-gnueabihf@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.16.0.tgz#5da29f465dd44d98434b00b424cc4b445ea49eff" + integrity sha512-oSwEbvXUPr//H/ainBRJXTxHerlheee/KgkTTmAQWiVnt8HV+bRohTBWWPBy5ZArgiGLwj7ogv45istgljPN2Q== + +lightningcss-linux-arm64-gnu@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.16.0.tgz#21d139f5201c9b8bb3972c24116a5bcbb7e2c3b5" + integrity sha512-Drq9BSVIvmV9zsDJbCZWCulMvKMQWFIlYXPCKV/iwRj+ZAJ1BRngma0cNHB6uW7Wac8Jg04CJN5IA4ELE3J+cQ== + +lightningcss-linux-arm64-musl@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.16.0.tgz#d06936e0570fb51d58cc18ef2fb8858aeecd1979" + integrity sha512-1QXWStnTEo4RFQf0mfGhRyNUeEHilCZ0NA97XgwKwrYr/M7sYKU/1HWY00dPxFJ6GITR2pfJGo9xi3ScSSBxbA== + +lightningcss-linux-x64-gnu@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.16.0.tgz#fd9881cd839cac4676a01b713b06b0b178743f87" + integrity sha512-gD2eQYD5OFs1p83R0TcMCEc5HRyJES4lR4THmclv7khm3dc9vc+2VT0kFBPxO1L2AwlZuvXaaMan7X1Ul7uSfA== + +lightningcss-linux-x64-musl@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.16.0.tgz#697d87448e0f6445b6fc3cf8529d89709f3bfacc" + integrity sha512-HJsKeYxloEvg2WCQhtYPqzZUliLu9JBJNeI5y9cPQeDR/7ayGGLbVhJaotPtzJkElOFL/SaXsS+FRuH4w+yafg== + +lightningcss-win32-x64-msvc@1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.16.0.tgz#e4a09c12a48534121ecd03ef0be8dadbbbbb63b6" + integrity sha512-h4ayyAlOMLUHV9NdofcIu79aEjmly93adVxcg5wDJpkvMiwDTufEN30M8G4gGcjo1JE5jFjAcyQcRpXYkYcemA== + +lightningcss@^1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/lightningcss/-/lightningcss-1.16.0.tgz#4c8e4ef8133d54488d1482a115d3758259191c52" + integrity sha512-5+ZS9h+xeADcJTF2oRCT3yNZBlDYyOgQSdrWNBCqsIwm8ucKbF061OBVv/WHP4Zk8FToNhwFklk/hMuOngqsIg== + dependencies: + detect-libc "^1.0.3" + optionalDependencies: + lightningcss-darwin-arm64 "1.16.0" + lightningcss-darwin-x64 "1.16.0" + lightningcss-linux-arm-gnueabihf "1.16.0" + lightningcss-linux-arm64-gnu "1.16.0" + lightningcss-linux-arm64-musl "1.16.0" + lightningcss-linux-x64-gnu "1.16.0" + lightningcss-linux-x64-musl "1.16.0" + lightningcss-win32-x64-msvc "1.16.0" + lilconfig@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd" From 0504e8bd587894b7bcff614bfae3e9bf12273349 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 19 Sep 2022 21:08:22 -0700 Subject: [PATCH 2/4] Use lightningcss to implement CSS packager --- packages/core/integration-tests/test/css.js | 17 +- .../test/integration/css-layer/a.css | 5 + .../test/integration/css-layer/b.css | 5 + .../test/integration/css-layer/c.css | 3 + .../core/integration-tests/test/sourcemaps.js | 3 +- packages/packagers/css/package.json | 1 + packages/packagers/css/src/CSSPackager.js | 195 +++++++++++------- .../transformers/css/src/CSSTransformer.js | 9 +- 8 files changed, 164 insertions(+), 74 deletions(-) create mode 100644 packages/core/integration-tests/test/integration/css-layer/a.css create mode 100644 packages/core/integration-tests/test/integration/css-layer/b.css create mode 100644 packages/core/integration-tests/test/integration/css-layer/c.css diff --git a/packages/core/integration-tests/test/css.js b/packages/core/integration-tests/test/css.js index 81a1cadb06a6..89b5069f2d84 100644 --- a/packages/core/integration-tests/test/css.js +++ b/packages/core/integration-tests/test/css.js @@ -114,7 +114,9 @@ describe('css', () => { let css = await outputFS.readFile(path.join(distDir, '/index.css'), 'utf8'); assert(css.includes('.local')); assert(css.includes('.other')); - assert(/@media print {\s*.other/.test(css)); + assert( + /@media print {\s*\.local(.|\n)*\.other(.|\n)*}(.|\n)*\.index/.test(css), + ); assert(css.includes('.index')); }); @@ -492,4 +494,17 @@ describe('css', () => { let res = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); assert(res.includes('.foo.bar')); }); + + it('should support @layer', async function () { + let b = await bundle(path.join(__dirname, '/integration/css-layer/a.css'), { + mode: 'production', + }); + + let res = await outputFS.readFile(b.getBundles()[0].filePath, 'utf8'); + assert( + res.includes( + '@layer b.c{.c{color:#ff0}}@layer b{.b{color:#00f}}.a{color:red}', + ), + ); + }); }); diff --git a/packages/core/integration-tests/test/integration/css-layer/a.css b/packages/core/integration-tests/test/integration/css-layer/a.css new file mode 100644 index 000000000000..05751a414e4b --- /dev/null +++ b/packages/core/integration-tests/test/integration/css-layer/a.css @@ -0,0 +1,5 @@ +@import "b.css" layer(b); + +.a { + color: red; +} diff --git a/packages/core/integration-tests/test/integration/css-layer/b.css b/packages/core/integration-tests/test/integration/css-layer/b.css new file mode 100644 index 000000000000..8210a4583ffa --- /dev/null +++ b/packages/core/integration-tests/test/integration/css-layer/b.css @@ -0,0 +1,5 @@ +@import "c.css" layer(c); + +.b { + color: blue; +} diff --git a/packages/core/integration-tests/test/integration/css-layer/c.css b/packages/core/integration-tests/test/integration/css-layer/c.css new file mode 100644 index 000000000000..1d152c0c2765 --- /dev/null +++ b/packages/core/integration-tests/test/integration/css-layer/c.css @@ -0,0 +1,3 @@ +.c { + color: yellow; +} diff --git a/packages/core/integration-tests/test/sourcemaps.js b/packages/core/integration-tests/test/sourcemaps.js index 42de11f19ca6..68a593ba24d0 100644 --- a/packages/core/integration-tests/test/sourcemaps.js +++ b/packages/core/integration-tests/test/sourcemaps.js @@ -828,12 +828,11 @@ describe('sourcemaps', function () { // This should actually just be `./integration/scss-sourcemap-imports/with_url.scss` // but this is a small bug in the extend utility of the source-map library assert.deepEqual(mapData.sources, [ - 'integration/scss-sourcemap-imports/style.scss', 'integration/scss-sourcemap-imports/with_url.scss', ]); let input = await inputFS.readFile( - path.join(path.dirname(filename), map.sourceRoot, map.sources[1]), + path.join(path.dirname(filename), map.sourceRoot, map.sources[0]), 'utf-8', ); diff --git a/packages/packagers/css/package.json b/packages/packagers/css/package.json index 42ad80ff8c95..74ccb887fc51 100644 --- a/packages/packagers/css/package.json +++ b/packages/packagers/css/package.json @@ -23,6 +23,7 @@ "@parcel/plugin": "2.7.0", "@parcel/source-map": "^2.0.0", "@parcel/utils": "2.7.0", + "lightningcss": "^1.16.0", "nullthrows": "^1.1.1" }, "devDependencies": { diff --git a/packages/packagers/css/src/CSSPackager.js b/packages/packagers/css/src/CSSPackager.js index c602e9044bf5..5babbe1969dc 100644 --- a/packages/packagers/css/src/CSSPackager.js +++ b/packages/packagers/css/src/CSSPackager.js @@ -3,19 +3,18 @@ import type {Root} from 'postcss'; import type {Asset, Dependency} from '@parcel/types'; import typeof PostCSS from 'postcss'; +import {bundleAsync} from 'lightningcss'; -import path from 'path'; +import invariant from 'assert'; +import nullthrows from 'nullthrows'; import SourceMap from '@parcel/source-map'; import {Packager} from '@parcel/plugin'; import { PromiseQueue, - countLines, replaceInlineReferences, replaceURLReferences, } from '@parcel/utils'; -import nullthrows from 'nullthrows'; - export default (new Packager({ async package({ bundle, @@ -25,37 +24,44 @@ export default (new Packager({ logger, options, }) { + // Inline style attributes are parsed differently from full CSS files. + if (bundle.bundleBehavior === 'inline') { + let entry = bundle.getMainEntry(); + if (entry?.meta.type === 'attr') { + return replaceReferences( + bundle, + bundleGraph, + await entry.getCode(), + await entry.getMap(), + getInlineBundleContents, + ); + } + } + let queue = new PromiseQueue({ maxConcurrent: 32, }); let hoistedImports = []; + let assetsByPlaceholder = new Map(); + bundle.traverse({ exit: node => { if (node.type === 'dependency') { + let resolved = bundleGraph.getResolvedAsset(node.value, bundle); + // Hoist unresolved external dependencies (i.e. http: imports) - if ( - node.value.priority === 'sync' && - !bundleGraph.getResolvedAsset(node.value, bundle) - ) { + if (node.value.priority === 'sync' && !resolved) { hoistedImports.push(node.value.specifier); } - return; - } - let asset = node.value; - - // Figure out which media types this asset was imported with. - // We only want to import the asset once, so group them all together. - let media = []; - for (let dep of bundleGraph.getIncomingDependencies(asset)) { - if (!dep.meta.media) { - // Asset was imported without a media type. Don't wrap in @media. - media.length = 0; - break; + if (resolved && bundle.hasAsset(resolved)) { + assetsByPlaceholder.set(node.value.meta.placeholder, resolved); } - media.push(dep.meta.media); + + return; } + let asset = node.value; queue.add(() => { if ( !asset.symbols.isCleared && @@ -69,7 +75,6 @@ export default (new Packager({ bundleGraph, bundle, asset, - media, ); } else { return Promise.all([ @@ -104,77 +109,132 @@ export default (new Packager({ } } - if (media.length) { - return `@media ${media.join(', ')} {\n${css}\n}\n`; - } - return css; }), - bundle.env.sourceMap && asset.getMapBuffer(), + bundle.env.sourceMap ? asset.getMap() : null, ]); } }); }, }); - let outputs = await queue.run(); + let outputs = new Map( + (await queue.run()).map(([asset, code, map]) => [asset, [code, map]]), + ); let contents = ''; let map = new SourceMap(options.projectRoot); - let lineOffset = 0; - - for (let url of hoistedImports) { - contents += `@import "${url}";\n`; - lineOffset++; - } - for (let [asset, code, mapBuffer] of outputs) { - contents += code + '\n'; - if (bundle.env.sourceMap) { - if (mapBuffer) { - map.addBuffer(mapBuffer, lineOffset); + // Collect entry assets. + let entry = null; + bundle.traverse((node, _, actions) => { + if (node.type === 'asset') { + // If there is only one entry, we'll use it directly. + // Otherwise, we'll create a fake bundle entry with @import rules for each root asset. + if (entry == null) { + entry = node.value.id; } else { - map.addEmptyMap( - path - .relative(options.projectRoot, asset.filePath) - .replace(/\\+/g, '/'), - code, - lineOffset, - ); + entry = bundle.id; } - lineOffset += countLines(code); + assetsByPlaceholder.set(node.value.id, node.value); + contents += `@import "${node.value.id}";\n`; + actions.skipChildren(); } - } + }); + + // $FlowFixMe - TODO: something is broken in types, fix in next release of lightningcss. + let res = await bundleAsync({ + filename: nullthrows(entry), + sourceMap: !!bundle.env.sourceMap, + resolver: { + resolve(specifier) { + return specifier; + }, + async read(file) { + if (file === bundle.id) { + return contents; + } + + let asset = assetsByPlaceholder.get(file); + if (!asset) { + return ''; + } + let [code, map] = nullthrows(outputs.get(asset)); + if (map) { + let sm = await map.stringify({format: 'inline'}); + invariant(typeof sm === 'string'); + code += `\n/*# sourceMappingURL=${sm} */`; + } + return code; + }, + }, + }); - if (bundle.env.sourceMap) { + contents = res.code.toString(); + + if (res.map) { + let vlqMap = JSON.parse(res.map.toString()); + map.addVLQMap(vlqMap); let reference = await getSourceMapReference(map); if (reference != null) { contents += '/*# sourceMappingURL=' + reference + ' */\n'; } } - ({contents, map} = replaceURLReferences({ - bundle, - bundleGraph, - contents, - map, - getReplacement: escapeString, - })); + // Prepend hoisted external imports. + if (hoistedImports.length > 0) { + let lineOffset = 0; + let hoistedCode = ''; + for (let url of hoistedImports) { + hoistedCode += `@import "${url}";\n`; + lineOffset++; + } - return replaceInlineReferences({ + if (bundle.env.sourceMap) { + map.offsetLines(1, lineOffset); + } + + contents = hoistedCode + contents; + } + + return replaceReferences( bundle, bundleGraph, contents, - getInlineBundleContents, - getInlineReplacement: (dep, inlineType, contents) => ({ - from: getSpecifier(dep), - to: escapeString(contents), - }), map, - }); + getInlineBundleContents, + ); }, }): Packager); +function replaceReferences( + bundle, + bundleGraph, + contents, + map, + getInlineBundleContents, +) { + ({contents, map} = replaceURLReferences({ + bundle, + bundleGraph, + contents, + map, + getReplacement: escapeString, + })); + + return replaceInlineReferences({ + bundle, + bundleGraph, + contents, + getInlineBundleContents, + getInlineReplacement: (dep, inlineType, contents) => ({ + from: getSpecifier(dep), + to: escapeString(contents), + }), + map, + }); +} + export function getSpecifier(dep: Dependency): string { if (typeof dep.meta.placeholder === 'string') { return dep.meta.placeholder; @@ -193,8 +253,7 @@ async function processCSSModule( bundleGraph, bundle, asset, - media, -): Promise<[Asset, string, ?Buffer]> { +): Promise<[Asset, string, ?SourceMap]> { let postcss: PostCSS = await options.packageManager.require( 'postcss', options.projectRoot + '/index', @@ -274,11 +333,7 @@ async function processCSSModule( sourceMap.addVLQMap(map.toJSON()); } - if (media.length) { - content = `@media ${media.join(', ')} {\n${content}\n}\n`; - } - - return [asset, content, sourceMap?.toBuffer()]; + return [asset, content, sourceMap]; } function escapeDashedIdent(name) { diff --git a/packages/transformers/css/src/CSSTransformer.js b/packages/transformers/css/src/CSSTransformer.js index 98c35241e966..cf88c660dea8 100644 --- a/packages/transformers/css/src/CSSTransformer.js +++ b/packages/transformers/css/src/CSSTransformer.js @@ -74,7 +74,12 @@ export default (new Transformer({ filename: path.relative(options.projectRoot, asset.filePath), code, cssModules, - analyzeDependencies: asset.meta.hasDependencies !== false, + analyzeDependencies: + asset.meta.hasDependencies !== false + ? { + preserveImports: true, + } + : false, sourceMap: !!asset.env.sourceMap, drafts: config?.drafts, pseudoClasses: config?.pseudoClasses, @@ -160,6 +165,8 @@ export default (new Transformer({ // For the glob resolver to distinguish between `@import` and other URL dependencies. isCSSImport: true, media: dep.media, + // $FlowFixMe - TODO fix in lightningcss. + placeholder: dep.placeholder, }, }); } else if (dep.type === 'url') { From 1e32ddf56e05a6b12d8ed58a1e11faffcf43c3cd Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Mon, 1 Jan 2024 00:47:11 -0500 Subject: [PATCH 3/4] New approach to css ordering issue --- packages/packagers/css/src/CSSPackager.js | 46 +++++++++---------- .../transformers/css/src/CSSTransformer.js | 13 ++++-- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/packages/packagers/css/src/CSSPackager.js b/packages/packagers/css/src/CSSPackager.js index 7c97f5dfe9c8..36c714f1eb84 100644 --- a/packages/packagers/css/src/CSSPackager.js +++ b/packages/packagers/css/src/CSSPackager.js @@ -44,8 +44,25 @@ export default (new Packager({ }); let hoistedImports = []; let assetsByPlaceholder = new Map(); + let entry = null; + let entryContents = ''; bundle.traverse({ + enter: (node, context) => { + if (node.type === 'asset' && !context) { + // If there is only one entry, we'll use it directly. + // Otherwise, we'll create a fake bundle entry with @import rules for each root asset. + if (entry == null) { + entry = node.value.id; + } else { + entry = bundle.id; + } + + assetsByPlaceholder.set(node.value.id, node.value); + entryContents += `@import "${node.value.id}";\n`; + } + return true; + }, exit: node => { if (node.type === 'dependency') { let resolved = bundleGraph.getResolvedAsset(node.value, bundle); @@ -60,7 +77,10 @@ export default (new Packager({ } if (resolved && bundle.hasAsset(resolved)) { - assetsByPlaceholder.set(node.value.meta.placeholder, resolved); + assetsByPlaceholder.set( + node.value.meta.placeholder ?? node.value.specifier, + resolved, + ); } return; @@ -126,28 +146,8 @@ export default (new Packager({ let outputs = new Map( (await queue.run()).map(([asset, code, map]) => [asset, [code, map]]), ); - let contents = ''; let map = new SourceMap(options.projectRoot); - // Collect entry assets. - let entry = null; - bundle.traverse((node, _, actions) => { - if (node.type === 'asset') { - // If there is only one entry, we'll use it directly. - // Otherwise, we'll create a fake bundle entry with @import rules for each root asset. - if (entry == null) { - entry = node.value.id; - } else { - entry = bundle.id; - } - - assetsByPlaceholder.set(node.value.id, node.value); - contents += `@import "${node.value.id}";\n`; - actions.skipChildren(); - } - }); - - // $FlowFixMe - TODO: something is broken in types, fix in next release of lightningcss. let res = await bundleAsync({ filename: nullthrows(entry), sourceMap: !!bundle.env.sourceMap, @@ -157,7 +157,7 @@ export default (new Packager({ }, async read(file) { if (file === bundle.id) { - return contents; + return entryContents; } let asset = assetsByPlaceholder.get(file); @@ -175,7 +175,7 @@ export default (new Packager({ }, }); - contents = res.code.toString(); + let contents = res.code.toString(); if (res.map) { let vlqMap = JSON.parse(res.map.toString()); diff --git a/packages/transformers/css/src/CSSTransformer.js b/packages/transformers/css/src/CSSTransformer.js index a11e271fe300..32ab86a13967 100644 --- a/packages/transformers/css/src/CSSTransformer.js +++ b/packages/transformers/css/src/CSSTransformer.js @@ -180,8 +180,6 @@ export default (new Transformer({ } } - asset.setBuffer(Buffer.from(res.code)); - if (res.map != null) { let vlqMap = JSON.parse(Buffer.from(res.map).toString()); let map = new SourceMap(options.projectRoot); @@ -211,7 +209,6 @@ export default (new Transformer({ // For the glob resolver to distinguish between `@import` and other URL dependencies. isCSSImport: true, media: dep.media, - // $FlowFixMe - TODO fix in lightningcss. placeholder: dep.placeholder, }, }); @@ -227,6 +224,7 @@ export default (new Transformer({ } let assets = [asset]; + let buffer = Buffer.from(res.code); if (res.exports != null) { let exports = res.exports; @@ -238,6 +236,7 @@ export default (new Transformer({ let c = 0; let depjs = ''; let js = ''; + let cssImports = ''; let jsDeps = []; @@ -280,6 +279,7 @@ export default (new Transformer({ ref.specifier, )};\n`; dependencies.set(ref.specifier, d); + cssImports += `@import "${ref.specifier}";\n`; asset.addDependency({ specifier: ref.specifier, specifierType: 'esm', @@ -341,6 +341,7 @@ export default (new Transformer({ }); asset.meta.hasReferences = true; + cssImports += `@import "${reference.specifier}";\n`; } } @@ -350,8 +351,14 @@ export default (new Transformer({ dependencies: jsDeps, env, }); + + // Prepend @import rules for each composes dependency so packager knows where to insert them. + if (cssImports.length > 0) { + buffer = Buffer.concat([Buffer.from(cssImports), buffer]); + } } + asset.setBuffer(buffer); return assets; }, }): Transformer); From 22f6be8855c28225b0ba297e9d12158136863195 Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sun, 7 Jan 2024 11:44:38 -0500 Subject: [PATCH 4/4] Add lightningcss-wasm dependency --- packages/packagers/css/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/packagers/css/package.json b/packages/packagers/css/package.json index 572eed085692..fa20a20b111d 100644 --- a/packages/packagers/css/package.json +++ b/packages/packagers/css/package.json @@ -28,6 +28,10 @@ "nullthrows": "^1.1.1" }, "devDependencies": { + "lightningcss-wasm": "^1.22.1", "postcss": "^8.4.5" + }, + "browser": { + "lightningcss": "lightningcss-wasm" } }