Skip to content
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
2 changes: 0 additions & 2 deletions packages/astro/src/container/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ function createManifest(
clientScriptHashes: manifest?.clientScriptHashes ?? [],
clientStyleHashes: manifest?.clientStyleHashes ?? [],
shouldInjectCspMetaTags: manifest?.shouldInjectCspMetaTags ?? false,
astroIslandHashes: manifest?.astroIslandHashes ?? {},
};
}

Expand Down Expand Up @@ -253,7 +252,6 @@ type AstroContainerManifest = Pick<
| 'clientScriptHashes'
| 'clientStyleHashes'
| 'shouldInjectCspMetaTags'
| 'astroIslandHashes'
>;

type AstroContainerConstructor = {
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/core/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ export type SSRManifest = {
* When enabled, Astro tracks the hashes of script and styles, and eventually it will render the `<meta>` tag
*/
shouldInjectCspMetaTags: boolean;
astroIslandHashes: Record<string, string>;
};

export type SSRActions = {
Expand Down
10 changes: 0 additions & 10 deletions packages/astro/src/core/astro-islands-hashes.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/astro/src/core/base-pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import type {
} from '../types/public/internal.js';
import { createOriginCheckMiddleware } from './app/middlewares.js';
import type { SSRActions } from './app/types.js';
import { createCSPMiddleware } from './csp/middleware.js';
import { ActionNotFoundError } from './errors/errors-data.js';
import { AstroError } from './errors/index.js';
import type { Logger } from './logger/core.js';
import { NOOP_MIDDLEWARE_FN } from './middleware/noop-middleware.js';
import { sequence } from './middleware/sequence.js';
import { RouteCache } from './render/route-cache.js';
import { createDefaultRoutes } from './routing/default.js';
import { createCSPMiddleware } from './csp/middleware.js';

/**
* The `Pipeline` represents the static parts of rendering that do not change between requests.
Expand Down
16 changes: 7 additions & 9 deletions packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
getStaticImageList,
prepareAssetsGenerationEnv,
} from '../../assets/build/generate.js';
import { type BuildInternals, hasPrerenderedPages } from './internal.js';
import {
isRelativePath,
joinPaths,
Expand All @@ -29,6 +28,7 @@ import type {
SSRLoadedRenderer,
} from '../../types/public/internal.js';
import type { SSRActions, SSRManifest, SSRManifestI18n } from '../app/types.js';
import { shouldTrackCspHashes, trackScriptHashes, trackStyleHashes } from '../csp/common.js';
import { NoPrerenderedRoutesWithDomains } from '../errors/errors-data.js';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { NOOP_MIDDLEWARE_FN } from '../middleware/noop-middleware.js';
Expand All @@ -41,6 +41,7 @@ import { matchRoute } from '../routing/match.js';
import { stringifyParams } from '../routing/params.js';
import { getOutputFilename } from '../util.js';
import { getOutFile, getOutFolder } from './common.js';
import { type BuildInternals, hasPrerenderedPages } from './internal.js';
import { cssOrder, mergeInlineCss } from './internal.js';
import { BuildPipeline } from './pipeline.js';
import type {
Expand All @@ -50,8 +51,6 @@ import type {
StylesheetAsset,
} from './types.js';
import { getTimeStat, shouldAppendForwardSlash } from './util.js';
import { shouldTrackCspHashes, trackScriptHashes, trackStyleHashes } from '../csp/common.js';
import { ASTRO_ISLAND_HASHES } from '../astro-islands-hashes.js';

export async function generatePages(options: StaticBuildOptions, internals: BuildInternals) {
const generatePagesTimer = performance.now();
Expand All @@ -70,7 +69,7 @@ export async function generatePages(options: StaticBuildOptions, internals: Buil
const actions: SSRActions = internals.astroActionsEntryPoint
? await import(internals.astroActionsEntryPoint.toString()).then((mod) => mod)
: NOOP_ACTIONS_MOD;
manifest = createBuildManifest(
manifest = await createBuildManifest(
options.settings,
internals,
renderers.renderers as SSRLoadedRenderer[],
Expand Down Expand Up @@ -604,22 +603,22 @@ function getPrettyRouteName(route: RouteData): string {
*
* Renderers needs to be pulled out from the page module emitted during the build.
*/
function createBuildManifest(
async function createBuildManifest(
settings: AstroSettings,
internals: BuildInternals,
renderers: SSRLoadedRenderer[],
middleware: MiddlewareHandler,
actions: SSRActions,
key: Promise<CryptoKey>,
): SSRManifest {
): Promise<SSRManifest> {
let i18nManifest: SSRManifestI18n | undefined = undefined;

let clientStyleHashes: string[] = [];
let clientScriptHashes: string[] = [];

if (shouldTrackCspHashes(settings.config)) {
clientScriptHashes = trackScriptHashes(internals, settings);
clientStyleHashes = trackStyleHashes(internals);
clientScriptHashes = await trackScriptHashes(internals, settings);
clientStyleHashes = await trackStyleHashes(internals, settings);
}

if (settings.config.i18n) {
Expand Down Expand Up @@ -668,6 +667,5 @@ function createBuildManifest(
clientStyleHashes,
clientScriptHashes,
shouldInjectCspMetaTags: shouldTrackCspHashes(settings.config),
astroIslandHashes: ASTRO_ISLAND_HASHES,
};
}
5 changes: 5 additions & 0 deletions packages/astro/src/core/build/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ export interface BuildInternals {

// A list of all static files created during the build. Used for SSR.
staticFiles: Set<string>;

// A list of all statics chunks and assets that are built in the client
clientChunksAndAssets: Set<string>;

// The SSR entry chunk. Kept in internals to share between ssr/client build steps
ssrEntryChunk?: Rollup.OutputChunk;
// The SSR manifest entry chunk.
Expand Down Expand Up @@ -121,6 +125,7 @@ export function createBuildInternals(): BuildInternals {
prerenderOnlyChunks: [],
astroActionsEntryPoint: undefined,
middlewareEntryPoint: undefined,
clientChunksAndAssets: new Set(),
};
}

Expand Down
4 changes: 3 additions & 1 deletion packages/astro/src/core/build/plugins/plugin-internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ function vitePluginInternals(input: Set<string>, internals: BuildInternals): Vit
);
}
await Promise.all(promises);
for (const [, chunk] of Object.entries(bundle)) {
for (const [_, chunk] of Object.entries(bundle)) {
internals.clientChunksAndAssets.add(chunk.fileName);

if (chunk.type === 'chunk' && chunk.facadeModuleId) {
const specifiers = mapping.get(chunk.facadeModuleId) || new Set([chunk.facadeModuleId]);
for (const specifier of specifiers) {
Expand Down
14 changes: 6 additions & 8 deletions packages/astro/src/core/build/plugins/plugin-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
SerializedRouteInfo,
SerializedSSRManifest,
} from '../../app/types.js';
import { shouldTrackCspHashes, trackScriptHashes, trackStyleHashes } from '../../csp/common.js';
import { encodeKey } from '../../encryption.js';
import { fileExtension, joinPaths, prependForwardSlash } from '../../path.js';
import { DEFAULT_COMPONENTS } from '../../routing/default.js';
Expand All @@ -23,8 +24,6 @@ import { type BuildInternals, cssOrder, mergeInlineCss } from '../internal.js';
import type { AstroBuildPlugin } from '../plugin.js';
import type { StaticBuildOptions } from '../types.js';
import { makePageDataKey } from './util.js';
import { shouldTrackCspHashes, trackScriptHashes, trackStyleHashes } from '../../csp/common.js';
import { ASTRO_ISLAND_HASHES } from '../../astro-islands-hashes.js';

const manifestReplace = '@@ASTRO_MANIFEST_REPLACE@@';
const replaceExp = new RegExp(`['"]${manifestReplace}['"]`, 'g');
Expand Down Expand Up @@ -143,7 +142,7 @@ async function createManifest(

const staticFiles = internals.staticFiles;
const encodedKey = await encodeKey(await buildOpts.key);
return buildManifest(buildOpts, internals, Array.from(staticFiles), encodedKey);
return await buildManifest(buildOpts, internals, Array.from(staticFiles), encodedKey);
}

/**
Expand All @@ -157,12 +156,12 @@ function injectManifest(manifest: SerializedSSRManifest, chunk: Readonly<OutputC
});
}

function buildManifest(
async function buildManifest(
opts: StaticBuildOptions,
internals: BuildInternals,
staticFiles: string[],
encodedKey: string,
): SerializedSSRManifest {
): Promise<SerializedSSRManifest> {
const { settings } = opts;

const routes: SerializedRouteInfo[] = [];
Expand Down Expand Up @@ -281,8 +280,8 @@ function buildManifest(
let clientStyleHashes: string[] = [];

if (shouldTrackCspHashes(settings.config)) {
clientScriptHashes = trackScriptHashes(internals, opts.settings);
clientStyleHashes = trackStyleHashes(internals);
clientScriptHashes = await trackScriptHashes(internals, opts.settings);
clientStyleHashes = await trackStyleHashes(internals, opts.settings);
}

return {
Expand Down Expand Up @@ -317,6 +316,5 @@ function buildManifest(
shouldInjectCspMetaTags: shouldTrackCspHashes(opts.settings.config),
clientStyleHashes,
clientScriptHashes,
astroIslandHashes: ASTRO_ISLAND_HASHES,
};
}
1 change: 0 additions & 1 deletion packages/astro/src/core/build/plugins/plugin-ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ function vitePluginSSR(
internals.staticFiles.add(chunk.fileName);
}
}

for (const [, chunk] of Object.entries(bundle)) {
if (chunk.type === 'asset') {
continue;
Expand Down
62 changes: 51 additions & 11 deletions packages/astro/src/core/csp/common.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,82 @@
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { ISLAND_STYLES } from '../../runtime/server/astro-island-styles.js';
import astroIslandPrebuiltDev from '../../runtime/server/astro-island.prebuilt-dev.js';
import astroIslandPrebuilt from '../../runtime/server/astro-island.prebuilt.js';
import type { AstroSettings } from '../../types/astro.js';
import type { AstroConfig } from '../../types/public/index.js';
import type { BuildInternals } from '../build/internal.js';
import crypto from 'node:crypto';
import type { AstroSettings } from '../../types/astro.js';
import { generateDigest } from '../encryption.js';

export function shouldTrackCspHashes(config: AstroConfig): boolean {
return config.experimental?.csp === true;
}

export function trackStyleHashes(internals: BuildInternals): string[] {
export async function trackStyleHashes(
internals: BuildInternals,
settings: AstroSettings,
): Promise<string[]> {
const clientStyleHashes: string[] = [];
for (const [_, page] of internals.pagesByViteID.entries()) {
for (const style of page.styles) {
if (style.sheet.type === 'inline') {
clientStyleHashes.push(generateHash(style.sheet.content));
clientStyleHashes.push(await generateDigest(style.sheet.content));
}
}
}

for (const clientAsset in internals.clientChunksAndAssets) {
const contents = readFileSync(
fileURLToPath(new URL(clientAsset, settings.config.build.client)),
'utf-8',
);
if (clientAsset.endsWith('.css') || clientAsset.endsWith('.css')) {
clientStyleHashes.push(await generateDigest(contents));
}
}

if (settings.renderers.length > 0) {
clientStyleHashes.push(await generateDigest(ISLAND_STYLES));
}

return clientStyleHashes;
}

export function trackScriptHashes(internals: BuildInternals, settings: AstroSettings): string[] {
export async function trackScriptHashes(
internals: BuildInternals,
settings: AstroSettings,
): Promise<string[]> {
const clientScriptHashes: string[] = [];

for (const script of internals.inlinedScripts.values()) {
clientScriptHashes.push(generateHash(script));
clientScriptHashes.push(await generateDigest(script));
}

for (const directiveContent of Array.from(settings.clientDirectives.values())) {
clientScriptHashes.push(await generateDigest(directiveContent));
}

for (const clientAsset in internals.clientChunksAndAssets) {
const contents = readFileSync(
fileURLToPath(new URL(clientAsset, settings.config.build.client)),
'utf-8',
);
if (clientAsset.endsWith('.js') || clientAsset.endsWith('.mjs')) {
clientScriptHashes.push(await generateDigest(contents));
}
}

for (const script of settings.scripts) {
const { content, stage } = script;
if (stage === 'head-inline' || stage === 'before-hydration') {
clientScriptHashes.push(generateHash(content));
clientScriptHashes.push(await generateDigest(content));
}
}

return clientScriptHashes;
}
if (settings.renderers.length > 0) {
clientScriptHashes.push(await generateDigest(astroIslandPrebuilt));
clientScriptHashes.push(await generateDigest(astroIslandPrebuiltDev));
}

function generateHash(content: string): string {
return crypto.createHash('sha256').update(content).digest('base64');
return clientScriptHashes;
}
10 changes: 10 additions & 0 deletions packages/astro/src/core/encryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,13 @@ export async function decryptString(key: CryptoKey, encoded: string) {
const decryptedString = decoder.decode(decryptedBuffer);
return decryptedString;
}

/**
* Generates an SHA-256 digest of the given string.
* @param {string} data The string to hash.
*/
export async function generateDigest(data: string): Promise<string> {
const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(data));

return encodeBase64(new Uint8Array(hashBuffer));
}
1 change: 0 additions & 1 deletion packages/astro/src/core/render-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,6 @@ export class RenderContext {
shouldInjectCspMetaTags: manifest.shouldInjectCspMetaTags,
clientScriptHashes: manifest.clientScriptHashes,
clientStyleHashes: manifest.clientStyleHashes,
astroIslandHashes: manifest.astroIslandHashes,
};

return result;
Expand Down
3 changes: 1 addition & 2 deletions packages/astro/src/runtime/server/astro-island-styles.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export const ISLAND_STYLES =
'<style>astro-island,astro-slot,astro-static-slot{display:contents}</style>';
export const ISLAND_STYLES = 'astro-island,astro-slot,astro-static-slot{display:contents}';
2 changes: 1 addition & 1 deletion packages/astro/src/runtime/server/render/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { RenderInstruction } from './instruction.js';
import type { SSRResult } from '../../../types/public/internal.js';
import type { HTMLBytes, HTMLString } from '../escape.js';
import { markHTMLString } from '../escape.js';
Expand All @@ -8,6 +7,7 @@ import {
getPrescripts,
} from '../scripts.js';
import { renderAllHeadContent } from './head.js';
import type { RenderInstruction } from './instruction.js';
import { isRenderInstruction } from './instruction.js';
import { renderServerIslandRuntime } from './server-islands.js';
import { type SlotString, isSlotString } from './slot.js';
Expand Down
10 changes: 0 additions & 10 deletions packages/astro/src/runtime/server/render/csp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,6 @@ export function renderCspContent(result: SSRResult): string {
finalStyleHashes.add(`'sha256-${styleHash}'`);
}

if (result.renderers.length > 0) {
for (const [ name, hash ] of Object.entries(result.astroIslandHashes)) {
if (name === 'astro-island-styles') {
finalStyleHashes.add(`'sha256-${hash}'`);
} else {
finalScriptHashes.add(`'sha256-${hash}'`);
}
}
}

const scriptSrc = `style-src 'self' ${Array.from(finalStyleHashes).join(' ')};`;
const styleSrc = `script-src 'self' ${Array.from(finalScriptHashes).join(' ')};`;
return `${scriptSrc} ${styleSrc}`;
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
@@ -1,9 +1,9 @@
import type { SSRResult } from '../../../types/public/internal.js';
import { markHTMLString } from '../escape.js';
import { renderCspContent } from './csp.js';
import type { MaybeRenderHeadInstruction, RenderHeadInstruction } from './instruction.js';
import { createRenderInstruction } from './instruction.js';
import { renderElement } from './util.js';
import { renderCspContent } from './csp.js';

// Filter out duplicate elements in our set
const uniqueElements = (item: any, index: number, all: any[]) => {
Expand Down
Loading