summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Drew Powers <1369770+drwpow@users.noreply.github.com> 2021-12-02 09:24:00 -0700
committerGravatar GitHub <noreply@github.com> 2021-12-02 09:24:00 -0700
commit62a5e98c9008a1ac88c3c38db64b74723f8fd422 (patch)
tree34f113540a82ae7d9121737612588779ff1f294a
parentf6b15c3516ac13000ebe0c54a61159bfccf3ef4e (diff)
downloadastro-62a5e98c9008a1ac88c3c38db64b74723f8fd422.tar.gz
astro-62a5e98c9008a1ac88c3c38db64b74723f8fd422.tar.zst
astro-62a5e98c9008a1ac88c3c38db64b74723f8fd422.zip
Improve CSS import ordering, fix empty CSS outputs (#2081)
Fixes #2060
-rw-r--r--.changeset/clever-suits-hide.md5
-rw-r--r--packages/astro/src/vite-plugin-build-css/index.ts2
-rw-r--r--packages/astro/src/vite-plugin-build-html/index.ts48
-rw-r--r--packages/astro/test/astro-css-bundling-import.test.js7
4 files changed, 50 insertions, 12 deletions
diff --git a/.changeset/clever-suits-hide.md b/.changeset/clever-suits-hide.md
new file mode 100644
index 000000000..31fdb518b
--- /dev/null
+++ b/.changeset/clever-suits-hide.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Bugfix: CSS import ordering, empty CSS output on build
diff --git a/packages/astro/src/vite-plugin-build-css/index.ts b/packages/astro/src/vite-plugin-build-css/index.ts
index d46f56d13..2989faee2 100644
--- a/packages/astro/src/vite-plugin-build-css/index.ts
+++ b/packages/astro/src/vite-plugin-build-css/index.ts
@@ -124,6 +124,8 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin {
}
}
+ if (!chunkCSS) return null; // don’t output empty .css files
+
if (isPureCSS) {
const { code: minifiedCSS } = await esbuild.transform(chunkCSS, {
loader: 'css',
diff --git a/packages/astro/src/vite-plugin-build-html/index.ts b/packages/astro/src/vite-plugin-build-html/index.ts
index 7650c0224..cdc5c1877 100644
--- a/packages/astro/src/vite-plugin-build-html/index.ts
+++ b/packages/astro/src/vite-plugin-build-html/index.ts
@@ -55,6 +55,7 @@ export function rollupPluginAstroBuildHTML(options: PluginOptions): VitePlugin {
const astroAssetMap = new Map<string, Promise<Buffer>>();
const cssChunkMap = new Map<string, string[]>();
+ const pageStyleImportOrder: string[] = [];
return {
name: PLUGIN_NAME,
@@ -176,6 +177,11 @@ export function rollupPluginAstroBuildHTML(options: PluginOptions): VitePlugin {
const jsSource = assetImports.map((sid) => `import '${sid}';`).join('\n');
astroPageStyleMap.set(pageStyleId, jsSource);
assetInput.add(pageStyleId);
+
+ // preserve asset order in the order we encounter them
+ for (const assetHref of assetImports) {
+ if (!pageStyleImportOrder.includes(assetHref)) pageStyleImportOrder.push(assetHref);
+ }
}
}
}
@@ -260,17 +266,43 @@ export function rollupPluginAstroBuildHTML(options: PluginOptions): VitePlugin {
assetIdMap.set(assetPath, referenceId);
}
+ // Sort CSS in order of appearance in HTML (pageStyleImportOrder)
+ // This is the “global ordering” used below
+ const sortedCSSChunks = [...pureCSSChunks];
+ sortedCSSChunks.sort((a, b) => {
+ let aIndex = Math.min(
+ ...Object.keys(a.modules).map((id) => {
+ const i = pageStyleImportOrder.findIndex((url) => id.endsWith(url));
+ return i >= 0 ? i : Infinity; // if -1 is encountered (unknown order), move to the end (Infinity)
+ })
+ );
+ let bIndex = Math.min(
+ ...Object.keys(b.modules).map((id) => {
+ const i = pageStyleImportOrder.findIndex((url) => id.endsWith(url));
+ return i >= 0 ? i : Infinity;
+ })
+ );
+ return aIndex - bIndex;
+ });
+ const sortedChunkNames = sortedCSSChunks.map(({ fileName }) => fileName);
+
// Create a mapping of chunks to dependent chunks, used to add the proper
// link tags for CSS.
- for (const chunk of pureCSSChunks) {
- const chunkReferenceIds: string[] = [];
- for (const [specifier, chunkRefID] of chunkToReferenceIdMap.entries()) {
- if (chunk.imports.includes(specifier) || specifier === chunk.fileName) {
- chunkReferenceIds.push(chunkRefID);
- }
+ for (const chunk of sortedCSSChunks) {
+ const chunkModules = [chunk.fileName, ...chunk.imports];
+ // For each HTML output, sort CSS in HTML order Note: here we actually
+ // want -1 to be first. Since the last CSS “wins”, we want to load
+ // “unknown” (-1) CSS ordering first, followed by “known” ordering at
+ // the end so it takes priority
+ chunkModules.sort((a, b) => sortedChunkNames.indexOf(a) - sortedChunkNames.indexOf(b));
+
+ const referenceIDs: string[] = [];
+ for (const chunkID of chunkModules) {
+ const referenceID = chunkToReferenceIdMap.get(chunkID);
+ if (referenceID) referenceIDs.push(referenceID);
}
- for (const [id] of Object.entries(chunk.modules)) {
- cssChunkMap.set(id, chunkReferenceIds);
+ for (const id of Object.keys(chunk.modules)) {
+ cssChunkMap.set(id, referenceIDs);
}
}
diff --git a/packages/astro/test/astro-css-bundling-import.test.js b/packages/astro/test/astro-css-bundling-import.test.js
index ea605143e..ae9b085d6 100644
--- a/packages/astro/test/astro-css-bundling-import.test.js
+++ b/packages/astro/test/astro-css-bundling-import.test.js
@@ -10,7 +10,7 @@ describe('CSS Bundling (ESM import)', () => {
await fixture.build();
});
- it.skip('CSS output in import order', async () => {
+ it('CSS output in import order', async () => {
// note: this test is a little confusing, but the main idea is that
// page-2.astro contains all of page-1.astro, plus some unique styles.
// we only test page-2 to ensure the proper order is observed.
@@ -29,11 +29,10 @@ describe('CSS Bundling (ESM import)', () => {
expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:red}'));
// test 2: insure green comes after blue (page-1.css)
- expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:red}'));
+ expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:#00f}'));
});
- // TODO: need more investigation to fix this
- it.skip('no empty CSS files', async () => {
+ it('no empty CSS files', async () => {
for (const page of ['/page-1/index.html', '/page-2/index.html']) {
const html = await fixture.readFile(page);
const $ = cheerio.load(html);