Skip to content

Commit

Permalink
retain css order across <style> and <link> tags
Browse files Browse the repository at this point in the history
  • Loading branch information
lilnasy committed Apr 4, 2023
1 parent cee7c29 commit 37500b5
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 39 deletions.
4 changes: 2 additions & 2 deletions packages/astro/src/core/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from '../render/index.js';
import { RouteCache } from '../render/route-cache.js';
import {
createInlineStyleElementSet,
createStylesheetElementSet,
createLinkStylesheetElementSet,
createModuleScriptElement,
} from '../render/ssr-element.js';
Expand Down Expand Up @@ -175,7 +175,7 @@ export class App {
const pathname = '/' + this.removeBase(url.pathname);
const info = this.#routeDataToRouteInfo.get(routeData!)!;
const links = createLinkStylesheetElementSet(info.links);
const styles = createInlineStyleElementSet(info.styles);
const styles = createStylesheetElementSet(info.styles);

let scripts = new Set<SSRElement>();
for (const script of info.scripts) {
Expand Down
5 changes: 4 additions & 1 deletion packages/astro/src/core/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ export interface RouteInfo {
// Hoisted
| { type: 'inline' | 'external'; value: string }
)[];
styles: string[];
styles: (
| { type: 'inline', content: string }
| { type: 'external', src: string}
)[];
}

export type SerializedRouteInfo = Omit<RouteInfo, 'routeData'> & {
Expand Down
12 changes: 8 additions & 4 deletions packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { createEnvironment, createRenderContext, renderPage } from '../render/in
import { callGetStaticPaths } from '../render/route-cache.js';
import {
createLinkStylesheetElementSet,
createInlineStyleElementSet,
createStylesheetElementSet,
createModuleScriptsSet,
} from '../render/ssr-element.js';
import { createRequest } from '../request.js';
Expand All @@ -46,6 +46,7 @@ import {
eachPrerenderedPageData,
getPageDataByComponent,
sortedCSS,
sortedStylesheets,
} from './internal.js';
import type { PageBuildData, SingleFileBuiltModule, StaticBuildOptions } from './types';
import { getTimeStat } from './util.js';
Expand Down Expand Up @@ -170,7 +171,7 @@ async function generatePage(
const pageInfo = getPageDataByComponent(internals, pageData.route.component);
const linkIds: string[] = sortedCSS(pageData);
const scripts = pageInfo?.hoistedScript ?? null;
const styles = pageInfo?.inlineStyles ?? null;
const styles = sortedStylesheets(pageData);

const pageModule = ssrEntry.pageMap?.get(pageData.component);

Expand Down Expand Up @@ -281,7 +282,10 @@ interface GeneratePathOptions {
internals: BuildInternals;
linkIds: string[];
scripts: { type: 'inline' | 'external'; value: string } | null;
styles: string[] | null;
styles: (
| { type: 'inline', content: string }
| { type: 'external', src: string }
)[] | null;
mod: ComponentInstance;
renderers: SSRLoadedRenderer[];
}
Expand Down Expand Up @@ -371,7 +375,7 @@ async function generatePath(
hoistedScripts ? [hoistedScripts] : [],
settings.config.base
);
const styles = createInlineStyleElementSet(_styles ?? []);
const styles = createStylesheetElementSet(_styles ?? []);

if (settings.scripts.some((script) => script.stage === 'page')) {
const hashedFilePath = internals.entrySpecifierToBundleMap.get(PAGE_SCRIPT_ID);
Expand Down
27 changes: 27 additions & 0 deletions packages/astro/src/core/build/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,33 @@ export function sortedCSS(pageData: PageBuildData) {
.map(([id]) => id);
}

export function sortedStylesheets(pageData: PageBuildData) {
return pageData.styles.sort((a, b) => {
let depthA = a.depth,
depthB = b.depth,
orderA = a.order,
orderB = b.order

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 {
if (depthA === -1) {
return -1;
} else if (depthB === -1) {
return 1;
} else {
return depthA > depthB ? -1 : 1;
}
}
}).map(({ depth, order, ...stylesheet }) => stylesheet)
}

export function isHoistedScript(internals: BuildInternals, id: string): boolean {
return internals.hoistedScriptIdToPagesMap.has(id);
}
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/core/build/page-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export async function collectPagesData(
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
inlineStyles: [],
styles: [],
};

clearInterval(routeCollectionLogTimeout);
Expand All @@ -81,7 +81,7 @@ export async function collectPagesData(
propagatedStyles: new Map(),
propagatedScripts: new Map(),
hoistedScript: undefined,
inlineStyles: [],
styles: [],
};
}

Expand Down
34 changes: 26 additions & 8 deletions packages/astro/src/core/build/plugins/plugin-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,22 +264,40 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
},
{
name: 'astro:rollup-plugin-inline-stylesheets',
// enforce: 'post',
async generateBundle(_outputOptions, bundle) {
if (settings.config.build.inlineStylesheets !== true) return;
const assetInlineLimit = settings.config.vite?.build?.assetsInlineLimit ?? 4096;
const stylesheetConfig = settings.config.experimental.stylesheets
const viteInlineLimit = settings.config.vite?.build?.assetsInlineLimit ?? 4096
for (const [id, stylesheet] of Object.entries(bundle)) {
if (
stylesheet.type === 'asset' &&
stylesheet.name?.endsWith('.css') &&
typeof stylesheet.source === 'string' &&
Buffer.byteLength(stylesheet.source) <= assetInlineLimit
typeof stylesheet.source === 'string'
) {
for (const pageData of eachPageData(internals)) {
if (pageData.css.has(stylesheet.fileName)) {
pageData.inlineStyles.push(stylesheet.source);
const orderingInfo = pageData.css.get(stylesheet.fileName)
if (orderingInfo !== undefined) {
const toBeInlined =
stylesheetConfig === 'inline'
? true
: stylesheetConfig === 'external'
? false
: Buffer.byteLength(stylesheet.source) <= viteInlineLimit
if (toBeInlined) {
pageData.styles.push({
type: 'inline',
content: stylesheet.source,
...orderingInfo
});
delete bundle[id]
}
else {
pageData.styles.push({
type: 'external',
src: stylesheet.name,
...orderingInfo
})
}
pageData.css.delete(stylesheet.fileName);
delete bundle[id];
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/core/build/plugins/plugin-ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ function buildManifest(
.filter((script) => script.stage === 'head-inline')
.map(({ stage, content }) => ({ stage, children: content })),
],
styles: pageData.inlineStyles,
styles: pageData.styles,
routeData: serializeRouteData(pageData.route, settings.config.trailingSlash),
});
}
Expand Down
5 changes: 4 additions & 1 deletion packages/astro/src/core/build/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ export interface PageBuildData {
propagatedStyles: Map<string, Set<string>>;
propagatedScripts: Map<string, Set<string>>;
hoistedScript: { type: 'inline' | 'external'; value: string } | undefined;
inlineStyles: string[];
styles: ({ depth: number, order: number } & (
| { type: 'inline', content: string }
| { type: 'external', src: string }
))[];
}
export type AllPagesData = Record<ComponentPath, PageBuildData>;

Expand Down
13 changes: 3 additions & 10 deletions packages/astro/src/core/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
server: './dist/server/',
assets: '_astro',
serverEntry: 'entry.mjs',
inlineStylesheets: false,
},
server: {
host: false,
Expand All @@ -38,6 +37,7 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
legacy: {},
experimental: {
assets: false,
stylesheets: 'external',
},
};

Expand Down Expand Up @@ -99,10 +99,6 @@ export const AstroConfigSchema = z.object({
.transform((val) => new URL(val)),
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
inlineStylesheets: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
})
.optional()
.default({}),
Expand Down Expand Up @@ -184,6 +180,7 @@ export const AstroConfigSchema = z.object({
experimental: z
.object({
assets: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.assets),
stylesheets: z.enum(['external', 'auto', 'inline']).optional().default(ASTRO_CONFIG_DEFAULTS.experimental.stylsheets)
})
.optional()
.default({}),
Expand Down Expand Up @@ -227,11 +224,7 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: URL) {
.default(ASTRO_CONFIG_DEFAULTS.build.server)
.transform((val) => new URL(val, fileProtocolRoot)),
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
inlineStylesheets: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.inlineStylesheets),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry)
})
.optional()
.default({}),
Expand Down
33 changes: 24 additions & 9 deletions packages/astro/src/core/render/ssr-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,32 @@ export function createLinkStylesheetElementSet(hrefs: string[], base?: string) {
return new Set<SSRElement>(hrefs.map((href) => createLinkStylesheetElement(href, base)));
}

export function createInlineStyleElement(text: string): SSRElement {
return {
props: {
type: 'text/css',
},
children: text,
};
type Stylesheet =
| { type: 'inline', content: string }
| { type: 'external', src: string }

export function createStylesheetElement(stylesheet: Stylesheet, base?: string): SSRElement {
if (stylesheet.type === 'inline') {
return {
props: {
type: 'text/css'
},
children: stylesheet.content
}
}
else {
return {
props: {
rel: 'stylesheet',
href: joinToRoot(stylesheet.src, base)
},
children: ''
}
}
}

export function createInlineStyleElementSet(texts: string[]) {
return new Set(texts.map(createInlineStyleElement));
export function createStylesheetElementSet(stylesheets: Stylesheet[], base?: string): Set<SSRElement> {
return new Set(stylesheets.map(s => createStylesheetElement(s, base)));
}

export function createModuleScriptElement(
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/runtime/server/render/head.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function renderAllHeadContent(result: SSRResult) {
result._metadata.hasRenderedHead = true;
const styles = Array.from(result.styles)
.filter(uniqueElements)
.map((style) => renderElement('style', style));
.map((style) => style.props.rel = 'stylesheet' ? renderElement('link', style) : renderElement('style', style));
// Clear result.styles so that any new styles added will be inlined.
result.styles.clear();
const scripts = Array.from(result.scripts)
Expand Down

0 comments on commit 37500b5

Please sign in to comment.