parent
f6b15c3516
commit
62a5e98c90
4 changed files with 50 additions and 12 deletions
5
.changeset/clever-suits-hide.md
Normal file
5
.changeset/clever-suits-hide.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Bugfix: CSS import ordering, empty CSS output on build
|
|
@ -124,6 +124,8 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!chunkCSS) return null; // don’t output empty .css files
|
||||||
|
|
||||||
if (isPureCSS) {
|
if (isPureCSS) {
|
||||||
const { code: minifiedCSS } = await esbuild.transform(chunkCSS, {
|
const { code: minifiedCSS } = await esbuild.transform(chunkCSS, {
|
||||||
loader: 'css',
|
loader: 'css',
|
||||||
|
|
|
@ -55,6 +55,7 @@ export function rollupPluginAstroBuildHTML(options: PluginOptions): VitePlugin {
|
||||||
const astroAssetMap = new Map<string, Promise<Buffer>>();
|
const astroAssetMap = new Map<string, Promise<Buffer>>();
|
||||||
|
|
||||||
const cssChunkMap = new Map<string, string[]>();
|
const cssChunkMap = new Map<string, string[]>();
|
||||||
|
const pageStyleImportOrder: string[] = [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: PLUGIN_NAME,
|
name: PLUGIN_NAME,
|
||||||
|
@ -176,6 +177,11 @@ export function rollupPluginAstroBuildHTML(options: PluginOptions): VitePlugin {
|
||||||
const jsSource = assetImports.map((sid) => `import '${sid}';`).join('\n');
|
const jsSource = assetImports.map((sid) => `import '${sid}';`).join('\n');
|
||||||
astroPageStyleMap.set(pageStyleId, jsSource);
|
astroPageStyleMap.set(pageStyleId, jsSource);
|
||||||
assetInput.add(pageStyleId);
|
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);
|
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
|
// Create a mapping of chunks to dependent chunks, used to add the proper
|
||||||
// link tags for CSS.
|
// link tags for CSS.
|
||||||
for (const chunk of pureCSSChunks) {
|
for (const chunk of sortedCSSChunks) {
|
||||||
const chunkReferenceIds: string[] = [];
|
const chunkModules = [chunk.fileName, ...chunk.imports];
|
||||||
for (const [specifier, chunkRefID] of chunkToReferenceIdMap.entries()) {
|
// For each HTML output, sort CSS in HTML order Note: here we actually
|
||||||
if (chunk.imports.includes(specifier) || specifier === chunk.fileName) {
|
// want -1 to be first. Since the last CSS “wins”, we want to load
|
||||||
chunkReferenceIds.push(chunkRefID);
|
// “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)) {
|
for (const id of Object.keys(chunk.modules)) {
|
||||||
cssChunkMap.set(id, chunkReferenceIds);
|
cssChunkMap.set(id, referenceIDs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe('CSS Bundling (ESM import)', () => {
|
||||||
await fixture.build();
|
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
|
// 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.
|
// 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.
|
// 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}'));
|
expect(css.indexOf('p{color:green}')).to.be.greaterThan(css.indexOf('p{color:red}'));
|
||||||
|
|
||||||
// test 2: insure green comes after blue (page-1.css)
|
// 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('no empty CSS files', async () => {
|
||||||
it.skip('no empty CSS files', async () => {
|
|
||||||
for (const page of ['/page-1/index.html', '/page-2/index.html']) {
|
for (const page of ['/page-1/index.html', '/page-2/index.html']) {
|
||||||
const html = await fixture.readFile(page);
|
const html = await fixture.readFile(page);
|
||||||
const $ = cheerio.load(html);
|
const $ = cheerio.load(html);
|
||||||
|
|
Loading…
Reference in a new issue