summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@matthewphillips.info> 2021-12-30 11:52:22 -0500
committerGravatar GitHub <noreply@github.com> 2021-12-30 11:52:22 -0500
commit2e55dc2686b0e2bff2e2ec76c184a17a3d2368c4 (patch)
tree4a3910fd8297d5dbc8fd5560139b1c3e23be7ebd
parentae1e61aec8b1c1ce3ebb4e45359151b584adb1c6 (diff)
downloadastro-2e55dc2686b0e2bff2e2ec76c184a17a3d2368c4.tar.gz
astro-2e55dc2686b0e2bff2e2ec76c184a17a3d2368c4.tar.zst
astro-2e55dc2686b0e2bff2e2ec76c184a17a3d2368c4.zip
Revert "Implement hydrated components in the static build (#2260)" (#2292)
* Revert "Implement hydrated components in the static build (#2260)" This reverts commit ae5255dd25e36da2ce03f515af7b8f1ec6fcac91. * Adds a changeset
-rw-r--r--.changeset/ninety-zoos-search.md5
-rw-r--r--examples/fast-build/package.json1
-rw-r--r--examples/fast-build/src/components/Counter.vue24
-rw-r--r--examples/fast-build/src/pages/[pokemon].astro20
-rw-r--r--examples/fast-build/src/pages/index.astro18
-rw-r--r--packages/astro/package.json2
-rw-r--r--packages/astro/src/@types/astro.ts1
-rw-r--r--packages/astro/src/core/build/internal.ts5
-rw-r--r--packages/astro/src/core/build/static-build.ts152
-rw-r--r--packages/astro/src/core/ssr/index.ts149
-rw-r--r--packages/astro/src/core/ssr/result.ts75
-rw-r--r--packages/astro/src/runtime/server/hydration.ts19
-rw-r--r--packages/astro/src/runtime/server/index.ts2
-rw-r--r--packages/astro/src/runtime/server/metadata.ts79
-rw-r--r--packages/astro/src/runtime/server/util.ts16
-rw-r--r--packages/astro/src/vite-plugin-astro/compile.ts5
-rw-r--r--packages/astro/src/vite-plugin-build-html/index.ts2
-rw-r--r--packages/astro/src/vite-plugin-markdown/index.ts4
-rw-r--r--packages/astro/test/astro-client-only.test.js2
-rw-r--r--yarn.lock8
20 files changed, 207 insertions, 382 deletions
diff --git a/.changeset/ninety-zoos-search.md b/.changeset/ninety-zoos-search.md
new file mode 100644
index 000000000..e5f8d7512
--- /dev/null
+++ b/.changeset/ninety-zoos-search.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Rolls back a feature flag feature that was breaking the docs site
diff --git a/examples/fast-build/package.json b/examples/fast-build/package.json
index a3fcff421..848a01997 100644
--- a/examples/fast-build/package.json
+++ b/examples/fast-build/package.json
@@ -6,7 +6,6 @@
"dev": "astro dev --experimental-static-build",
"start": "astro dev",
"build": "astro build --experimental-static-build",
- "scan-build": "astro build",
"preview": "astro preview"
},
"devDependencies": {
diff --git a/examples/fast-build/src/components/Counter.vue b/examples/fast-build/src/components/Counter.vue
deleted file mode 100644
index 599bcf615..000000000
--- a/examples/fast-build/src/components/Counter.vue
+++ /dev/null
@@ -1,24 +0,0 @@
-<template>
- <div id="vue" class="counter">
- <button @click="subtract()">-</button>
- <pre>{{ count }}</pre>
- <button @click="add()">+</button>
- </div>
-</template>
-
-<script>
-import { ref } from 'vue';
-export default {
- setup() {
- const count = ref(0);
- const add = () => (count.value = count.value + 1);
- const subtract = () => (count.value = count.value - 1);
-
- return {
- count,
- add,
- subtract,
- };
- },
-};
-</script>
diff --git a/examples/fast-build/src/pages/[pokemon].astro b/examples/fast-build/src/pages/[pokemon].astro
deleted file mode 100644
index ea01cc4f7..000000000
--- a/examples/fast-build/src/pages/[pokemon].astro
+++ /dev/null
@@ -1,20 +0,0 @@
----
-import Greeting from '../components/Greeting.vue';
-
-export async function getStaticPaths() {
- const response = await fetch(`https://pokeapi.co/api/v2/pokemon?limit=2000`);
- const result = await response.json();
- const allPokemon = result.results;
- return allPokemon.map(pokemon => ({params: {pokemon: pokemon.name}, props: {pokemon}}));
-}
----
-<html lang="en">
- <head>
- <title>Hello</title>
- </head>
-
- <body>
- <h1>{Astro.props.pokemon.name}</h1>
- <Greeting client:load />
- </body>
-</html> \ No newline at end of file
diff --git a/examples/fast-build/src/pages/index.astro b/examples/fast-build/src/pages/index.astro
index ee228aa19..2bdadbf5b 100644
--- a/examples/fast-build/src/pages/index.astro
+++ b/examples/fast-build/src/pages/index.astro
@@ -2,7 +2,6 @@
import imgUrl from '../images/penguin.jpg';
import grayscaleUrl from '../images/random.jpg?grayscale=true';
import Greeting from '../components/Greeting.vue';
-import Counter from '../components/Counter.vue';
---
<html>
@@ -27,14 +26,9 @@ import Counter from '../components/Counter.vue';
<Greeting />
</section>
- <section>
- <h1>ImageTools</h1>
- <img src={grayscaleUrl} />
- </section>
-
- <section>
- <h1>Hydrated component</h1>
- <Counter client:idle />
- </section>
-</body>
-</html> \ No newline at end of file
+ <section>
+ <h1>ImageTools</h1>
+ <img src={grayscaleUrl} />
+ </section>
+ </body>
+</html>
diff --git a/packages/astro/package.json b/packages/astro/package.json
index 6b6b521d1..eda3c7bef 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -56,7 +56,7 @@
"test": "mocha --parallel --timeout 15000"
},
"dependencies": {
- "@astrojs/compiler": "^0.7.0",
+ "@astrojs/compiler": "^0.6.0",
"@astrojs/language-server": "^0.8.2",
"@astrojs/markdown-remark": "^0.6.0",
"@astrojs/prism": "0.4.0",
diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts
index 2e60da3b7..f6f47cd98 100644
--- a/packages/astro/src/@types/astro.ts
+++ b/packages/astro/src/@types/astro.ts
@@ -371,6 +371,5 @@ export interface SSRResult {
scripts: Set<SSRElement>;
links: Set<SSRElement>;
createAstro(Astro: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null): AstroGlobal;
- resolve: (s: string) => Promise<string>;
_metadata: SSRMetadata;
}
diff --git a/packages/astro/src/core/build/internal.ts b/packages/astro/src/core/build/internal.ts
index ee379f4e3..b30ea7ddf 100644
--- a/packages/astro/src/core/build/internal.ts
+++ b/packages/astro/src/core/build/internal.ts
@@ -14,10 +14,6 @@ export interface BuildInternals {
// A mapping to entrypoints (facadeId) to assets (styles) that are added.
facadeIdToAssetsMap: Map<string, string[]>;
-
- // A mapping of specifiers like astro/client/idle.js to the hashed bundled name.
- // Used to render pages with the correct specifiers.
- entrySpecifierToBundleMap: Map<string, string>;
}
/**
@@ -45,6 +41,5 @@ export function createBuildInternals(): BuildInternals {
astroStyleMap,
astroPageStyleMap,
facadeIdToAssetsMap,
- entrySpecifierToBundleMap: new Map<string, string>(),
};
}
diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts
index 07e97c7ca..191cdb987 100644
--- a/packages/astro/src/core/build/static-build.ts
+++ b/packages/astro/src/core/build/static-build.ts
@@ -1,6 +1,6 @@
import type { OutputChunk, PreRenderedChunk, RollupOutput } from 'rollup';
import type { Plugin as VitePlugin } from '../vite';
-import type { AstroConfig, RouteCache, SSRElement } from '../../@types/astro';
+import type { AstroConfig, RouteCache } from '../../@types/astro';
import type { AllPagesData } from './types';
import type { LogOptions } from '../logger';
import type { ViteConfigWithSSR } from '../create-vite';
@@ -9,16 +9,12 @@ import type { BuildInternals } from '../../core/build/internal.js';
import type { AstroComponentFactory } from '../../runtime/server';
import fs from 'fs';
-import npath from 'path';
import { fileURLToPath } from 'url';
-import glob from 'fast-glob';
import vite from '../vite.js';
import { debug, info, error } from '../../core/logger.js';
import { createBuildInternals } from '../../core/build/internal.js';
import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js';
-import { getParamsAndProps } from '../ssr/index.js';
-import { createResult } from '../ssr/result.js';
-import { renderPage } from '../../runtime/server/index.js';
+import { renderComponent, getParamsAndProps } from '../ssr/index.js';
export interface StaticBuildOptions {
allPages: AllPagesData;
@@ -32,11 +28,8 @@ export interface StaticBuildOptions {
export async function staticBuild(opts: StaticBuildOptions) {
const { allPages, astroConfig } = opts;
- // The pages to be built for rendering purposes.
- const pageInput = new Set<string>();
-
// The JavaScript entrypoints.
- const jsInput = new Set<string>();
+ const jsInput: Set<string> = new Set();
// A map of each page .astro file, to the PageBuildData which contains information
// about that page, such as its paths.
@@ -44,35 +37,26 @@ export async function staticBuild(opts: StaticBuildOptions) {
for (const [component, pageData] of Object.entries(allPages)) {
const [renderers, mod] = pageData.preload;
- const metadata = mod.$$metadata;
-
- const topLevelImports = new Set([
- // Any component that gets hydrated
- ...metadata.hydratedComponentPaths(),
- // Any hydration directive like astro/client/idle.js
- ...metadata.hydrationDirectiveSpecifiers(),
- // The client path for each renderer
- ...renderers.filter((renderer) => !!renderer.source).map((renderer) => renderer.source!),
- ]);
- for (const specifier of topLevelImports) {
- jsInput.add(specifier);
+ // Hydrated components are statically identified.
+ for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
+ // Note that this part is not yet implemented in the static build.
+ //jsInput.add(path);
}
let astroModuleId = new URL('./' + component, astroConfig.projectRoot).pathname;
- pageInput.add(astroModuleId);
+ jsInput.add(astroModuleId);
facadeIdToPageDataMap.set(astroModuleId, pageData);
}
// Build internals needed by the CSS plugin
const internals = createBuildInternals();
- // Run the SSR build and client build in parallel
- const [ssrResult] = (await Promise.all([ssrBuild(opts, internals, pageInput), clientBuild(opts, internals, jsInput)])) as RollupOutput[];
+ // Perform the SSR build
+ const result = (await ssrBuild(opts, internals, jsInput)) as RollupOutput;
// Generate each of the pages.
- await generatePages(ssrResult, opts, internals, facadeIdToPageDataMap);
- await cleanSsrOutput(opts);
+ await generatePages(result, opts, internals, facadeIdToPageDataMap);
}
async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
@@ -83,7 +67,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
mode: 'production',
build: {
emptyOutDir: true,
- minify: false,
+ minify: false, // 'esbuild', // significantly faster than "terser" but may produce slightly-bigger bundles
outDir: fileURLToPath(astroConfig.dist),
ssr: true,
rollupOptions: {
@@ -95,41 +79,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
target: 'es2020', // must match an esbuild target
},
plugins: [
- vitePluginNewBuild(input, internals, 'mjs'),
- rollupPluginAstroBuildCSS({
- internals,
- }),
- ...(viteConfig.plugins || []),
- ],
- publicDir: viteConfig.publicDir,
- root: viteConfig.root,
- envPrefix: 'PUBLIC_',
- server: viteConfig.server,
- base: astroConfig.buildOptions.site ? new URL(astroConfig.buildOptions.site).pathname : '/',
- });
-}
-
-async function clientBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
- const { astroConfig, viteConfig } = opts;
-
- return await vite.build({
- logLevel: 'error',
- mode: 'production',
- build: {
- emptyOutDir: false,
- minify: 'esbuild',
- outDir: fileURLToPath(astroConfig.dist),
- rollupOptions: {
- input: Array.from(input),
- output: {
- format: 'esm',
- },
- preserveEntrySignatures: 'exports-only',
- },
- target: 'es2020', // must match an esbuild target
- },
- plugins: [
- vitePluginNewBuild(input, internals, 'js'),
+ vitePluginNewBuild(),
rollupPluginAstroBuildCSS({
internals,
}),
@@ -174,7 +124,6 @@ async function generatePage(output: OutputChunk, opts: StaticBuildOptions, inter
const generationOptions: Readonly<GeneratePathOptions> = {
pageData,
- internals,
linkIds,
Component,
};
@@ -187,14 +136,13 @@ async function generatePage(output: OutputChunk, opts: StaticBuildOptions, inter
interface GeneratePathOptions {
pageData: PageBuildData;
- internals: BuildInternals;
linkIds: string[];
Component: AstroComponentFactory;
}
-async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: GeneratePathOptions) {
+async function generatePath(path: string, opts: StaticBuildOptions, gopts: GeneratePathOptions) {
const { astroConfig, logging, origin, routeCache } = opts;
- const { Component, internals, linkIds, pageData } = gopts;
+ const { Component, linkIds, pageData } = gopts;
const [renderers, mod] = pageData.preload;
@@ -203,36 +151,14 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G
route: pageData.route,
routeCache,
logging,
- pathname,
+ pathname: path,
mod,
});
- debug(logging, 'generate', `Generating: ${pathname}`);
+ info(logging, 'generate', `Generating: ${path}`);
- const result = createResult({ astroConfig, origin, params, pathname, renderers });
- result.links = new Set<SSRElement>(
- linkIds.map((href) => ({
- props: {
- rel: 'stylesheet',
- href,
- },
- children: '',
- }))
- );
- // Override the `resolve` method so that hydrated components are given the
- // hashed filepath to the component.
- result.resolve = async (specifier: string) => {
- const hashedFilePath = internals.entrySpecifierToBundleMap.get(specifier);
- if (typeof hashedFilePath !== 'string') {
- throw new Error(`Cannot find the built path for ${specifier}`);
- }
- const relPath = npath.posix.relative(pathname, '/' + hashedFilePath);
- const fullyRelativePath = relPath[0] === '.' ? relPath : './' + relPath;
- return fullyRelativePath;
- };
-
- let html = await renderPage(result, Component, pageProps, null);
- const outFolder = new URL('.' + pathname + '/', astroConfig.dist);
+ const html = await renderComponent(renderers, Component, astroConfig, path, origin, params, pageProps, linkIds);
+ const outFolder = new URL('.' + path + '/', astroConfig.dist);
const outFile = new URL('./index.html', outFolder);
await fs.promises.mkdir(outFolder, { recursive: true });
await fs.promises.writeFile(outFile, html, 'utf-8');
@@ -241,20 +167,7 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G
}
}
-async function cleanSsrOutput(opts: StaticBuildOptions) {
- // The SSR output is all .mjs files, the client output is not.
- const files = await glob('**/*.mjs', {
- cwd: opts.astroConfig.dist.pathname,
- });
- await Promise.all(
- files.map(async (filename) => {
- const url = new URL(filename, opts.astroConfig.dist);
- await fs.promises.rm(url);
- })
- );
-}
-
-export function vitePluginNewBuild(input: Set<string>, internals: BuildInternals, ext: 'js' | 'mjs'): VitePlugin {
+export function vitePluginNewBuild(): VitePlugin {
return {
name: '@astro/rollup-plugin-new-build',
@@ -270,34 +183,13 @@ export function vitePluginNewBuild(input: Set<string>, internals: BuildInternals
outputOptions(outputOptions) {
Object.assign(outputOptions, {
entryFileNames(_chunk: PreRenderedChunk) {
- return 'assets/[name].[hash].' + ext;
+ return 'assets/[name].[hash].mjs';
},
chunkFileNames(_chunk: PreRenderedChunk) {
- return 'assets/[name].[hash].' + ext;
+ return 'assets/[name].[hash].mjs';
},
});
return outputOptions;
},
-
- async generateBundle(_options, bundle) {
- const promises = [];
- const mapping = new Map<string, string>();
- for (const specifier of input) {
- promises.push(
- this.resolve(specifier).then((result) => {
- if (result) {
- mapping.set(result.id, specifier);
- }
- })
- );
- }
- await Promise.all(promises);
- for (const [, chunk] of Object.entries(bundle)) {
- if (chunk.type === 'chunk' && chunk.facadeModuleId && mapping.has(chunk.facadeModuleId)) {
- const specifier = mapping.get(chunk.facadeModuleId)!;
- internals.entrySpecifierToBundleMap.set(specifier, chunk.fileName);
- }
- }
- },
};
}
diff --git a/packages/astro/src/core/ssr/index.ts b/packages/astro/src/core/ssr/index.ts
index d9b56b270..b53411b91 100644
--- a/packages/astro/src/core/ssr/index.ts
+++ b/packages/astro/src/core/ssr/index.ts
@@ -1,19 +1,34 @@
import type { BuildResult } from 'esbuild';
import type vite from '../vite';
-import type { AstroConfig, ComponentInstance, GetStaticPathsResult, Params, Props, Renderer, RouteCache, RouteData, RuntimeMode, SSRElement, SSRError } from '../../@types/astro';
+import type {
+ AstroConfig,
+ AstroGlobal,
+ AstroGlobalPartial,
+ ComponentInstance,
+ GetStaticPathsResult,
+ Params,
+ Props,
+ Renderer,
+ RouteCache,
+ RouteData,
+ RuntimeMode,
+ SSRElement,
+ SSRError,
+ SSRResult,
+} from '../../@types/astro';
import type { LogOptions } from '../logger';
+import type { AstroComponentFactory } from '../../runtime/server/index';
import eol from 'eol';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
-import { renderPage } from '../../runtime/server/index.js';
-import { codeFrame, resolveDependency } from '../util.js';
+import { renderPage, renderSlot } from '../../runtime/server/index.js';
+import { canonicalURL as getCanonicalURL, codeFrame, resolveDependency } from '../util.js';
import { getStylesForURL } from './css.js';
import { injectTags } from './html.js';
import { generatePaginateFunction } from './paginate.js';
import { getParams, validateGetStaticPathsModule, validateGetStaticPathsResult } from './routing.js';
-import { createResult } from './result.js';
const svelteStylesRE = /svelte\?svelte&type=style/;
@@ -124,6 +139,75 @@ export async function preload({ astroConfig, filePath, viteServer }: SSROptions)
return [renderers, mod];
}
+export async function renderComponent(
+ renderers: Renderer[],
+ Component: AstroComponentFactory,
+ astroConfig: AstroConfig,
+ pathname: string,
+ origin: string,
+ params: Params,
+ pageProps: Props,
+ links: string[] = []
+): Promise<string> {
+ const _links = new Set<SSRElement>(
+ links.map((href) => ({
+ props: {
+ rel: 'stylesheet',
+ href,
+ },
+ children: '',
+ }))
+ );
+ const result: SSRResult = {
+ styles: new Set<SSRElement>(),
+ scripts: new Set<SSRElement>(),
+ links: _links,
+ /** This function returns the `Astro` faux-global */
+ createAstro(astroGlobal: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null) {
+ const site = new URL(origin);
+ const url = new URL('.' + pathname, site);
+ const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin);
+ return {
+ __proto__: astroGlobal,
+ props,
+ request: {
+ canonicalURL,
+ params,
+ url,
+ },
+ slots: Object.fromEntries(Object.entries(slots || {}).map(([slotName]) => [slotName, true])),
+ // This is used for <Markdown> but shouldn't be used publicly
+ privateRenderSlotDoNotUse(slotName: string) {
+ return renderSlot(result, slots ? slots[slotName] : null);
+ },
+ // <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
+ async privateRenderMarkdownDoNotUse(content: string, opts: any) {
+ let mdRender = astroConfig.markdownOptions.render;
+ let renderOpts = {};
+ if (Array.isArray(mdRender)) {
+ renderOpts = mdRender[1];
+ mdRender = mdRender[0];
+ }
+ if (typeof mdRender === 'string') {
+ ({ default: mdRender } = await import(mdRender));
+ }
+ const { code } = await mdRender(content, { ...renderOpts, ...(opts ?? {}) });
+ return code;
+ },
+ } as unknown as AstroGlobal;
+ },
+ _metadata: {
+ renderers,
+ pathname,
+ experimentalStaticBuild: astroConfig.buildOptions.experimentalStaticBuild,
+ },
+ };
+
+ let html = await renderPage(result, Component, pageProps, null);
+
+ return html;
+}
+
export async function getParamsAndProps({
route,
routeCache,
@@ -208,10 +292,57 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
if (!Component) throw new Error(`Expected an exported Astro component but received typeof ${typeof Component}`);
if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`);
- const result = createResult({ astroConfig, origin, params, pathname, renderers });
- result.resolve = async (s: string) => {
- const [, resolvedPath] = await viteServer.moduleGraph.resolveUrl(s);
- return resolvedPath;
+ // Create the result object that will be passed into the render function.
+ // This object starts here as an empty shell (not yet the result) but then
+ // calling the render() function will populate the object with scripts, styles, etc.
+ const result: SSRResult = {
+ styles: new Set<SSRElement>(),
+ scripts: new Set<SSRElement>(),
+ links: new Set<SSRElement>(),
+ /** This function returns the `Astro` faux-global */
+ createAstro(astroGlobal: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null) {
+ const site = new URL(origin);
+ const url = new URL('.' + pathname, site);
+ const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin);
+ return {
+ __proto__: astroGlobal,
+ props,
+ request: {
+ canonicalURL,
+ params,
+ url,
+ },
+ slots: Object.fromEntries(Object.entries(slots || {}).map(([slotName]) => [slotName, true])),
+ // This is used for <Markdown> but shouldn't be used publicly
+ privateRenderSlotDoNotUse(slotName: string) {
+ return renderSlot(result, slots ? slots[slotName] : null);
+ },
+ // <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
+ async privateRenderMarkdownDoNotUse(content: string, opts: any) {
+ let mdRender = astroConfig.markdownOptions.render;
+ let renderOpts = {};
+ if (Array.isArray(mdRender)) {
+ renderOpts = mdRender[1];
+ mdRender = mdRender[0];
+ }
+ // ['rehype-toc', opts]
+ if (typeof mdRender === 'string') {
+ ({ default: mdRender } = await import(mdRender));
+ }
+ // [import('rehype-toc'), opts]
+ else if (mdRender instanceof Promise) {
+ ({ default: mdRender } = await mdRender);
+ }
+ const { code } = await mdRender(content, { ...renderOpts, ...(opts ?? {}) });
+ return code;
+ },
+ } as unknown as AstroGlobal;
+ },
+ _metadata: {
+ renderers,
+ pathname,
+ experimentalStaticBuild: astroConfig.buildOptions.experimentalStaticBuild,
+ },
};
let html = await renderPage(result, Component, pageProps, null);
@@ -256,7 +387,7 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
html = injectTags(html, tags);
// run transformIndexHtml() in dev to run Vite dev transformations
- if (mode === 'development' && !astroConfig.buildOptions.experimentalStaticBuild) {
+ if (mode === 'development') {
const relativeURL = filePath.href.replace(astroConfig.projectRoot.href, '/');
html = await viteServer.transformIndexHtml(relativeURL, html, pathname);
}
diff --git a/packages/astro/src/core/ssr/result.ts b/packages/astro/src/core/ssr/result.ts
deleted file mode 100644
index 32694dd05..000000000
--- a/packages/astro/src/core/ssr/result.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import type { AstroConfig, AstroGlobal, AstroGlobalPartial, Params, Renderer, SSRElement, SSRResult } from '../../@types/astro';
-
-import { canonicalURL as getCanonicalURL } from '../util.js';
-import { renderSlot } from '../../runtime/server/index.js';
-
-export interface CreateResultArgs {
- astroConfig: AstroConfig;
- origin: string;
- params: Params;
- pathname: string;
- renderers: Renderer[];
-}
-
-export function createResult(args: CreateResultArgs): SSRResult {
- const { astroConfig, origin, params, pathname, renderers } = args;
-
- // Create the result object that will be passed into the render function.
- // This object starts here as an empty shell (not yet the result) but then
- // calling the render() function will populate the object with scripts, styles, etc.
- const result: SSRResult = {
- styles: new Set<SSRElement>(),
- scripts: new Set<SSRElement>(),
- links: new Set<SSRElement>(),
- /** This function returns the `Astro` faux-global */
- createAstro(astroGlobal: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null) {
- const site = new URL(origin);
- const url = new URL('.' + pathname, site);
- const canonicalURL = getCanonicalURL('.' + pathname, astroConfig.buildOptions.site || origin);
- return {
- __proto__: astroGlobal,
- props,
- request: {
- canonicalURL,
- params,
- url,
- },
- slots: Object.fromEntries(Object.entries(slots || {}).map(([slotName]) => [slotName, true])),
- // This is used for <Markdown> but shouldn't be used publicly
- privateRenderSlotDoNotUse(slotName: string) {
- return renderSlot(result, slots ? slots[slotName] : null);
- },
- // <Markdown> also needs the same `astroConfig.markdownOptions.render` as `.md` pages
- async privateRenderMarkdownDoNotUse(content: string, opts: any) {
- let mdRender = astroConfig.markdownOptions.render;
- let renderOpts = {};
- if (Array.isArray(mdRender)) {
- renderOpts = mdRender[1];
- mdRender = mdRender[0];
- }
- // ['rehype-toc', opts]
- if (typeof mdRender === 'string') {
- ({ default: mdRender } = await import(mdRender));
- }
- // [import('rehype-toc'), opts]
- else if (mdRender instanceof Promise) {
- ({ default: mdRender } = await mdRender);
- }
- const { code } = await mdRender(content, { ...renderOpts, ...(opts ?? {}) });
- return code;
- },
- } as unknown as AstroGlobal;
- },
- // This is a stub and will be implemented by dev and build.
- async resolve(s: string): Promise<string> {
- return '';
- },
- _metadata: {
- renderers,
- pathname,
- experimentalStaticBuild: astroConfig.buildOptions.experimentalStaticBuild,
- },
- };
-
- return result;
-}
diff --git a/packages/astro/src/runtime/server/hydration.ts b/packages/astro/src/runtime/server/hydration.ts
index f83632bdd..fd71287dd 100644
--- a/packages/astro/src/runtime/server/hydration.ts
+++ b/packages/astro/src/runtime/server/hydration.ts
@@ -1,8 +1,8 @@
import type { AstroComponentMetadata } from '../../@types/astro';
-import type { SSRElement, SSRResult } from '../../@types/astro';
+import type { SSRElement } from '../../@types/astro';
import { valueToEstree } from 'estree-util-value-to-estree';
import * as astring from 'astring';
-import { hydrationSpecifier, serializeListValue } from './util.js';
+import { serializeListValue } from './util.js';
const { generate, GENERATOR } = astring;
@@ -69,11 +69,6 @@ export function extractDirectives(inputProps: Record<string | number, any>): Ext
extracted.hydration.componentExport.value = value;
break;
}
- // This is a special prop added to prove that the client hydration method
- // was added statically.
- case 'client:component-hydration': {
- break;
- }
default: {
extracted.hydration.directive = key.split(':')[1];
extracted.hydration.value = value;
@@ -98,20 +93,18 @@ export function extractDirectives(inputProps: Record<string | number, any>): Ext
extracted.props[key] = value;
}
}
-
return extracted;
}
interface HydrateScriptOptions {
renderer: any;
- result: SSRResult;
astroId: string;
props: Record<string | number, any>;
}
/** For hydrated components, generate a <script type="module"> to load the component */
export async function generateHydrateScript(scriptOptions: HydrateScriptOptions, metadata: Required<AstroComponentMetadata>): Promise<SSRElement> {
- const { renderer, result, astroId, props } = scriptOptions;
+ const { renderer, astroId, props } = scriptOptions;
const { hydrate, componentUrl, componentExport } = metadata;
if (!componentExport) {
@@ -124,9 +117,7 @@ export async function generateHydrateScript(scriptOptions: HydrateScriptOptions,
}
hydrationSource += renderer.source
- ? `const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${await result.resolve(componentUrl)}"), import("${await result.resolve(
- renderer.source
- )}")]);
+ ? `const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${componentUrl}"), import("${renderer.source}")]);
return (el, children) => hydrate(el)(Component, ${serializeProps(props)}, children);
`
: `await import("${componentUrl}");
@@ -135,7 +126,7 @@ export async function generateHydrateScript(scriptOptions: HydrateScriptOptions,
const hydrationScript = {
props: { type: 'module', 'data-astro-component-hydration': true },
- children: `import setup from '${await result.resolve(hydrationSpecifier(hydrate))}';
+ children: `import setup from 'astro/client/${hydrate}.js';
setup("${astroId}", {${metadata.hydrateArgs ? `value: ${JSON.stringify(metadata.hydrateArgs)}` : ''}}, async () => {
${hydrationSource}
});
diff --git a/packages/astro/src/runtime/server/index.ts b/packages/astro/src/runtime/server/index.ts
index 7fe0a174e..7bbba1a4b 100644
--- a/packages/astro/src/runtime/server/index.ts
+++ b/packages/astro/src/runtime/server/index.ts
@@ -249,7 +249,7 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
// Rather than appending this inline in the page, puts this into the `result.scripts` set that will be appended to the head.
// INVESTIGATE: This will likely be a problem in streaming because the `<head>` will be gone at this point.
- result.scripts.add(await generateHydrateScript({ renderer, result, astroId, props }, metadata as Required<AstroComponentMetadata>));
+ result.scripts.add(await generateHydrateScript({ renderer, astroId, props }, metadata as Required<AstroComponentMetadata>));
return `<astro-root uid="${astroId}">${html ?? ''}</astro-root>`;
}
diff --git a/packages/astro/src/runtime/server/metadata.ts b/packages/astro/src/runtime/server/metadata.ts
index f012105b1..b4454e16f 100644
--- a/packages/astro/src/runtime/server/metadata.ts
+++ b/packages/astro/src/runtime/server/metadata.ts
@@ -1,5 +1,3 @@
-import { hydrationSpecifier } from './util.js';
-
interface ModuleInfo {
module: Record<string, any>;
specifier: string;
@@ -10,28 +8,11 @@ interface ComponentMetadata {
componentUrl: string;
}
-interface CreateMetadataOptions {
- modules: ModuleInfo[];
- hydratedComponents: any[];
- hydrationDirectives: Set<string>;
- hoisted: any[];
-}
-
export class Metadata {
public fileURL: URL;
- public modules: ModuleInfo[];
- public hoisted: any[];
- public hydratedComponents: any[];
- public hydrationDirectives: Set<string>;
-
private metadataCache: Map<any, ComponentMetadata | null>;
-
- constructor(filePathname: string, opts: CreateMetadataOptions) {
- this.modules = opts.modules;
- this.hoisted = opts.hoisted;
- this.hydratedComponents = opts.hydratedComponents;
- this.hydrationDirectives = opts.hydrationDirectives;
- this.fileURL = new URL(filePathname, 'http://example.com');
+ constructor(fileURL: string, public modules: ModuleInfo[], public hydratedComponents: any[], public hoisted: any[]) {
+ this.fileURL = new URL(fileURL);
this.metadataCache = new Map<any, ComponentMetadata | null>();
}
@@ -49,50 +30,24 @@ export class Metadata {
return metadata?.componentExport || null;
}
- /**
- * Gets the paths of all hydrated components within this component
- * and children components.
- */
- *hydratedComponentPaths() {
- const found = new Set<string>();
- for (const metadata of this.deepMetadata()) {
- for (const component of metadata.hydratedComponents) {
- const path = this.getPath(component);
- if (path && !found.has(path)) {
- found.add(path);
- yield path;
- }
+ // Recursively collect all of the hydrated components' paths.
+ getAllHydratedComponentPaths(): Set<string> {
+ const paths = new Set<string>();
+ for (const component of this.hydratedComponents) {
+ const path = this.getPath(component);
+ if (path) {
+ paths.add(path);
}
}
- }
- /**
- * Gets all of the hydration specifiers used within this component.
- */
- *hydrationDirectiveSpecifiers() {
- for (const directive of this.hydrationDirectives) {
- yield hydrationSpecifier(directive);
- }
- }
-
- private *deepMetadata(): Generator<Metadata, void, unknown> {
- // Yield self
- yield this;
- // Keep a Set of metadata objects so we only yield them out once.
- const seen = new Set<Metadata>();
for (const { module: mod } of this.modules) {
if (typeof mod.$$metadata !== 'undefined') {
- const md = mod.$$metadata as Metadata;
- // Call children deepMetadata() which will yield the child metadata
- // and any of its children metadatas
- for (const childMetdata of md.deepMetadata()) {
- if (!seen.has(childMetdata)) {
- seen.add(childMetdata);
- yield childMetdata;
- }
+ for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
+ paths.add(path);
}
}
}
+ return paths;
}
private getComponentMetadata(Component: any): ComponentMetadata | null {
@@ -128,6 +83,12 @@ export class Metadata {
}
}
-export function createMetadata(filePathname: string, options: CreateMetadataOptions) {
- return new Metadata(filePathname, options);
+interface CreateMetadataOptions {
+ modules: ModuleInfo[];
+ hydratedComponents: any[];
+ hoisted: any[];
+}
+
+export function createMetadata(fileURL: string, options: CreateMetadataOptions) {
+ return new Metadata(fileURL, options.modules, options.hydratedComponents, options.hoisted);
}
diff --git a/packages/astro/src/runtime/server/util.ts b/packages/astro/src/runtime/server/util.ts
index 22f38f970..62a0da388 100644
--- a/packages/astro/src/runtime/server/util.ts
+++ b/packages/astro/src/runtime/server/util.ts
@@ -1,10 +1,3 @@
-function formatList(values: string[]): string {
- if (values.length === 1) {
- return values[0];
- }
- return `${values.slice(0, -1).join(', ')} or ${values[values.length - 1]}`;
-}
-
export function serializeListValue(value: any) {
const hash: Record<string, any> = {};
@@ -34,12 +27,3 @@ export function serializeListValue(value: any) {
}
}
}
-
-/**
- * Get the import specifier for a given hydration directive.
- * @param hydrate The hydration directive such as `idle` or `visible`
- * @returns
- */
-export function hydrationSpecifier(hydrate: string) {
- return `astro/client/${hydrate}.js`;
-}
diff --git a/packages/astro/src/vite-plugin-astro/compile.ts b/packages/astro/src/vite-plugin-astro/compile.ts
index 4c6ceccd7..868230037 100644
--- a/packages/astro/src/vite-plugin-astro/compile.ts
+++ b/packages/astro/src/vite-plugin-astro/compile.ts
@@ -29,10 +29,8 @@ function isSSR(options: undefined | boolean | { ssr: boolean }): boolean {
async function compile(config: AstroConfig, filename: string, source: string, viteTransform: TransformHook, opts: boolean | undefined) {
// pages and layouts should be transformed as full documents (implicit <head> <body> etc)
// everything else is treated as a fragment
- const filenameURL = new URL(`file://${filename}`);
- const normalizedID = fileURLToPath(filenameURL);
+ const normalizedID = fileURLToPath(new URL(`file://${filename}`));
const isPage = normalizedID.startsWith(fileURLToPath(config.pages)) || normalizedID.startsWith(fileURLToPath(config.layouts));
- const pathname = filenameURL.pathname.substr(config.projectRoot.pathname.length - 1);
let cssTransformError: Error | undefined;
@@ -41,7 +39,6 @@ async function compile(config: AstroConfig, filename: string, source: string, vi
// 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,
sourcefile: filename,
diff --git a/packages/astro/src/vite-plugin-build-html/index.ts b/packages/astro/src/vite-plugin-build-html/index.ts
index 0c92d5a44..c4d998191 100644
--- a/packages/astro/src/vite-plugin-build-html/index.ts
+++ b/packages/astro/src/vite-plugin-build-html/index.ts
@@ -69,7 +69,7 @@ export function rollupPluginAstroBuildHTML(options: PluginOptions): VitePlugin {
const [renderers, mod] = pageData.preload;
// Hydrated components are statically identified.
- for (const path of mod.$$metadata.hydratedComponentPaths()) {
+ for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
jsInput.add(path);
}
diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts
index 9b3ccaca8..e3d977066 100644
--- a/packages/astro/src/vite-plugin-markdown/index.ts
+++ b/packages/astro/src/vite-plugin-markdown/index.ts
@@ -51,12 +51,8 @@ ${setup}`.trim();
astroResult = `${prelude}\n${astroResult}`;
}
- const filenameURL = new URL(`file://${id}`);
- const pathname = filenameURL.pathname.substr(config.projectRoot.pathname.length - 1);
-
// Transform from `.astro` to valid `.ts`
let { code: tsResult } = await transform(astroResult, {
- pathname,
projectRoot: config.projectRoot.toString(),
site: config.buildOptions.site,
sourcefile: id,
diff --git a/packages/astro/test/astro-client-only.test.js b/packages/astro/test/astro-client-only.test.js
index 72ba0a672..2f767ba5b 100644
--- a/packages/astro/test/astro-client-only.test.js
+++ b/packages/astro/test/astro-client-only.test.js
@@ -20,7 +20,7 @@ describe('Client only components', () => {
const script = await fixture.readFile(src);
// test 2: svelte renderer is on the page
- const exp = /import\("(.\/@astrojs_renderer-svelte_client.*)"\)/g;
+ const exp = /import\("(.\/client.*)"\)/g;
let match, svelteRenderer;
while ((match = exp.exec(script))) {
svelteRenderer = match[1].replace(/^\./, '/assets/');
diff --git a/yarn.lock b/yarn.lock
index 0e2c658ce..05637a995 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -122,10 +122,10 @@
jsonpointer "^5.0.0"
leven "^3.1.0"
-"@astrojs/compiler@^0.7.0":
- version "0.7.0"
- resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.7.0.tgz#b2c93c0df6cfe84360043918bb581f8cec1fac31"
- integrity sha512-lnEdXsGSCMbUfVfXewvYKzTOyHYxsiZHmePNc5YBGjNUvJsFljr4ttMAE958gvwRWltOuaVqc4HuNSx6ylL/hQ==
+"@astrojs/compiler@^0.6.0":
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.6.2.tgz#f9f6d2bfabc70921fa2be9da49767f878a1bc1e4"
+ integrity sha512-okzco1cwAPC1Fs1EovCckQpZFLAkuysTM+0qVXQ41fE6mLxmq/4i7fFR7l0Wy/0JapgcRQbK5xN4Y08ku4EPQg==
dependencies:
typescript "^4.3.5"