From a766758fb00920ebe13a29d217780bb91fc4b83a Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Thu, 20 Jan 2022 17:24:23 -0600 Subject: [PATCH 1/5] feat: add renderHead util to server --- packages/astro/src/@types/astro.ts | 11 ------ packages/astro/src/core/render/core.ts | 4 +-- packages/astro/src/runtime/server/index.ts | 41 +++++++++------------- 3 files changed, 19 insertions(+), 37 deletions(-) diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index a70c4ad153fb..cb3d2b950fd2 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -303,17 +303,6 @@ export type Params = Record; export type Props = Record; -export interface RenderPageOptions { - request: { - params?: Params; - url: URL; - canonicalURL: URL; - }; - children: any[]; - props: Props; - css?: string[]; -} - type Body = string; export interface EndpointOutput { diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts index 554fbfb8a90d..ba52ed573f6f 100644 --- a/packages/astro/src/core/render/core.ts +++ b/packages/astro/src/core/render/core.ts @@ -1,7 +1,7 @@ import type { ComponentInstance, EndpointHandler, MarkdownRenderOptions, Params, Props, Renderer, RouteData, SSRElement } from '../../@types/astro'; import type { LogOptions } from '../logger.js'; -import { renderEndpoint, renderPage } from '../../runtime/server/index.js'; +import { renderEndpoint, renderToString } from '../../runtime/server/index.js'; import { getParams } from '../routing/index.js'; import { createResult } from './result.js'; import { findPathItemByKey, RouteCache, callGetStaticPaths } from './route-cache.js'; @@ -97,7 +97,7 @@ export async function render(opts: RenderOptions): Promise { scripts, }); - let html = await renderPage(result, Component, pageProps, null); + let html = await renderToString(result, Component, pageProps, null); // inject if missing (TODO: is a more robust check needed for comments, etc.?) if (!legacyBuild && !/) { return output; } +// Renders an endpoint request to completion, returning the body. +export async function renderEndpoint(mod: EndpointHandler, params: any) { + const method = 'get'; + const handler = mod[method]; + + if (!handler || typeof handler !== 'function') { + throw new Error(`Endpoint handler not found! Expected an exported function for "${method}"`); + } + + const { body } = await mod.get(params); + + return body; +} + // Calls a component and renders it into a string of HTML export async function renderToString(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any) { const Component = await componentFactory(result, props, children); @@ -419,24 +433,10 @@ const uniqueElements = (item: any, index: number, all: any[]) => { return index === all.findIndex((i) => JSON.stringify(i.props) === props && i.children == children); }; -// Renders an endpoint request to completion, returning the body. -export async function renderEndpoint(mod: EndpointHandler, params: any) { - const method = 'get'; - const handler = mod[method]; - - if (!handler || typeof handler !== 'function') { - throw new Error(`Endpoint handler not found! Expected an exported function for "${method}"`); - } - - const { body } = await mod.get(params); - - return body; -} // Renders a page to completion by first calling the factory callback, waiting for its result, and then appending // styles and scripts into the head. -export async function renderPage(result: SSRResult, Component: AstroComponentFactory, props: any, children: any) { - const template = await renderToString(result, Component, props, children); +export async function renderHead(result: SSRResult) { const styles = Array.from(result.styles) .filter(uniqueElements) .map((style) => { @@ -462,17 +462,10 @@ export async function renderPage(result: SSRResult, Component: AstroComponentFac if (needsHydrationStyles) { styles.push(renderElement('style', { props: { 'astro-style': true }, children: 'astro-root, astro-fragment { display: contents; }' })); } - const links = Array.from(result.links) .filter(uniqueElements) - .map((link) => renderElement('link', link, false)); - - // inject styles & scripts at end of - let headPos = template.indexOf(''); - if (headPos === -1) { - return links.join('\n') + styles.join('\n') + scripts.join('\n') + template; // if no , prepend styles & scripts - } - return template.substring(0, headPos) + links.join('\n') + styles.join('\n') + scripts.join('\n') + template.substring(headPos); + .map((link) => renderElement('link', link)); + return links.join('\n') + styles.join('\n') + scripts.join('\n'); } export async function renderAstroComponent(component: InstanceType) { From eee6fb72d796ca7539794539b496c10769b441fc Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Fri, 25 Feb 2022 17:55:59 -0600 Subject: [PATCH 2/5] feat: remove `layouts` from config, Vite plugin --- packages/astro/src/core/config.ts | 9 --------- packages/astro/src/runtime/server/index.ts | 1 - packages/astro/src/vite-plugin-astro/compile.ts | 4 ---- 3 files changed, 14 deletions(-) diff --git a/packages/astro/src/core/config.ts b/packages/astro/src/core/config.ts index 41898fe281d2..05728527f672 100644 --- a/packages/astro/src/core/config.ts +++ b/packages/astro/src/core/config.ts @@ -26,11 +26,6 @@ export const AstroConfigSchema = z.object({ .optional() .default('./src/pages') .transform((val) => new URL(val)), - layouts: z - .string() - .optional() - .default('./src/layouts') - .transform((val) => new URL(val)), public: z .string() .optional() @@ -101,10 +96,6 @@ export async function validateConfig(userConfig: any, root: string): Promise new URL(addTrailingSlash(val), fileProtocolRoot)), - layouts: z - .string() - .default('./src/layouts') - .transform((val) => new URL(addTrailingSlash(val), fileProtocolRoot)), public: z .string() .default('./public') diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index 7c82ad7d4da5..6e56afb7c7c8 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -441,7 +441,6 @@ export async function renderHead(result: SSRResult) { .filter(uniqueElements) .map((style) => { const styleChildren = !result._metadata.legacyBuild ? '' : style.children; - return renderElement('style', { children: styleChildren, props: { ...style.props, 'astro-style': true }, diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts index d584dc296c5d..7fd4ebe00a67 100644 --- a/packages/astro/src/vite-plugin-astro/compile.ts +++ b/packages/astro/src/vite-plugin-astro/compile.ts @@ -33,11 +33,8 @@ function safelyReplaceImportPlaceholder(code: string) { const configCache = new WeakMap(); async function compile(config: AstroConfig, filename: string, source: string, viteTransform: TransformHook, opts: { ssr: boolean }): Promise { - // pages and layouts should be transformed as full documents (implicit etc) - // everything else is treated as a fragment const filenameURL = new URL(`file://${filename}`); const normalizedID = fileURLToPath(filenameURL); - const isPage = normalizedID.startsWith(fileURLToPath(config.pages)) || normalizedID.startsWith(fileURLToPath(config.layouts)); const pathname = filenameURL.pathname.substr(config.projectRoot.pathname.length - 1); let rawCSSDeps = new Set(); @@ -47,7 +44,6 @@ async function compile(config: AstroConfig, filename: string, source: string, vi // use `sourcemap: "both"` so that sourcemap is included in the code // result passed to esbuild, but also available in the catch handler. const transformResult = await transform(source, { - as: isPage ? 'document' : 'fragment', pathname, projectRoot: config.projectRoot.toString(), site: config.buildOptions.site, From b9adc1d6762e3f90deb5285588670368b60c13f2 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Tue, 1 Mar 2022 16:27:53 -0600 Subject: [PATCH 3/5] fix: improve head injection during rendering --- packages/astro/src/core/render/core.ts | 9 ++++++++- packages/astro/src/runtime/server/index.ts | 12 +++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/core/render/core.ts b/packages/astro/src/core/render/core.ts index ba52ed573f6f..c7ccef245ebe 100644 --- a/packages/astro/src/core/render/core.ts +++ b/packages/astro/src/core/render/core.ts @@ -1,7 +1,7 @@ import type { ComponentInstance, EndpointHandler, MarkdownRenderOptions, Params, Props, Renderer, RouteData, SSRElement } from '../../@types/astro'; import type { LogOptions } from '../logger.js'; -import { renderEndpoint, renderToString } from '../../runtime/server/index.js'; +import { renderEndpoint, renderHead, renderToString } from '../../runtime/server/index.js'; import { getParams } from '../routing/index.js'; import { createResult } from './result.js'; import { findPathItemByKey, RouteCache, callGetStaticPaths } from './route-cache.js'; @@ -99,6 +99,13 @@ export async function render(opts: RenderOptions): Promise { let html = await renderToString(result, Component, pageProps, null); + // handle final head injection if it hasn't happened already + if (html.indexOf("") == -1) { + html = await renderHead(result) + html; + } + // cleanup internal state flags + html = html.replace("", ''); + // inject if missing (TODO: is a more robust check needed for comments, etc.?) if (!legacyBuild && !/\n' + html; diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index 6e56afb7c7c8..d020ce890637 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -71,7 +71,7 @@ export class AstroComponent { const html = htmlParts[i]; const expression = expressions[i]; - yield _render(unescapeHTML(html)); + yield unescapeHTML(html); yield _render(expression); } } @@ -423,7 +423,13 @@ export async function renderEndpoint(mod: EndpointHandler, params: any) { export async function renderToString(result: SSRResult, componentFactory: AstroComponentFactory, props: any, children: any) { const Component = await componentFactory(result, props, children); let template = await renderAstroComponent(Component); - return unescapeHTML(template); + + // injected by compiler + // Must be handled at the end of the rendering process + if (template.indexOf('') > -1) { + template = template.replace('', await renderHead(result)); + } + return template; } // Filter out duplicate elements in our set @@ -464,7 +470,7 @@ export async function renderHead(result: SSRResult) { const links = Array.from(result.links) .filter(uniqueElements) .map((link) => renderElement('link', link)); - return links.join('\n') + styles.join('\n') + scripts.join('\n'); + return unescapeHTML(links.join('\n') + styles.join('\n') + scripts.join('\n') + '\n' + ''); } export async function renderAstroComponent(component: InstanceType) { From 647956692bca5a150061489d92ca8a295822c69b Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 2 Mar 2022 14:03:07 -0600 Subject: [PATCH 4/5] chore: update compiler --- packages/astro/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/astro/package.json b/packages/astro/package.json index 0734e67a6969..23508301ec50 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -56,7 +56,7 @@ "test:match": "mocha --timeout 20000 -g" }, "dependencies": { - "@astrojs/compiler": "^0.12.0-next.5", + "@astrojs/compiler": "^0.12.0-next.8", "@astrojs/language-server": "^0.8.6", "@astrojs/markdown-remark": "^0.6.2", "@astrojs/prism": "0.4.0", diff --git a/yarn.lock b/yarn.lock index 82bebfd65729..b812207e2075 100644 --- a/yarn.lock +++ b/yarn.lock @@ -156,10 +156,10 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@astrojs/compiler@^0.12.0-next.5": - version "0.12.0-next.5" - resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.12.0-next.5.tgz#4e6d27c74787777522395018f2497dab4a032c77" - integrity sha512-4YVPRrB9JJhxoNC9PWN2zpGE7SXRAXcyCouawbd24iyBl4g9aRoQN12XA0qQZkbea9/NNLe9f2yhFMubM2CrJQ== +"@astrojs/compiler@^0.12.0-next.8": + version "0.12.0-next.8" + resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.12.0-next.8.tgz#a15792791790aaeeaf944797635a40a56f806975" + integrity sha512-HeREaw5OR5J7zML+/LxhrqUr57571kyNXL4HD2pU929oevhx3PQ37PQ0FkD5N65X9YfO+gcoEO6whl76vtSZag== dependencies: typescript "^4.3.5" From b499ffd4f4340c8b789553a93c5e3d5d9d1734d3 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 2 Mar 2022 14:08:48 -0600 Subject: [PATCH 5/5] fix: do not escape links --- packages/astro/src/runtime/server/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts index d020ce890637..625c762dec71 100644 --- a/packages/astro/src/runtime/server/index.ts +++ b/packages/astro/src/runtime/server/index.ts @@ -469,7 +469,7 @@ export async function renderHead(result: SSRResult) { } const links = Array.from(result.links) .filter(uniqueElements) - .map((link) => renderElement('link', link)); + .map((link) => renderElement('link', link, false)); return unescapeHTML(links.join('\n') + styles.join('\n') + scripts.join('\n') + '\n' + ''); }