Use import order to sort CSS in prod (#4724)
* Use import order to sort CSS in prod * Adding a changeset * Pass through the parent id * Update remaining test
This commit is contained in:
parent
56e225b41a
commit
6efafa4b0e
9 changed files with 93 additions and 21 deletions
5
.changeset/many-tables-brush.md
Normal file
5
.changeset/many-tables-brush.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Use import order to sort CSS in the build
|
|
@ -7,19 +7,21 @@ export function* walkParentInfos(
|
|||
id: string,
|
||||
ctx: { getModuleInfo: GetModuleInfo },
|
||||
depth = 0,
|
||||
seen = new Set<string>()
|
||||
): Generator<[ModuleInfo, number], void, unknown> {
|
||||
seen = new Set<string>(),
|
||||
childId = '',
|
||||
): Generator<[ModuleInfo, number, number], void, unknown> {
|
||||
seen.add(id);
|
||||
const info = ctx.getModuleInfo(id);
|
||||
if (info) {
|
||||
yield [info, depth];
|
||||
let order = childId ? info.importedIds.indexOf(childId) : 0;
|
||||
yield [info, depth, order];
|
||||
}
|
||||
const importers = (info?.importers || []).concat(info?.dynamicImporters || []);
|
||||
for (const imp of importers) {
|
||||
if (seen.has(imp)) {
|
||||
continue;
|
||||
}
|
||||
yield* walkParentInfos(imp, ctx, ++depth, seen);
|
||||
yield* walkParentInfos(imp, ctx, ++depth, seen, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +36,7 @@ export function moduleIsTopLevelPage(info: ModuleInfo): boolean {
|
|||
export function* getTopLevelPages(
|
||||
id: string,
|
||||
ctx: { getModuleInfo: GetModuleInfo }
|
||||
): Generator<[ModuleInfo, number], void, unknown> {
|
||||
): Generator<[ModuleInfo, number, number], void, unknown> {
|
||||
for (const res of walkParentInfos(id, ctx)) {
|
||||
if (moduleIsTopLevelPage(res[0])) {
|
||||
yield res;
|
||||
|
|
|
@ -191,14 +191,26 @@ export function sortedCSS(pageData: PageBuildData) {
|
|||
return Array.from(pageData.css)
|
||||
.sort((a, b) => {
|
||||
let depthA = a[1].depth,
|
||||
depthB = b[1].depth;
|
||||
depthB = b[1].depth,
|
||||
orderA = a[1].order,
|
||||
orderB = b[1].order;
|
||||
|
||||
if (depthA === -1) {
|
||||
return -1;
|
||||
} else if (depthB === -1) {
|
||||
if(orderA === -1 && orderB >= 0) {
|
||||
return 1;
|
||||
} else if(orderB === -1 && orderA >= 0) {
|
||||
return -1;
|
||||
} else if(orderA > orderB) {
|
||||
return 1;
|
||||
} else if(orderA < orderB) {
|
||||
return -1;
|
||||
} else {
|
||||
return depthA > depthB ? -1 : 1;
|
||||
if (depthA === -1) {
|
||||
return -1;
|
||||
} else if (depthB === -1) {
|
||||
return 1;
|
||||
} else {
|
||||
return depthA > depthB ? -1 : 1;
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(([id]) => id);
|
||||
|
|
|
@ -18,7 +18,7 @@ export interface PageBuildData {
|
|||
component: ComponentPath;
|
||||
route: RouteData;
|
||||
moduleSpecifier: string;
|
||||
css: Map<string, { depth: number }>;
|
||||
css: Map<string, { depth: number; order: number; }>;
|
||||
hoistedScript: { type: 'inline' | 'external'; value: string } | undefined;
|
||||
}
|
||||
export type AllPagesData = Record<ComponentPath, PageBuildData>;
|
||||
|
|
|
@ -110,7 +110,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
importedCss: Set<string>;
|
||||
};
|
||||
|
||||
const appendCSSToPage = (pageData: PageBuildData, meta: ViteMetadata, depth: number) => {
|
||||
const appendCSSToPage = (pageData: PageBuildData, meta: ViteMetadata, depth: number, order: number) => {
|
||||
for (const importedCssImport of meta.importedCss) {
|
||||
// CSS is prioritized based on depth. Shared CSS has a higher depth due to being imported by multiple pages.
|
||||
// Depth info is used when sorting the links on the page.
|
||||
|
@ -120,8 +120,15 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
if (depth < cssInfo.depth) {
|
||||
cssInfo.depth = depth;
|
||||
}
|
||||
|
||||
// Update the order, preferring the lowest order we have.
|
||||
if(cssInfo.order === -1) {
|
||||
cssInfo.order = order;
|
||||
} else if(order < cssInfo.order && order > -1) {
|
||||
cssInfo.order = order;
|
||||
}
|
||||
} else {
|
||||
pageData?.css.set(importedCssImport, { depth });
|
||||
pageData?.css.set(importedCssImport, { depth, order });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -161,7 +168,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
for (const id of Object.keys(c.modules)) {
|
||||
for (const pageData of getParentClientOnlys(id, this)) {
|
||||
for (const importedCssImport of meta.importedCss) {
|
||||
pageData.css.set(importedCssImport, { depth: -1 });
|
||||
pageData.css.set(importedCssImport, { depth: -1, order: -1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -169,12 +176,12 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
|
||||
// For this CSS chunk, walk parents until you find a page. Add the CSS to that page.
|
||||
for (const id of Object.keys(c.modules)) {
|
||||
for (const [pageInfo, depth] of walkParentInfos(id, this)) {
|
||||
for (const [pageInfo, depth, order] of walkParentInfos(id, this)) {
|
||||
if (moduleIsTopLevelPage(pageInfo)) {
|
||||
const pageViteID = pageInfo.id;
|
||||
const pageData = getPageDataByViteID(internals, pageViteID);
|
||||
if (pageData) {
|
||||
appendCSSToPage(pageData, meta, depth);
|
||||
appendCSSToPage(pageData, meta, depth, order);
|
||||
}
|
||||
} else if (
|
||||
options.target === 'client' &&
|
||||
|
@ -184,7 +191,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
internals,
|
||||
pageInfo.id
|
||||
)) {
|
||||
appendCSSToPage(pageData, meta, -1);
|
||||
appendCSSToPage(pageData, meta, -1, order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,7 +218,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
);
|
||||
if (cssChunk) {
|
||||
for (const pageData of eachPageData(internals)) {
|
||||
pageData.css.set(cssChunk.fileName, { depth: -1 });
|
||||
pageData.css.set(cssChunk.fileName, { depth: -1, order: -1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,10 +86,25 @@ describe('CSS production ordering', () => {
|
|||
getLinks(html).map((href) => getLinkContent(fixture, href))
|
||||
);
|
||||
|
||||
expect(content).to.have.a.lengthOf(2, 'there are 2 stylesheets');
|
||||
const [, last] = content;
|
||||
expect(content).to.have.a.lengthOf(3, 'there are 3 stylesheets');
|
||||
const [,found] = content;
|
||||
|
||||
expect(last.css).to.match(/#00f/);
|
||||
expect(found.css).to.match(/#00f/);
|
||||
});
|
||||
|
||||
it('CSS injected by injectScript comes first because of import order', async () => {
|
||||
let oneHtml = await fixture.readFile('/one/index.html');
|
||||
let twoHtml = await fixture.readFile('/two/index.html');
|
||||
let threeHtml = await fixture.readFile('/three/index.html');
|
||||
|
||||
for(const html of [oneHtml, twoHtml, threeHtml]) {
|
||||
const content = await Promise.all(
|
||||
getLinks(html).map((href) => getLinkContent(fixture, href))
|
||||
);
|
||||
|
||||
const [first] = content;
|
||||
expect(first.css).to.include('green', 'Came from the injected script');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
14
packages/astro/test/fixtures/css-order/astro.config.mjs
vendored
Normal file
14
packages/astro/test/fixtures/css-order/astro.config.mjs
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
{
|
||||
name: 'test-integration',
|
||||
hooks: {
|
||||
'astro:config:setup'({ injectScript }) {
|
||||
injectScript('page-ssr', `import '/src/styles/base.css';`);
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
14
packages/astro/test/fixtures/css-order/src/pages/three.astro
vendored
Normal file
14
packages/astro/test/fixtures/css-order/src/pages/three.astro
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Testing</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 2px;
|
||||
color: blueviolet;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Testing</h1>
|
||||
</body>
|
||||
</html>
|
3
packages/astro/test/fixtures/css-order/src/styles/base.css
vendored
Normal file
3
packages/astro/test/fixtures/css-order/src/styles/base.css
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
body {
|
||||
background: green;
|
||||
}
|
Loading…
Reference in a new issue