Skip to content

Commit

Permalink
Include CSS when child component uses Astro.glob on page (#4156)
Browse files Browse the repository at this point in the history
* Include CSS when child component uses Astro.glob on page

* windows compat

* Make work on windows

* Remove unnecessary concat
  • Loading branch information
matthewp committed Aug 5, 2022
1 parent c7efcf5 commit 82a1063
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 53 deletions.
5 changes: 5 additions & 0 deletions .changeset/long-toys-rule.md
@@ -0,0 +1,5 @@
---
'astro': patch
---

Add CSS to page when child component uses Astro.glob
7 changes: 3 additions & 4 deletions packages/astro/src/core/build/graph.ts
Expand Up @@ -26,11 +26,10 @@ export function* walkParentInfos(
export function* getTopLevelPages(
id: string,
ctx: { getModuleInfo: GetModuleInfo }
): Generator<string, void, unknown> {
): Generator<ModuleInfo, void, unknown> {
for (const info of walkParentInfos(id, ctx)) {
const importers = (info?.importers || []).concat(info?.dynamicImporters || []);
if (importers.length <= 2 && importers[0] === resolvedPagesVirtualModuleId) {
yield info.id;
if (info?.importers[0] === resolvedPagesVirtualModuleId) {
yield info;
}
}
}
4 changes: 3 additions & 1 deletion packages/astro/src/core/build/static-build.ts
Expand Up @@ -7,7 +7,7 @@ import { BuildInternals, createBuildInternals } from '../../core/build/internal.
import { prependForwardSlash } from '../../core/path.js';
import { emptyDir, isModeServerWithNoAdapter, removeDir } from '../../core/util.js';
import { runHookBuildSetup } from '../../integrations/index.js';
import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js';
import { rollupPluginAstroBuildCSS } from './vite-plugin-css.js';
import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
import type { ViteConfigWithSSR } from '../create-vite';
import { info } from '../logger/core.js';
Expand Down Expand Up @@ -151,6 +151,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
vitePluginInternals(input, internals),
vitePluginPages(opts, internals),
rollupPluginAstroBuildCSS({
buildOptions: opts,
internals,
target: 'server',
astroConfig,
Expand Down Expand Up @@ -233,6 +234,7 @@ async function clientBuild(
vitePluginInternals(input, internals),
vitePluginHoistedScripts(astroConfig, internals),
rollupPluginAstroBuildCSS({
buildOptions: opts,
internals,
target: 'client',
astroConfig,
Expand Down
7 changes: 4 additions & 3 deletions packages/astro/src/core/build/vite-plugin-analyzer.ts
Expand Up @@ -22,7 +22,8 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
}

if (hoistedScripts.size) {
for (const pageId of getTopLevelPages(from, this)) {
for (const pageInfo of getTopLevelPages(from, this)) {
const pageId = pageInfo.id;
for (const hid of hoistedScripts) {
if (pageScripts.has(pageId)) {
pageScripts.get(pageId)?.add(hid);
Expand Down Expand Up @@ -97,8 +98,8 @@ export function vitePluginAnalyzer(internals: BuildInternals): VitePlugin {
clientOnlys.push(cid);
}

for (const pageId of getTopLevelPages(id, this)) {
const pageData = getPageDataByViteID(internals, pageId);
for (const pageInfo of getTopLevelPages(id, this)) {
const pageData = getPageDataByViteID(internals, pageInfo.id);
if (!pageData) continue;

trackClientOnlyPageDatas(internals, pageData, clientOnlys);
Expand Down
@@ -1,29 +1,58 @@
import type { GetModuleInfo, OutputChunk } from 'rollup';
import { BuildInternals } from '../core/build/internal';
import type { PageBuildData } from '../core/build/types';
import type { BuildInternals } from './internal';
import type { PageBuildData, StaticBuildOptions } from './types';
import type { AstroConfig } from '../../@types/astro';

import crypto from 'crypto';
import esbuild from 'esbuild';
import npath from 'path';
import { Plugin as VitePlugin } from 'vite';
import { AstroConfig } from '../@types/astro';
import { getTopLevelPages, walkParentInfos } from '../core/build/graph.js';
import { getPageDataByViteID, getPageDatasByClientOnlyID } from '../core/build/internal.js';
import { isCSSRequest } from '../core/render/util.js';
import { getTopLevelPages, walkParentInfos } from './graph.js';
import { getPageDataByViteID, getPageDatasByClientOnlyID } from './internal.js';
import { relativeToSrcDir } from '../util.js';
import { isCSSRequest } from '../render/util.js';

interface PluginOptions {
internals: BuildInternals;
buildOptions: StaticBuildOptions;
target: 'client' | 'server';
astroConfig: AstroConfig;
}

// Arbitrary magic number, can change.
const MAX_NAME_LENGTH = 70;

export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
const { internals } = options;
const { internals, buildOptions } = options;
const { astroConfig } = buildOptions;

// Turn a page location into a name to be used for the CSS file.
function nameifyPage(id: string) {
let rel = relativeToSrcDir(astroConfig, id);
// Remove pages, ex. blog/posts/something.astro
if(rel.startsWith('pages/')) {
rel = rel.slice(6);
}
// Remove extension, ex. blog/posts/something
const ext = npath.extname(rel);
const noext = rel.slice(0, rel.length - ext.length);
// Replace slashes with dashes, ex. blog-posts-something
const named = noext.replace(/\//g, '-');
return named;
}

function createHashOfPageParents(id: string, ctx: { getModuleInfo: GetModuleInfo }): string {
function createNameForParentPages(id: string, ctx: { getModuleInfo: GetModuleInfo }): string {
const parents = Array.from(getTopLevelPages(id, ctx)).sort();
const hash = crypto.createHash('sha256');
const proposedName = parents.map(page => nameifyPage(page.id)).join('-');

// We don't want absurdedly long chunk names, so if this is too long create a hashed version instead.
if(proposedName.length <= MAX_NAME_LENGTH) {
return proposedName;
}

const hash = crypto.createHash('sha256');
for (const page of parents) {
hash.update(page, 'utf-8');
hash.update(page.id, 'utf-8');
}
return hash.digest('hex').slice(0, 8);
}
Expand All @@ -37,12 +66,9 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
}
}

const CSS_PLUGIN_NAME = '@astrojs/rollup-plugin-build-css';
const CSS_MINIFY_PLUGIN_NAME = '@astrojs/rollup-plugin-build-css-minify';

return [
{
name: CSS_PLUGIN_NAME,
name: 'astro:rollup-plugin-build-css',

outputOptions(outputOptions) {
const manualChunks = outputOptions.manualChunks || Function.prototype;
Expand All @@ -62,7 +88,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
// For CSS, create a hash of all of the pages that use it.
// This causes CSS to be built into shared chunks when used by multiple pages.
if (isCSSRequest(id)) {
return createHashOfPageParents(id, args[0]);
return createNameForParentPages(id, args[0]);
}
};
},
Expand Down Expand Up @@ -96,7 +122,8 @@ 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.entries(c.modules)) {
for (const pageViteID of getTopLevelPages(id, this)) {
for (const pageInfo of getTopLevelPages(id, this)) {
const pageViteID = pageInfo.id;
const pageData = getPageDataByViteID(internals, pageViteID);
for (const importedCssImport of meta.importedCss) {
pageData?.css.add(importedCssImport);
Expand All @@ -110,7 +137,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
},
},
{
name: CSS_MINIFY_PLUGIN_NAME,
name: 'astro:rollup-plugin-build-css-minify',
enforce: 'post',
async generateBundle(_outputOptions, bundle) {
// Minify CSS in each bundle ourselves, since server builds are not minified
Expand Down
10 changes: 10 additions & 0 deletions packages/astro/src/core/util.ts
Expand Up @@ -186,6 +186,16 @@ export function isModeServerWithNoAdapter(config: AstroConfig): boolean {
return config.output === 'server' && !config._ctx.adapter;
}

export function relativeToSrcDir(config: AstroConfig, idOrUrl: URL | string) {
let id: string;
if(typeof idOrUrl !== 'string') {
id = unwrapId(viteID(idOrUrl));
} else {
id = idOrUrl;
}
return id.slice(slash(fileURLToPath(config.srcDir)).length);
}

export function emoji(char: string, fallback: string) {
return process.platform !== 'win32' ? char : fallback;
}
Expand Down
28 changes: 0 additions & 28 deletions packages/astro/src/vite-plugin-build-css/resolve.ts

This file was deleted.

@@ -0,0 +1,12 @@
---
// The side-effect of this happening is needed for the test.
await Astro.glob("../pages/**/*.astro");
---
<style>
.box {
height: 50px;
width: 100%;
background: yellow;
}
</style>
<div class="box"></div>
@@ -0,0 +1,6 @@
---
// The side-effect of this happening is needed for the test.
const pages = await Astro.glob("../pages/**/*.astro");
---

<div>navbar</div>
14 changes: 14 additions & 0 deletions packages/astro/test/fixtures/glob-pages-css/src/pages/index.astro
@@ -0,0 +1,14 @@
---
import Footer from '../components/Footer.astro';
import Navbar from '../components/Navbar.astro';
---
<html>
<head>
<title>Testing</title>
</head>
<body>
<Navbar />
<h1>Testing</h1>
<Footer />
</body>
</html>
@@ -0,0 +1,8 @@
<html>
<head>
<title>Testing</title>
</head>
<body>
<h1>Testing</h1>
</body>
</html>
22 changes: 22 additions & 0 deletions packages/astro/test/glob-pages-css.test.js
@@ -0,0 +1,22 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';

describe('Astro.glob on pages/ directory', () => {
let fixture;

before(async () => {
fixture = await loadFixture({
root: './fixtures/glob-pages-css/',
});
await fixture.build();
});

it('It includes styles from child components', async () => {
let html = await fixture.readFile('/index.html');
let $ = cheerio.load(html);

expect($('link[rel=stylesheet]')).to.have.a.lengthOf(1);

});
});

0 comments on commit 82a1063

Please sign in to comment.