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

Refactor SSRResult and RenderContext #7575

Merged
merged 9 commits into from
Jul 5, 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/many-nails-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/markdoc': patch
---

Handle internal access change
40 changes: 23 additions & 17 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1932,16 +1932,6 @@ export interface SSRElement {
children: string;
}

export interface SSRMetadata {
renderers: SSRLoadedRenderer[];
pathname: string;
hasHydrationScript: boolean;
hasDirectives: Set<string>;
hasRenderedHead: boolean;
headInTree: boolean;
clientDirectives: Map<string, string>;
}

/**
* A hint on whether the Astro runtime needs to wait on a component to render head
* content. The meanings:
Expand All @@ -1964,23 +1954,39 @@ export interface SSRResult {
scripts: Set<SSRElement>;
links: Set<SSRElement>;
componentMetadata: Map<string, SSRComponentMetadata>;
propagators: Map<AstroComponentFactory, AstroComponentInstance>;
extraHead: Array<string>;
cookies: AstroCookies | undefined;
createAstro(
Astro: AstroGlobalPartial,
props: Record<string, any>,
slots: Record<string, any> | null
): AstroGlobal;
resolve: (s: string) => Promise<string>;
response: ResponseInit;
// Bits 1 = astro, 2 = jsx, 4 = slot
// As rendering occurs these bits are manipulated to determine where content
// is within a slot. This is used for head injection.
scope: number;
renderers: SSRLoadedRenderer[];
/**
* Map of directive name (e.g. `load`) to the directive script code
*/
clientDirectives: Map<string, string>;
/**
* Only used for logging
*/
pathname: string;
cookies: AstroCookies | undefined;
_metadata: SSRMetadata;
}

/**
* Ephemeral and mutable state during rendering that doesn't rely
* on external configuration
*/
export interface SSRMetadata {
hasHydrationScript: boolean;
hasDirectives: Set<string>;
hasRenderedHead: boolean;
headInTree: boolean;
extraHead: string[];
propagators: Map<AstroComponentFactory, AstroComponentInstance>;
}

/* Preview server stuff */
export interface PreviewServer {
host?: string;
Expand Down
2 changes: 0 additions & 2 deletions packages/astro/src/core/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,6 @@ export class App {
const mod = (await page.page()) as any;
const renderContext = await createRenderContext({
request,
origin: url.origin,
pathname,
componentMetadata: this.#manifest.componentMetadata,
scripts,
Expand Down Expand Up @@ -295,7 +294,6 @@ export class App {

const ctx = await createRenderContext({
request,
origin: url.origin,
pathname,
route: routeData,
status,
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,6 @@ async function generatePath(
});

const renderContext = await createRenderContext({
origin,
pathname,
request: createRequest({ url, headers: new Headers(), logging, ssr }),
componentMetadata: manifest.componentMetadata,
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/core/endpoint/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export async function call(options: SSROptions, logging: LogOptions) {

const ctx = await createRenderContext({
request: options.request,
origin: options.origin,
pathname: options.pathname,
route: options.route,
env,
Expand Down
17 changes: 5 additions & 12 deletions packages/astro/src/core/render/context.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type {
AstroCookies,
ComponentInstance,
Params,
Props,
Expand All @@ -18,23 +17,21 @@ const clientLocalsSymbol = Symbol.for('astro.locals');
*/
export interface RenderContext {
request: Request;
origin: string;
pathname: string;
url: URL;
scripts?: Set<SSRElement>;
links?: Set<SSRElement>;
styles?: Set<SSRElement>;
componentMetadata?: SSRResult['componentMetadata'];
route?: RouteData;
status?: number;
cookies?: AstroCookies;
params: Params;
props: Props;
locals?: object;
}

export type CreateRenderContextArgs = Partial<RenderContext> & {
origin?: string;
export type CreateRenderContextArgs = Partial<
Omit<RenderContext, 'params' | 'props' | 'locals'>
> & {
request: RenderContext['request'];
mod: ComponentInstance;
env: Environment;
Expand All @@ -44,9 +41,7 @@ export async function createRenderContext(
options: CreateRenderContextArgs
): Promise<RenderContext> {
const request = options.request;
const url = new URL(request.url);
const origin = options.origin ?? url.origin;
const pathname = options.pathname ?? url.pathname;
const pathname = options.pathname ?? new URL(request.url).pathname;
const [params, props] = await getParamsAndProps({
mod: options.mod as any,
route: options.route,
Expand All @@ -56,11 +51,9 @@ export async function createRenderContext(
ssr: options.env.ssr,
});

let context = {
const context: RenderContext = {
...options,
origin,
pathname,
url,
params,
props,
};
Expand Down
3 changes: 0 additions & 3 deletions packages/astro/src/core/render/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ export async function renderPage({
styles: renderContext.styles,
logging: env.logging,
markdown: env.markdown,
mode: env.mode,
origin: renderContext.origin,
params: renderContext.params,
props: renderContext.props,
pathname: renderContext.pathname,
componentMetadata: renderContext.componentMetadata,
resolve: env.resolve,
Expand Down
3 changes: 0 additions & 3 deletions packages/astro/src/core/render/dev/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ export interface SSROptions {
env: DevelopmentEnvironment;
/** location of file on disk */
filePath: URL;
/** production website */
origin: string;
/** the web request (needed for dynamic routes) */
pathname: string;
/** The runtime component instance */
Expand Down Expand Up @@ -157,7 +155,6 @@ export async function renderPage(options: SSROptions): Promise<Response> {

const renderContext = await createRenderContext({
request: options.request,
origin: options.origin,
pathname: options.pathname,
scripts,
links,
Expand Down
18 changes: 6 additions & 12 deletions packages/astro/src/core/render/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import type {
AstroGlobal,
AstroGlobalPartial,
Params,
Props,
RuntimeMode,
SSRElement,
SSRLoadedRenderer,
SSRResult,
Expand Down Expand Up @@ -33,15 +31,12 @@ export interface CreateResultArgs {
*/
ssr: boolean;
logging: LogOptions;
origin: string;
/**
* Used to support `Astro.__renderMarkdown` for legacy `<Markdown />` component
*/
markdown: MarkdownRenderingOptions;
mode: RuntimeMode;
params: Params;
pathname: string;
props: Props;
renderers: SSRLoadedRenderer[];
clientDirectives: Map<string, string>;
resolve: (s: string) => Promise<string>;
Expand Down Expand Up @@ -170,9 +165,9 @@ export function createResult(args: CreateResultArgs): SSRResult {
scripts: args.scripts ?? new Set<SSRElement>(),
links: args.links ?? new Set<SSRElement>(),
componentMetadata,
propagators: new Map(),
extraHead: [],
scope: 0,
renderers,
clientDirectives,
pathname,
cookies,
/** This function returns the `Astro` faux-global */
createAstro(
Expand Down Expand Up @@ -259,16 +254,15 @@ export function createResult(args: CreateResultArgs): SSRResult {
return Astro;
},
resolve,
response,
_metadata: {
renderers,
pathname,
hasHydrationScript: false,
hasRenderedHead: false,
hasDirectives: new Set(),
headInTree: false,
clientDirectives,
extraHead: [],
propagators: new Map(),
},
response,
};

return result;
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/runtime/server/hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ interface ExtractedProps {
// Finds these special props and removes them from what gets passed into the component.
export function extractDirectives(
inputProps: Record<string | number | symbol, any>,
clientDirectives: SSRResult['_metadata']['clientDirectives']
clientDirectives: SSRResult['clientDirectives']
): ExtractedProps {
let extracted: ExtractedProps = {
isPage: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/runtime/server/jsx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ async function renderJSXVNode(result: SSRResult, vnode: AstroVNode, skip: Skip):
if (isVNode(vnode)) {
switch (true) {
case !vnode.type: {
throw new Error(`Unable to render ${result._metadata.pathname} because it contains an undefined Component!
throw new Error(`Unable to render ${result.pathname} because it contains an undefined Component!
Did you forget to import the component or is it possible there is a typo?`);
}
case (vnode.type as any) === Symbol.for('astro:fragment'):
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/runtime/server/render/astro/instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ export function createAstroComponentInstance(
) {
validateComponentProps(props, displayName);
const instance = new AstroComponentInstance(result, props, slots, factory);
if (isAPropagatingComponent(result, factory) && !result.propagators.has(factory)) {
result.propagators.set(factory, instance);
if (isAPropagatingComponent(result, factory) && !result._metadata.propagators.has(factory)) {
result._metadata.propagators.set(factory, instance);
}
return instance;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/runtime/server/render/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ async function renderFrameworkComponent(
);
}

const { renderers, clientDirectives } = result._metadata;
const { renderers, clientDirectives } = result;
const metadata: AstroComponentMetadata = {
astroStaticSlot: true,
displayName,
Expand Down
17 changes: 7 additions & 10 deletions packages/astro/src/runtime/server/render/head.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { SSRResult } from '../../../@types/astro';

import { markHTMLString } from '../escape.js';
import type { MaybeRenderHeadInstruction, RenderHeadInstruction } from './types';
import { renderElement } from './util.js';

// Filter out duplicate elements in our set
Expand Down Expand Up @@ -34,29 +35,25 @@ export function renderAllHeadContent(result: SSRResult) {

let content = links.join('\n') + styles.join('\n') + scripts.join('\n');

if (result.extraHead.length > 0) {
for (const part of result.extraHead) {
if (result._metadata.extraHead.length > 0) {
for (const part of result._metadata.extraHead) {
content += part;
}
}

return markHTMLString(content);
}

export function* renderHead(result: SSRResult) {
yield { type: 'head', result } as const;
export function* renderHead(): Generator<RenderHeadInstruction> {
yield { type: 'head' };
}

// This function is called by Astro components that do not contain a <head> component
// This accommodates the fact that using a <head> is optional in Astro, so this
// is called before a component's first non-head HTML element. If the head was
// already injected it is a noop.
export function* maybeRenderHead(result: SSRResult) {
if (result._metadata.hasRenderedHead) {
return;
}

export function* maybeRenderHead(): Generator<MaybeRenderHeadInstruction> {
// This is an instruction informing the page rendering that head might need rendering.
// This allows the page to deduplicate head injections.
yield { type: 'maybe-head', result, scope: result.scope } as const;
yield { type: 'maybe-head' };
}
6 changes: 3 additions & 3 deletions packages/astro/src/runtime/server/render/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ async function iterableToHTMLBytes(
// Recursively calls component instances that might have head content
// to be propagated up.
async function bufferHeadContent(result: SSRResult) {
const iterator = result.propagators.values();
const iterator = result._metadata.propagators.values();
while (true) {
const { value, done } = iterator.next();
if (done) {
break;
}
const returnValue = await value.init(result);
if (isHeadAndContent(returnValue)) {
result.extraHead.push(returnValue.head);
result._metadata.extraHead.push(returnValue.head);
}
}
}
Expand All @@ -86,7 +86,7 @@ export async function renderPage(
try {
if (nonAstroPageNeedsHeadInjection(componentFactory)) {
const parts = new HTMLParts();
for await (const chunk of maybeRenderHead(result)) {
for await (const chunk of maybeRenderHead()) {
parts.append(chunk, result);
}
head = parts.toString();
Expand Down
5 changes: 0 additions & 5 deletions packages/astro/src/runtime/server/render/types.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,16 @@
import type { SSRResult } from '../../../@types/astro';
import type { HydrationMetadata } from '../hydration.js';

export type RenderDirectiveInstruction = {
type: 'directive';
result: SSRResult;
hydration: HydrationMetadata;
};

export type RenderHeadInstruction = {
type: 'head';
result: SSRResult;
};

export type MaybeRenderHeadInstruction = {
type: 'maybe-head';
result: SSRResult;
scope: number;
};

export type RenderInstruction =
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/runtime/server/scripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function determinesIfNeedsDirectiveScript(result: SSRResult, directive: s
export type PrescriptType = null | 'both' | 'directive';

function getDirectiveScriptText(result: SSRResult, directive: string): string {
const clientDirectives = result._metadata.clientDirectives;
const clientDirectives = result.clientDirectives;
const clientDirective = clientDirectives.get(directive);
if (!clientDirective) {
throw new Error(`Unknown directive: ${directive}`);
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/vite-plugin-astro-server/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ export async function handleRoute(
const options: SSROptions = {
env,
filePath,
origin,
preload: preloadedComponent,
pathname,
request,
Expand Down
Loading