Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix CSS chunking between multiple framework components #6582

Merged
merged 8 commits into from
Apr 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/violet-islands-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fix CSS chunking and deduping between multiple Astro files and framework components
24 changes: 13 additions & 11 deletions packages/astro/src/core/build/plugins/plugin-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,20 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
{
name: 'astro:rollup-plugin-build-css',

transform(_, id) {
// In the SSR build, styles that are bundled are tracked in `internals.cssChunkModuleIds`.
// In the client build, if we're also bundling the same style, return an empty string to
// deduplicate the final CSS output.
if (options.target === 'client' && internals.cssChunkModuleIds.has(id)) {
return '';
}
},

outputOptions(outputOptions) {
// Skip in client builds as its module graph doesn't have reference to Astro pages
// to be able to chunk based on it's related top-level pages.
if (options.target === 'client') return;

const assetFileNames = outputOptions.assetFileNames;
const namingIncludesHash = assetFileNames?.toString().includes('[hash]');
const createNameForParentPages = namingIncludesHash
Expand Down Expand Up @@ -133,17 +146,6 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
internals.cssChunkModuleIds.add(id);
}
}
// In the client build, we bail if the chunk is a duplicated CSS chunk tracked from
// above. We remove all the importedCss to prevent emitting the CSS asset.
if (options.target === 'client') {
if (Object.keys(c.modules).every((id) => internals.cssChunkModuleIds.has(id))) {
for (const importedCssImport of meta.importedCss) {
delete bundle[importedCssImport];
meta.importedCss.delete(importedCssImport);
}
return;
}
}
Comment on lines -136 to -146
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is removed in favour of the transform() hook, which does the same thing, and should be safer.


// For the client build, client:only styles need to be mapped
// over to their page. For this chunk, determine if it's a child of a
Expand Down
16 changes: 12 additions & 4 deletions packages/astro/test/astro-client-only.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,12 @@ describe('Client only components', () => {
const html = await fixture.readFile('/index.html');
const $ = cheerioLoad(html);

const href = $('link[rel=stylesheet]').attr('href');
const css = await fixture.readFile(href);
const stylesheets = await Promise.all(
$('link[rel=stylesheet]').map((_, el) => {
return fixture.readFile(el.attribs.href);
})
);
const css = stylesheets.join('');

expect(css).to.match(/yellowgreen/, 'Svelte styles are added');
expect(css).to.match(/Courier New/, 'Global styles are added');
Expand Down Expand Up @@ -87,8 +91,12 @@ describe('Client only components subpath', () => {
const html = await fixture.readFile('/index.html');
const $ = cheerioLoad(html);

const href = $('link[rel=stylesheet]').attr('href');
const css = await fixture.readFile(href.replace(/\/blog/, ''));
const stylesheets = await Promise.all(
$('link[rel=stylesheet]').map((_, el) => {
return fixture.readFile(el.attribs.href.replace(/\/blog/, ''));
})
);
const css = stylesheets.join('');

expect(css).to.match(/yellowgreen/, 'Svelte styles are added');
expect(css).to.match(/Courier New/, 'Global styles are added');
Expand Down
19 changes: 19 additions & 0 deletions packages/astro/test/css-order-import.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,25 @@ describe('CSS ordering - import order', () => {
expect(idx1).to.be.greaterThan(idx2);
expect(idx2).to.be.greaterThan(idx3);
});

it('correctly chunks css import from framework components', async () => {
let html = await fixture.readFile('/index.html');

const content = await Promise.all(getLinks(html).map((href) => getLinkContent(href)));
const [, { css }] = content;
expect(css).to.not.include(
'.client-1{background:red!important}',
'CSS from Client2.jsx leaked into index.astro when chunking'
);
});

it('dedupe css between astro and framework components', async () => {
let html = await fixture.readFile('/dedupe/index.html');

const content = await Promise.all(getLinks(html).map((href) => getLinkContent(href)));
const css = content.map((c) => c.css).join('');
expect(css.match(/\.astro-jsx/)?.length).to.eq(1, '.astro-jsx class is duplicated');
});
});

describe('Dynamic import', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from 'astro/config';
import react from '@astrojs/react'

// https://astro.build/config
export default defineConfig({
integrations: [react()]
});
5 changes: 4 additions & 1 deletion packages/astro/test/fixtures/css-order-import/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"name": "@test/css-order-import",
"dependencies": {
"astro": "workspace:*"
"@astrojs/react": "workspace:*",
"astro": "workspace:*",
"react": "^18.1.0",
"react-dom": "^18.1.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "../styles/Client1.css"

export default function Client() {
return <div className="client-1">Client 1</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "../styles/Client2.css"

export default function Client() {
return <div className="client-2">Client 2</div>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import '../styles/AstroJsx.css';

export default function Dedupe() {
return <div className="astro-jsx">Dedupe Astro JSX</div>;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
import One from '../components/One.astro';
import Two from '../components/Two.astro';
import Client2 from '../components/Client2.jsx';
---
<html>
<head>
Expand All @@ -15,5 +16,6 @@ import Two from '../components/Two.astro';
</head>
<body>
<h1>Test</h1>
<Client2 client:only="react" />
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
import '../styles/AstroJsx.css';
import Dedupe from '../components/Dedupe.jsx';
---
<html>
<head>
<title>Test</title>
</head>
<body>
<h1>Test</h1>
<Dedupe client:only="react" />
</body>
</html>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
import '../styles/base.css';
import Client1 from '../components/Client1.jsx';
---
<html>
<head>
Expand All @@ -12,5 +13,6 @@ import '../styles/base.css';
</head>
<body>
<h1>Test</h1>
<Client1 client:only="react" />
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* this css is imported in both .astro and .jsx component, make sure CSS is deduped */
.astro-jsx {
color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.client-1 {
background: green;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
This cheeky css tries to affect index.astro, the good thing is that the Client2.jsx component is
only loaded in component.astro. So index.astro's Client1.jsx component should not be affected by this.
*/
.client-1 {
background: red !important;
}
.client-2 {
background: green;
}
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.