0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-04-07 23:41:43 -05:00

Improve content collection styles and scripts build perf ()

* Improve content collection styles and scripts build perf

* Update test

It was actually a bug. There was an empty module script injected.

* Skip test

* Fix test not matching non-ccc behaviour

* Workaround bug to make test pass

* Update .changeset/grumpy-pillows-develop.md

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>

---------

Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca>
This commit is contained in:
Bjorn Lu 2024-05-08 17:24:47 +08:00 committed by GitHub
parent cceeafb62a
commit 685fc22bc6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 287 additions and 219 deletions

View file

@ -0,0 +1,5 @@
---
"astro": patch
---
Refactors internal handling of styles and scripts for content collections to improve build performance

View file

@ -3,8 +3,7 @@ import { pathToFileURL } from 'node:url';
import type { Plugin, Rollup } from 'vite';
import type { AstroSettings, SSRElement } from '../@types/astro.js';
import { getAssetsPrefix } from '../assets/utils/getAssetsPrefix.js';
import { getParentModuleInfos, moduleIsTopLevelPage } from '../core/build/graph.js';
import { type BuildInternals, getPageDataByViteID } from '../core/build/internal.js';
import type { BuildInternals } from '../core/build/internal.js';
import type { AstroBuildPlugin } from '../core/build/plugin.js';
import type { StaticBuildOptions } from '../core/build/types.js';
import type { ModuleLoader } from '../core/module-loader/loader.js';
@ -163,49 +162,25 @@ export function astroConfigBuildPlugin(
chunk.type === 'chunk' &&
(chunk.code.includes(LINKS_PLACEHOLDER) || chunk.code.includes(SCRIPTS_PLACEHOLDER))
) {
let entryStyles = new Set<string>();
let entryLinks = new Set<string>();
let entryScripts = new Set<string>();
const entryStyles = new Set<string>();
const entryLinks = new Set<string>();
const entryScripts = new Set<string>();
if (options.settings.config.experimental.contentCollectionCache) {
// TODO: hoisted scripts are still handled on the pageData rather than the asset propagation point
for (const id of chunk.moduleIds) {
const _entryCss = internals.propagatedStylesMap.get(id);
const _entryScripts = internals.propagatedScriptsMap.get(id);
if (_entryCss) {
for (const value of _entryCss) {
if (value.type === 'inline') entryStyles.add(value.content);
if (value.type === 'external') entryLinks.add(value.src);
}
}
if (_entryScripts) {
for (const value of _entryScripts) {
entryScripts.add(value);
}
for (const id of chunk.moduleIds) {
const _entryCss = internals.propagatedStylesMap.get(id);
const _entryScripts = internals.propagatedScriptsMap.get(id);
if (_entryCss) {
// TODO: Separating styles and links this way is not ideal. The `entryCss` list is order-sensitive
// and splitting them into two sets causes the order to be lost, because styles are rendered after
// links. Refactor this away in the future.
for (const value of _entryCss) {
if (value.type === 'inline') entryStyles.add(value.content);
if (value.type === 'external') entryLinks.add(value.src);
}
}
} else {
for (const id of Object.keys(chunk.modules)) {
for (const pageInfo of getParentModuleInfos(id, ssrPluginContext!)) {
if (moduleIsTopLevelPage(pageInfo)) {
const pageViteID = pageInfo.id;
const pageData = getPageDataByViteID(internals, pageViteID);
if (!pageData) continue;
const _entryCss = pageData.propagatedStyles?.get(id);
const _entryScripts = pageData.propagatedScripts?.get(id);
if (_entryCss) {
for (const value of _entryCss) {
if (value.type === 'inline') entryStyles.add(value.content);
if (value.type === 'external') entryLinks.add(value.src);
}
}
if (_entryScripts) {
for (const value of _entryScripts) {
entryScripts.add(value);
}
}
}
if (_entryScripts) {
for (const value of _entryScripts) {
entryScripts.add(value);
}
}
}

View file

@ -25,8 +25,6 @@ export interface BuildInternals {
hoistedScriptIdToHoistedMap: Map<string, Set<string>>;
// A mapping of hoisted script ids back to the pages which reference it
hoistedScriptIdToPagesMap: Map<string, Set<string>>;
// A mapping of hoisted script ids back to the content which reference it
hoistedScriptIdToContentMap: Map<string, Set<string>>;
/**
* Used by the `directRenderScript` option. If script is inlined, its id and
@ -93,7 +91,15 @@ export interface BuildInternals {
cachedClientEntries: string[];
cacheManifestUsed: boolean;
/**
* Map of propagated module ids (usually something like `/Users/...blog.mdx?astroPropagatedAssets`)
* to a set of stylesheets that it uses.
*/
propagatedStylesMap: Map<string, Set<StylesheetAsset>>;
/**
* Map of propagated module ids (usually something like `/Users/...blog.mdx?astroPropagatedAssets`)
* to a set of hoisted scripts that it uses.
*/
propagatedScriptsMap: Map<string, Set<string>>;
// A list of all static files created during the build. Used for SSR.
@ -125,7 +131,6 @@ export function createBuildInternals(): BuildInternals {
cssModuleToChunkIdMap: new Map(),
hoistedScriptIdToHoistedMap,
hoistedScriptIdToPagesMap,
hoistedScriptIdToContentMap: new Map(),
inlinedScripts: new Map(),
entrySpecifierToBundleMap: new Map<string, string>(),
pageToBundleMap: new Map<string, string>(),

View file

@ -53,8 +53,6 @@ export async function collectPagesData(
route,
moduleSpecifier: '',
styles: [],
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
hasSharedModules: false,
};
@ -78,8 +76,6 @@ export async function collectPagesData(
route,
moduleSpecifier: '',
styles: [],
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
hasSharedModules: false,
};

View file

@ -6,7 +6,6 @@ import type { AstroBuildPlugin } from '../plugin.js';
import { PROPAGATED_ASSET_FLAG } from '../../../content/consts.js';
import { prependForwardSlash } from '../../../core/path.js';
import { isContentCollectionsCacheEnabled } from '../../../core/util.js';
import {
getParentModuleInfos,
getTopLevelPageModuleInfos,
@ -32,9 +31,7 @@ export function vitePluginAnalyzer(
const pageScripts = new Map<
string,
{
type: 'page' | 'content';
hoistedSet: Set<string>;
propagatedMapByImporter: Map<string, Set<string>>;
}
>();
@ -53,48 +50,12 @@ export function vitePluginAnalyzer(
if (hoistedScripts.size) {
for (const parentInfo of getParentModuleInfos(from, this, isPropagatedAsset)) {
if (isPropagatedAsset(parentInfo.id)) {
if (isContentCollectionsCacheEnabled(options.settings.config)) {
if (!pageScripts.has(parentInfo.id)) {
pageScripts.set(parentInfo.id, {
type: 'content',
hoistedSet: new Set(),
propagatedMapByImporter: new Map(),
});
}
const propagaters = pageScripts.get(parentInfo.id)!.propagatedMapByImporter;
for (const hid of hoistedScripts) {
if (!propagaters.has(parentInfo.id)) {
propagaters.set(parentInfo.id, new Set());
}
propagaters.get(parentInfo.id)!.add(hid);
}
} else {
for (const nestedParentInfo of getParentModuleInfos(from, this)) {
if (moduleIsTopLevelPage(nestedParentInfo)) {
for (const hid of hoistedScripts) {
if (!pageScripts.has(nestedParentInfo.id)) {
pageScripts.set(nestedParentInfo.id, {
type: 'page',
hoistedSet: new Set(),
propagatedMapByImporter: new Map(),
});
}
const entry = pageScripts.get(nestedParentInfo.id)!;
if (!entry.propagatedMapByImporter.has(parentInfo.id)) {
entry.propagatedMapByImporter.set(parentInfo.id, new Set());
}
entry.propagatedMapByImporter.get(parentInfo.id)!.add(hid);
}
}
}
}
internals.propagatedScriptsMap.set(parentInfo.id, hoistedScripts);
} else if (moduleIsTopLevelPage(parentInfo)) {
for (const hid of hoistedScripts) {
if (!pageScripts.has(parentInfo.id)) {
pageScripts.set(parentInfo.id, {
type: 'page',
hoistedSet: new Set(),
propagatedMapByImporter: new Map(),
});
}
pageScripts.get(parentInfo.id)?.hoistedSet.add(hid);
@ -105,21 +66,20 @@ export function vitePluginAnalyzer(
},
finalize() {
for (const [pageId, { hoistedSet, propagatedMapByImporter, type }] of pageScripts) {
let astroModuleId: string;
if (type === 'page') {
const pageData = getPageDataByViteID(internals, pageId);
if (!pageData) {
continue;
}
const { component } = pageData;
astroModuleId = prependForwardSlash(component);
// Keep track of the importers
pageData.propagatedScripts = propagatedMapByImporter;
} else {
astroModuleId = pageId;
// Add propagated scripts to client build,
// but DON'T add to pages -> hoisted script map.
for (const propagatedScripts of internals.propagatedScriptsMap.values()) {
for (const propagatedScript of propagatedScripts) {
internals.discoveredScripts.add(propagatedScript);
}
}
for (const [pageId, { hoistedSet }] of pageScripts) {
const pageData = getPageDataByViteID(internals, pageId);
if (!pageData) continue;
const { component } = pageData;
const astroModuleId = prependForwardSlash(component);
const uniqueHoistedId = JSON.stringify(Array.from(hoistedSet).sort());
let moduleId: string;
@ -134,32 +94,13 @@ export function vitePluginAnalyzer(
}
internals.discoveredScripts.add(moduleId);
// Add propagated scripts to client build,
// but DON'T add to pages -> hoisted script map.
for (const propagatedScripts of propagatedMapByImporter.values()) {
for (const propagatedScript of propagatedScripts) {
internals.discoveredScripts.add(propagatedScript);
}
}
if (type === 'page') {
// Make sure to track that this page uses this set of hoisted scripts
if (internals.hoistedScriptIdToPagesMap.has(moduleId)) {
const pages = internals.hoistedScriptIdToPagesMap.get(moduleId);
pages!.add(astroModuleId);
} else {
internals.hoistedScriptIdToPagesMap.set(moduleId, new Set([astroModuleId]));
internals.hoistedScriptIdToHoistedMap.set(moduleId, hoistedSet);
}
// Make sure to track that this page uses this set of hoisted scripts
if (internals.hoistedScriptIdToPagesMap.has(moduleId)) {
const pages = internals.hoistedScriptIdToPagesMap.get(moduleId);
pages!.add(astroModuleId);
} else {
// For content collections save to hoistedScriptIdToContentMap instead
if (internals.hoistedScriptIdToContentMap.has(moduleId)) {
const contentModules = internals.hoistedScriptIdToContentMap.get(moduleId);
contentModules!.add(astroModuleId);
} else {
internals.hoistedScriptIdToContentMap.set(moduleId, new Set([astroModuleId]));
internals.hoistedScriptIdToHoistedMap.set(moduleId, hoistedSet);
}
internals.hoistedScriptIdToPagesMap.set(moduleId, new Set([astroModuleId]));
internals.hoistedScriptIdToHoistedMap.set(moduleId, hoistedSet);
}
}
},

View file

@ -171,6 +171,7 @@ function vitePluginContent(
outputOptions(outputOptions) {
const rootPath = normalizePath(fileURLToPath(opts.settings.config.root));
const srcPath = normalizePath(fileURLToPath(opts.settings.config.srcDir));
const entryCache = new Map<string, string>();
extendManualChunks(outputOptions, {
before(id, meta) {
if (id.startsWith(srcPath) && id.slice(srcPath.length).startsWith('content')) {
@ -186,7 +187,11 @@ function vitePluginContent(
return resultId;
}
const [srcRelativePath, flag] = id.replace(rootPath, '/').split('?');
const collectionEntry = findEntryFromSrcRelativePath(lookupMap, srcRelativePath);
const collectionEntry = findEntryFromSrcRelativePath(
lookupMap,
srcRelativePath,
entryCache
);
if (collectionEntry) {
let suffix = '.mjs';
if (flag === PROPAGATED_ASSET_FLAG) {
@ -273,8 +278,11 @@ function vitePluginContent(
};
}
const entryCache = new Map<string, string>();
function findEntryFromSrcRelativePath(lookupMap: ContentLookupMap, srcRelativePath: string) {
function findEntryFromSrcRelativePath(
lookupMap: ContentLookupMap,
srcRelativePath: string,
entryCache: Map<string, string>
) {
let value = entryCache.get(srcRelativePath);
if (value) return value;
for (const collection of Object.values(lookupMap)) {

View file

@ -5,7 +5,6 @@ import type { BuildInternals } from '../internal.js';
import type { AstroBuildPlugin, BuildTarget } from '../plugin.js';
import type { PageBuildData, StaticBuildOptions, StylesheetAsset } from '../types.js';
import { RESOLVED_VIRTUAL_MODULE_ID as ASTRO_CONTENT_VIRTUAL_MODULE_ID } from '../../../content/consts.js';
import { hasAssetPropagationFlag } from '../../../content/index.js';
import type { AstroPluginCssMetadata } from '../../../vite-plugin-astro/index.js';
import * as assetName from '../css-asset-name.js';
@ -63,11 +62,8 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
// stylesheet filenames are kept in here until "post", when they are rendered and ready to be inlined
const pagesToCss: Record<string, Record<string, { order: number; depth: number }>> = {};
const pagesToPropagatedCss: Record<string, Record<string, Set<string>>> = {};
const isContentCollectionCache =
options.buildOptions.settings.config.output === 'static' &&
options.buildOptions.settings.config.experimental.contentCollectionCache;
// Map of module Ids (usually something like `/Users/...blog.mdx?astroPropagatedAssets`) to its imported CSS
const moduleIdToPropagatedCss: Record<string, Set<string>> = {};
const cssBuildPlugin: VitePlugin = {
name: 'astro:rollup-plugin-build-css',
@ -141,20 +137,9 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
const parentModuleInfos = getParentExtendedModuleInfos(id, this, hasAssetPropagationFlag);
for (const { info: pageInfo, depth, order } of parentModuleInfos) {
if (hasAssetPropagationFlag(pageInfo.id)) {
const walkId = isContentCollectionCache ? ASTRO_CONTENT_VIRTUAL_MODULE_ID : id;
for (const parentInfo of getParentModuleInfos(walkId, this)) {
if (moduleIsTopLevelPage(parentInfo) === false) continue;
const pageViteID = parentInfo.id;
const pageData = getPageDataByViteID(internals, pageViteID);
if (pageData === undefined) continue;
for (const css of meta.importedCss) {
const propagatedStyles = (pagesToPropagatedCss[pageData.moduleSpecifier] ??= {});
const existingCss = (propagatedStyles[pageInfo.id] ??= new Set());
existingCss.add(css);
}
const propagatedCss = (moduleIdToPropagatedCss[pageInfo.id] ??= new Set());
for (const css of meta.importedCss) {
propagatedCss.add(css);
}
} else if (moduleIsTopLevelPage(pageInfo)) {
const pageViteID = pageInfo.id;
@ -251,41 +236,30 @@ function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
? { type: 'inline', content: stylesheet.source }
: { type: 'external', src: stylesheet.fileName };
const pages = Array.from(eachPageData(internals));
let sheetAddedToPage = false;
pages.forEach((pageData) => {
// Apply `pagesToCss` information to the respective `pageData.styles`
for (const pageData of eachPageData(internals)) {
const orderingInfo = pagesToCss[pageData.moduleSpecifier]?.[stylesheet.fileName];
if (orderingInfo !== undefined) {
pageData.styles.push({ ...orderingInfo, sheet });
sheetAddedToPage = true;
return;
}
}
const propagatedPaths = pagesToPropagatedCss[pageData.moduleSpecifier];
if (propagatedPaths === undefined) return;
Object.entries(propagatedPaths).forEach(([pageInfoId, css]) => {
// return early if sheet does not need to be propagated
if (css.has(stylesheet.fileName) !== true) return;
// return early if the stylesheet needing propagation has already been included
if (pageData.styles.some((s) => s.sheet === sheet)) return;
let propagatedStyles: Set<StylesheetAsset>;
if (isContentCollectionCache) {
propagatedStyles =
internals.propagatedStylesMap.get(pageInfoId) ??
internals.propagatedStylesMap.set(pageInfoId, new Set()).get(pageInfoId)!;
} else {
propagatedStyles =
pageData.propagatedStyles.get(pageInfoId) ??
pageData.propagatedStyles.set(pageInfoId, new Set()).get(pageInfoId)!;
}
propagatedStyles.add(sheet);
sheetAddedToPage = true;
});
});
// Apply `moduleIdToPropagatedCss` information to `internals.propagatedStylesMap`.
// NOTE: It's pretty much a copy over to `internals.propagatedStylesMap` as it should be
// completely empty. The whole propagation handling could be better refactored in the future.
for (const moduleId in moduleIdToPropagatedCss) {
if (!moduleIdToPropagatedCss[moduleId].has(stylesheet.fileName)) continue;
let propagatedStyles = internals.propagatedStylesMap.get(moduleId);
if (!propagatedStyles) {
propagatedStyles = new Set();
internals.propagatedStylesMap.set(moduleId, propagatedStyles);
}
propagatedStyles.add(sheet);
sheetAddedToPage = true;
}
if (toBeInlined && sheetAddedToPage) {
// CSS is already added to all used pages, we can delete it from the bundle

View file

@ -1,6 +1,6 @@
import type { BuildOptions, Plugin as VitePlugin } from 'vite';
import type { AstroSettings } from '../../../@types/astro.js';
import { isContentCollectionsCacheEnabled, viteID } from '../../util.js';
import { viteID } from '../../util.js';
import type { BuildInternals } from '../internal.js';
import { getPageDataByViteID } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
@ -72,42 +72,23 @@ export function vitePluginHoistedScripts(
output.dynamicImports.length === 0 &&
shouldInlineAsset(output.code, output.fileName, assetsInlineLimit);
let removeFromBundle = false;
const facadeId = output.facadeModuleId!;
// Pages
if (internals.hoistedScriptIdToPagesMap.has(facadeId)) {
const pages = internals.hoistedScriptIdToPagesMap.get(facadeId)!;
for (const pathname of pages) {
const vid = viteID(new URL('.' + pathname, settings.config.root));
const pageInfo = getPageDataByViteID(internals, vid);
if (pageInfo) {
if (canBeInlined) {
pageInfo.hoistedScript = {
type: 'inline',
value: output.code,
};
removeFromBundle = true;
} else {
pageInfo.hoistedScript = {
type: 'external',
value: id,
};
}
}
}
}
// Content collection entries
else {
const contentModules = internals.hoistedScriptIdToContentMap.get(facadeId)!;
for (const contentId of contentModules) {
if (isContentCollectionsCacheEnabled(settings.config)) {
const scripts =
internals.propagatedScriptsMap.get(contentId) ??
internals.propagatedScriptsMap.set(contentId, new Set()).get(contentId)!;
scripts.add(facadeId);
const pages = internals.hoistedScriptIdToPagesMap.get(facadeId)!;
for (const pathname of pages) {
const vid = viteID(new URL('.' + pathname, settings.config.root));
const pageInfo = getPageDataByViteID(internals, vid);
if (pageInfo) {
if (canBeInlined) {
pageInfo.hoistedScript = {
type: 'inline',
value: output.code,
};
removeFromBundle = true;
} else {
pageInfo.hoistedScript = {
type: 'external',
value: id,
};
}
}
}

View file

@ -26,8 +26,6 @@ export interface PageBuildData {
component: ComponentPath;
route: RouteData;
moduleSpecifier: string;
propagatedStyles: Map<string, Set<StylesheetAsset>>;
propagatedScripts: Map<string, Set<string>>;
hoistedScript: HoistedScriptAsset | undefined;
styles: Array<{ depth: number; order: number; sheet: StylesheetAsset }>;
hasSharedModules: boolean;

View file

@ -154,7 +154,10 @@ describe('Setting inlineStylesheets to always in static output', () => {
// to bust cache and prevent modules and their state
// from being reused
site: 'https://test.net/',
root: './fixtures/css-inline-stylesheets/',
// TODO: Uses -3 variant to bust ESM module cache when rendering the pages. Particularly in
// `node_modules/.astro/content/entry.mjs` and `import('./en/endeavour.mjs')`. Ideally this
// should be solved in core, but using this workaround for now.
root: './fixtures/css-inline-stylesheets-3/',
output: 'static',
build: {
inlineStylesheets: 'always',

View file

@ -72,7 +72,7 @@ if (!isWindows) {
// Includes hoisted script
assert.notEqual(
[...allScripts].find((script) => $(script).attr('src')?.includes('hoisted')),
[...allScripts].find((script) => $(script).attr('src')?.includes('/_astro/WithScripts')),
undefined,
'hoisted script missing from head.'
);

View file

@ -0,0 +1,8 @@
{
"name": "@test/css-inline-stylesheets-3",
"version": "0.0.0",
"private": true,
"dependencies": {
"astro": "workspace:*"
}
}

View file

@ -0,0 +1,86 @@
---
const { class: className = '', style, href } = Astro.props;
const { variant = 'primary' } = Astro.props;
---
<span class:list={[`link pixel variant-${variant}`, className]} >
<a {href}>
<span><slot /></span>
</a>
</span>
<style>
.link {
--border-radius: 8;
--duration: 200ms;
--delay: 30ms;
--background: linear-gradient(180deg, var(--link-color-stop-a), var(--link-color-stop-b));
display: flex;
color: white;
font-size: 1.25rem;
width: max-content;
}
a {
display: flex;
align-items: center;
justify-content: center;
padding: 0.67rem 1.25rem;
width: 100%;
height: 100%;
text-decoration: none;
color: inherit !important;
/* Indicates the button boundaries for forced colors users in older browsers */
outline: 1px solid transparent;
}
@media (forced-colors: active) {
a {
border: 1px solid LinkText;
}
}
a > :global(* + *) {
margin-inline-start: 0.25rem;
}
.variant-primary {
--variant: primary;
--background: linear-gradient(180deg, var(--link-color-stop-a), var(--link-color-stop-b));
}
.variant-primary:hover,
.variant-primary:focus-within {
--link-color-stop-a: #6d39ff;
--link-color-stop-b: #af43ff;
}
.variant-primary:active {
--link-color-stop-a: #5f31e1;
--link-color-stop-b: #a740f3;
}
.variant-outline {
--variant: outline;
--background: none;
color: var(--background);
}
.variant-outline > a::before {
position: absolute;
top: 0;
right: calc(var(--pixel-size) * 1px);
bottom: calc(var(--pixel-size) * 1px);
left: calc(var(--pixel-size) * 1px);
content: '';
display: block;
transform-origin: bottom center;
background: linear-gradient(to top, var(--background), rgba(255, 255, 255, 0));
opacity: 0.3;
transform: scaleY(0);
transition: transform 200ms cubic-bezier(0.22, 1, 0.36, 1);
}
.variant-outline:hover > a::before,
.variant-outline:focus-within > a::before {
transform: scaleY(1);
}
.variant-outline:active > a::before {
transform: scaleY(1);
}
</style>

View file

@ -0,0 +1,15 @@
---
title: Endeavour
description: 'Learn about the Endeavour NASA space shuttle.'
publishedDate: 'Sun Jul 11 2021 00:00:00 GMT-0400 (Eastern Daylight Time)'
layout: '../../layouts/Layout.astro'
tags: [space, 90s]
---
**Source:** [Wikipedia](https://en.wikipedia.org/wiki/Space_Shuttle_Endeavour)
Space Shuttle Endeavour (Orbiter Vehicle Designation: OV-105) is a retired orbiter from NASA's Space Shuttle program and the fifth and final operational Shuttle built. It embarked on its first mission, STS-49, in May 1992 and its 25th and final mission, STS-134, in May 2011. STS-134 was expected to be the final mission of the Space Shuttle program, but with the authorization of STS-135, Atlantis became the last shuttle to fly.
The United States Congress approved the construction of Endeavour in 1987 to replace the Space Shuttle Challenger, which was destroyed in 1986.
NASA chose, on cost grounds, to build much of Endeavour from spare parts rather than refitting the Space Shuttle Enterprise, and used structural spares built during the construction of Discovery and Atlantis in its assembly.

View file

@ -0,0 +1,15 @@
.bg-skyblue {
background: skyblue;
}
.bg-lightcoral {
background: lightcoral;
}
.red {
color: darkred;
}
.blue {
color: royalblue;
}

View file

@ -0,0 +1,35 @@
---
import Button from '../components/Button.astro';
import '../imported.css';
interface Props {
title: string;
}
const { title } = Astro.props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<Button>Button used in layout</Button>
<slot />
</body>
</html>
<style is:global>
html {
font-family: system-ui, sans-serif;
background-color: #F6F6F6;
}
code {
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
</style>

View file

@ -0,0 +1,17 @@
---
import { getEntryBySlug } from 'astro:content';
import Button from '../components/Button.astro';
const entry = await getEntryBySlug('en', 'endeavour');
const { Content } = await entry.render();
---
<style>
#welcome::after {
content: '🚀'
}
</style>
<main>
<h1 id="welcome">Welcome to Astro</h1>
<Content/>
<Button>Button used directly in page</Button>
</main>

View file

@ -50,7 +50,7 @@ describe('Head injection w/ MDX', () => {
assert.equal(links.length, 1);
const scripts = document.querySelectorAll('head script[type=module]');
assert.equal(scripts.length, 2);
assert.equal(scripts.length, 1);
});
it('Using component using slots.render() API', async () => {

6
pnpm-lock.yaml generated
View file

@ -2612,6 +2612,12 @@ importers:
specifier: workspace:*
version: link:../../..
packages/astro/test/fixtures/css-inline-stylesheets-3:
dependencies:
astro:
specifier: workspace:*
version: link:../../..
packages/astro/test/fixtures/css-no-code-split:
dependencies:
astro: