* Revert "Implement hydrated components in the static build (#2260)"
This reverts commit ae5255dd25
.
* Adds a changeset
This commit is contained in:
parent
ae1e61aec8
commit
2e55dc2686
20 changed files with 207 additions and 382 deletions
5
.changeset/ninety-zoos-search.md
Normal file
5
.changeset/ninety-zoos-search.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Rolls back a feature flag feature that was breaking the docs site
|
|
@ -6,7 +6,6 @@
|
||||||
"dev": "astro dev --experimental-static-build",
|
"dev": "astro dev --experimental-static-build",
|
||||||
"start": "astro dev",
|
"start": "astro dev",
|
||||||
"build": "astro build --experimental-static-build",
|
"build": "astro build --experimental-static-build",
|
||||||
"scan-build": "astro build",
|
|
||||||
"preview": "astro preview"
|
"preview": "astro preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -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>
|
|
|
@ -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>
|
|
|
@ -2,7 +2,6 @@
|
||||||
import imgUrl from '../images/penguin.jpg';
|
import imgUrl from '../images/penguin.jpg';
|
||||||
import grayscaleUrl from '../images/random.jpg?grayscale=true';
|
import grayscaleUrl from '../images/random.jpg?grayscale=true';
|
||||||
import Greeting from '../components/Greeting.vue';
|
import Greeting from '../components/Greeting.vue';
|
||||||
import Counter from '../components/Counter.vue';
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
|
@ -27,14 +26,9 @@ import Counter from '../components/Counter.vue';
|
||||||
<Greeting />
|
<Greeting />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h1>ImageTools</h1>
|
<h1>ImageTools</h1>
|
||||||
<img src={grayscaleUrl} />
|
<img src={grayscaleUrl} />
|
||||||
</section>
|
</section>
|
||||||
|
</body>
|
||||||
<section>
|
</html>
|
||||||
<h1>Hydrated component</h1>
|
|
||||||
<Counter client:idle />
|
|
||||||
</section>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
"test": "mocha --parallel --timeout 15000"
|
"test": "mocha --parallel --timeout 15000"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/compiler": "^0.7.0",
|
"@astrojs/compiler": "^0.6.0",
|
||||||
"@astrojs/language-server": "^0.8.2",
|
"@astrojs/language-server": "^0.8.2",
|
||||||
"@astrojs/markdown-remark": "^0.6.0",
|
"@astrojs/markdown-remark": "^0.6.0",
|
||||||
"@astrojs/prism": "0.4.0",
|
"@astrojs/prism": "0.4.0",
|
||||||
|
|
|
@ -371,6 +371,5 @@ export interface SSRResult {
|
||||||
scripts: Set<SSRElement>;
|
scripts: Set<SSRElement>;
|
||||||
links: Set<SSRElement>;
|
links: Set<SSRElement>;
|
||||||
createAstro(Astro: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null): AstroGlobal;
|
createAstro(Astro: AstroGlobalPartial, props: Record<string, any>, slots: Record<string, any> | null): AstroGlobal;
|
||||||
resolve: (s: string) => Promise<string>;
|
|
||||||
_metadata: SSRMetadata;
|
_metadata: SSRMetadata;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,6 @@ export interface BuildInternals {
|
||||||
|
|
||||||
// A mapping to entrypoints (facadeId) to assets (styles) that are added.
|
// A mapping to entrypoints (facadeId) to assets (styles) that are added.
|
||||||
facadeIdToAssetsMap: Map<string, string[]>;
|
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,
|
astroStyleMap,
|
||||||
astroPageStyleMap,
|
astroPageStyleMap,
|
||||||
facadeIdToAssetsMap,
|
facadeIdToAssetsMap,
|
||||||
entrySpecifierToBundleMap: new Map<string, string>(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { OutputChunk, PreRenderedChunk, RollupOutput } from 'rollup';
|
import type { OutputChunk, PreRenderedChunk, RollupOutput } from 'rollup';
|
||||||
import type { Plugin as VitePlugin } from '../vite';
|
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 { AllPagesData } from './types';
|
||||||
import type { LogOptions } from '../logger';
|
import type { LogOptions } from '../logger';
|
||||||
import type { ViteConfigWithSSR } from '../create-vite';
|
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 type { AstroComponentFactory } from '../../runtime/server';
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import npath from 'path';
|
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import glob from 'fast-glob';
|
|
||||||
import vite from '../vite.js';
|
import vite from '../vite.js';
|
||||||
import { debug, info, error } from '../../core/logger.js';
|
import { debug, info, error } from '../../core/logger.js';
|
||||||
import { createBuildInternals } from '../../core/build/internal.js';
|
import { createBuildInternals } from '../../core/build/internal.js';
|
||||||
import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js';
|
import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js';
|
||||||
import { getParamsAndProps } from '../ssr/index.js';
|
import { renderComponent, getParamsAndProps } from '../ssr/index.js';
|
||||||
import { createResult } from '../ssr/result.js';
|
|
||||||
import { renderPage } from '../../runtime/server/index.js';
|
|
||||||
|
|
||||||
export interface StaticBuildOptions {
|
export interface StaticBuildOptions {
|
||||||
allPages: AllPagesData;
|
allPages: AllPagesData;
|
||||||
|
@ -32,11 +28,8 @@ export interface StaticBuildOptions {
|
||||||
export async function staticBuild(opts: StaticBuildOptions) {
|
export async function staticBuild(opts: StaticBuildOptions) {
|
||||||
const { allPages, astroConfig } = opts;
|
const { allPages, astroConfig } = opts;
|
||||||
|
|
||||||
// The pages to be built for rendering purposes.
|
|
||||||
const pageInput = new Set<string>();
|
|
||||||
|
|
||||||
// The JavaScript entrypoints.
|
// 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
|
// A map of each page .astro file, to the PageBuildData which contains information
|
||||||
// about that page, such as its paths.
|
// 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)) {
|
for (const [component, pageData] of Object.entries(allPages)) {
|
||||||
const [renderers, mod] = pageData.preload;
|
const [renderers, mod] = pageData.preload;
|
||||||
const metadata = mod.$$metadata;
|
|
||||||
|
|
||||||
const topLevelImports = new Set([
|
// Hydrated components are statically identified.
|
||||||
// Any component that gets hydrated
|
for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
|
||||||
...metadata.hydratedComponentPaths(),
|
// Note that this part is not yet implemented in the static build.
|
||||||
// Any hydration directive like astro/client/idle.js
|
//jsInput.add(path);
|
||||||
...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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let astroModuleId = new URL('./' + component, astroConfig.projectRoot).pathname;
|
let astroModuleId = new URL('./' + component, astroConfig.projectRoot).pathname;
|
||||||
pageInput.add(astroModuleId);
|
jsInput.add(astroModuleId);
|
||||||
facadeIdToPageDataMap.set(astroModuleId, pageData);
|
facadeIdToPageDataMap.set(astroModuleId, pageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build internals needed by the CSS plugin
|
// Build internals needed by the CSS plugin
|
||||||
const internals = createBuildInternals();
|
const internals = createBuildInternals();
|
||||||
|
|
||||||
// Run the SSR build and client build in parallel
|
// Perform the SSR build
|
||||||
const [ssrResult] = (await Promise.all([ssrBuild(opts, internals, pageInput), clientBuild(opts, internals, jsInput)])) as RollupOutput[];
|
const result = (await ssrBuild(opts, internals, jsInput)) as RollupOutput;
|
||||||
|
|
||||||
// Generate each of the pages.
|
// Generate each of the pages.
|
||||||
await generatePages(ssrResult, opts, internals, facadeIdToPageDataMap);
|
await generatePages(result, opts, internals, facadeIdToPageDataMap);
|
||||||
await cleanSsrOutput(opts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
|
async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
|
||||||
|
@ -83,7 +67,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
build: {
|
build: {
|
||||||
emptyOutDir: true,
|
emptyOutDir: true,
|
||||||
minify: false,
|
minify: false, // 'esbuild', // significantly faster than "terser" but may produce slightly-bigger bundles
|
||||||
outDir: fileURLToPath(astroConfig.dist),
|
outDir: fileURLToPath(astroConfig.dist),
|
||||||
ssr: true,
|
ssr: true,
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
|
@ -95,41 +79,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
|
||||||
target: 'es2020', // must match an esbuild target
|
target: 'es2020', // must match an esbuild target
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vitePluginNewBuild(input, internals, 'mjs'),
|
vitePluginNewBuild(),
|
||||||
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'),
|
|
||||||
rollupPluginAstroBuildCSS({
|
rollupPluginAstroBuildCSS({
|
||||||
internals,
|
internals,
|
||||||
}),
|
}),
|
||||||
|
@ -174,7 +124,6 @@ async function generatePage(output: OutputChunk, opts: StaticBuildOptions, inter
|
||||||
|
|
||||||
const generationOptions: Readonly<GeneratePathOptions> = {
|
const generationOptions: Readonly<GeneratePathOptions> = {
|
||||||
pageData,
|
pageData,
|
||||||
internals,
|
|
||||||
linkIds,
|
linkIds,
|
||||||
Component,
|
Component,
|
||||||
};
|
};
|
||||||
|
@ -187,14 +136,13 @@ async function generatePage(output: OutputChunk, opts: StaticBuildOptions, inter
|
||||||
|
|
||||||
interface GeneratePathOptions {
|
interface GeneratePathOptions {
|
||||||
pageData: PageBuildData;
|
pageData: PageBuildData;
|
||||||
internals: BuildInternals;
|
|
||||||
linkIds: string[];
|
linkIds: string[];
|
||||||
Component: AstroComponentFactory;
|
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 { astroConfig, logging, origin, routeCache } = opts;
|
||||||
const { Component, internals, linkIds, pageData } = gopts;
|
const { Component, linkIds, pageData } = gopts;
|
||||||
|
|
||||||
const [renderers, mod] = pageData.preload;
|
const [renderers, mod] = pageData.preload;
|
||||||
|
|
||||||
|
@ -203,36 +151,14 @@ async function generatePath(pathname: string, opts: StaticBuildOptions, gopts: G
|
||||||
route: pageData.route,
|
route: pageData.route,
|
||||||
routeCache,
|
routeCache,
|
||||||
logging,
|
logging,
|
||||||
pathname,
|
pathname: path,
|
||||||
mod,
|
mod,
|
||||||
});
|
});
|
||||||
|
|
||||||
debug(logging, 'generate', `Generating: ${pathname}`);
|
info(logging, 'generate', `Generating: ${path}`);
|
||||||
|
|
||||||
const result = createResult({ astroConfig, origin, params, pathname, renderers });
|
const html = await renderComponent(renderers, Component, astroConfig, path, origin, params, pageProps, linkIds);
|
||||||
result.links = new Set<SSRElement>(
|
const outFolder = new URL('.' + path + '/', astroConfig.dist);
|
||||||
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 outFile = new URL('./index.html', outFolder);
|
const outFile = new URL('./index.html', outFolder);
|
||||||
await fs.promises.mkdir(outFolder, { recursive: true });
|
await fs.promises.mkdir(outFolder, { recursive: true });
|
||||||
await fs.promises.writeFile(outFile, html, 'utf-8');
|
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) {
|
export function vitePluginNewBuild(): VitePlugin {
|
||||||
// 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 {
|
|
||||||
return {
|
return {
|
||||||
name: '@astro/rollup-plugin-new-build',
|
name: '@astro/rollup-plugin-new-build',
|
||||||
|
|
||||||
|
@ -270,34 +183,13 @@ export function vitePluginNewBuild(input: Set<string>, internals: BuildInternals
|
||||||
outputOptions(outputOptions) {
|
outputOptions(outputOptions) {
|
||||||
Object.assign(outputOptions, {
|
Object.assign(outputOptions, {
|
||||||
entryFileNames(_chunk: PreRenderedChunk) {
|
entryFileNames(_chunk: PreRenderedChunk) {
|
||||||
return 'assets/[name].[hash].' + ext;
|
return 'assets/[name].[hash].mjs';
|
||||||
},
|
},
|
||||||
chunkFileNames(_chunk: PreRenderedChunk) {
|
chunkFileNames(_chunk: PreRenderedChunk) {
|
||||||
return 'assets/[name].[hash].' + ext;
|
return 'assets/[name].[hash].mjs';
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return outputOptions;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,34 @@
|
||||||
import type { BuildResult } from 'esbuild';
|
import type { BuildResult } from 'esbuild';
|
||||||
import type vite from '../vite';
|
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 { LogOptions } from '../logger';
|
||||||
|
import type { AstroComponentFactory } from '../../runtime/server/index';
|
||||||
|
|
||||||
import eol from 'eol';
|
import eol from 'eol';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { renderPage } from '../../runtime/server/index.js';
|
import { renderPage, renderSlot } from '../../runtime/server/index.js';
|
||||||
import { codeFrame, resolveDependency } from '../util.js';
|
import { canonicalURL as getCanonicalURL, codeFrame, resolveDependency } from '../util.js';
|
||||||
import { getStylesForURL } from './css.js';
|
import { getStylesForURL } from './css.js';
|
||||||
import { injectTags } from './html.js';
|
import { injectTags } from './html.js';
|
||||||
import { generatePaginateFunction } from './paginate.js';
|
import { generatePaginateFunction } from './paginate.js';
|
||||||
import { getParams, validateGetStaticPathsModule, validateGetStaticPathsResult } from './routing.js';
|
import { getParams, validateGetStaticPathsModule, validateGetStaticPathsResult } from './routing.js';
|
||||||
import { createResult } from './result.js';
|
|
||||||
|
|
||||||
const svelteStylesRE = /svelte\?svelte&type=style/;
|
const svelteStylesRE = /svelte\?svelte&type=style/;
|
||||||
|
|
||||||
|
@ -124,6 +139,75 @@ export async function preload({ astroConfig, filePath, viteServer }: SSROptions)
|
||||||
return [renderers, mod];
|
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({
|
export async function getParamsAndProps({
|
||||||
route,
|
route,
|
||||||
routeCache,
|
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) 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})`);
|
if (!Component.isAstroComponentFactory) throw new Error(`Unable to SSR non-Astro component (${route?.component})`);
|
||||||
|
|
||||||
const result = createResult({ astroConfig, origin, params, pathname, renderers });
|
// Create the result object that will be passed into the render function.
|
||||||
result.resolve = async (s: string) => {
|
// This object starts here as an empty shell (not yet the result) but then
|
||||||
const [, resolvedPath] = await viteServer.moduleGraph.resolveUrl(s);
|
// calling the render() function will populate the object with scripts, styles, etc.
|
||||||
return resolvedPath;
|
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);
|
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);
|
html = injectTags(html, tags);
|
||||||
|
|
||||||
// run transformIndexHtml() in dev to run Vite dev transformations
|
// 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, '/');
|
const relativeURL = filePath.href.replace(astroConfig.projectRoot.href, '/');
|
||||||
html = await viteServer.transformIndexHtml(relativeURL, html, pathname);
|
html = await viteServer.transformIndexHtml(relativeURL, html, pathname);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
import type { AstroComponentMetadata } from '../../@types/astro';
|
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 { valueToEstree } from 'estree-util-value-to-estree';
|
||||||
import * as astring from 'astring';
|
import * as astring from 'astring';
|
||||||
import { hydrationSpecifier, serializeListValue } from './util.js';
|
import { serializeListValue } from './util.js';
|
||||||
|
|
||||||
const { generate, GENERATOR } = astring;
|
const { generate, GENERATOR } = astring;
|
||||||
|
|
||||||
|
@ -69,11 +69,6 @@ export function extractDirectives(inputProps: Record<string | number, any>): Ext
|
||||||
extracted.hydration.componentExport.value = value;
|
extracted.hydration.componentExport.value = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// This is a special prop added to prove that the client hydration method
|
|
||||||
// was added statically.
|
|
||||||
case 'client:component-hydration': {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
extracted.hydration.directive = key.split(':')[1];
|
extracted.hydration.directive = key.split(':')[1];
|
||||||
extracted.hydration.value = value;
|
extracted.hydration.value = value;
|
||||||
|
@ -98,20 +93,18 @@ export function extractDirectives(inputProps: Record<string | number, any>): Ext
|
||||||
extracted.props[key] = value;
|
extracted.props[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return extracted;
|
return extracted;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HydrateScriptOptions {
|
interface HydrateScriptOptions {
|
||||||
renderer: any;
|
renderer: any;
|
||||||
result: SSRResult;
|
|
||||||
astroId: string;
|
astroId: string;
|
||||||
props: Record<string | number, any>;
|
props: Record<string | number, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** For hydrated components, generate a <script type="module"> to load the component */
|
/** For hydrated components, generate a <script type="module"> to load the component */
|
||||||
export async function generateHydrateScript(scriptOptions: HydrateScriptOptions, metadata: Required<AstroComponentMetadata>): Promise<SSRElement> {
|
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;
|
const { hydrate, componentUrl, componentExport } = metadata;
|
||||||
|
|
||||||
if (!componentExport) {
|
if (!componentExport) {
|
||||||
|
@ -124,9 +117,7 @@ export async function generateHydrateScript(scriptOptions: HydrateScriptOptions,
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrationSource += renderer.source
|
hydrationSource += renderer.source
|
||||||
? `const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${await result.resolve(componentUrl)}"), import("${await result.resolve(
|
? `const [{ ${componentExport.value}: Component }, { default: hydrate }] = await Promise.all([import("${componentUrl}"), import("${renderer.source}")]);
|
||||||
renderer.source
|
|
||||||
)}")]);
|
|
||||||
return (el, children) => hydrate(el)(Component, ${serializeProps(props)}, children);
|
return (el, children) => hydrate(el)(Component, ${serializeProps(props)}, children);
|
||||||
`
|
`
|
||||||
: `await import("${componentUrl}");
|
: `await import("${componentUrl}");
|
||||||
|
@ -135,7 +126,7 @@ export async function generateHydrateScript(scriptOptions: HydrateScriptOptions,
|
||||||
|
|
||||||
const hydrationScript = {
|
const hydrationScript = {
|
||||||
props: { type: 'module', 'data-astro-component-hydration': true },
|
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 () => {
|
setup("${astroId}", {${metadata.hydrateArgs ? `value: ${JSON.stringify(metadata.hydrateArgs)}` : ''}}, async () => {
|
||||||
${hydrationSource}
|
${hydrationSource}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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.
|
// 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.
|
// 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>`;
|
return `<astro-root uid="${astroId}">${html ?? ''}</astro-root>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { hydrationSpecifier } from './util.js';
|
|
||||||
|
|
||||||
interface ModuleInfo {
|
interface ModuleInfo {
|
||||||
module: Record<string, any>;
|
module: Record<string, any>;
|
||||||
specifier: string;
|
specifier: string;
|
||||||
|
@ -10,28 +8,11 @@ interface ComponentMetadata {
|
||||||
componentUrl: string;
|
componentUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateMetadataOptions {
|
|
||||||
modules: ModuleInfo[];
|
|
||||||
hydratedComponents: any[];
|
|
||||||
hydrationDirectives: Set<string>;
|
|
||||||
hoisted: any[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Metadata {
|
export class Metadata {
|
||||||
public fileURL: URL;
|
public fileURL: URL;
|
||||||
public modules: ModuleInfo[];
|
|
||||||
public hoisted: any[];
|
|
||||||
public hydratedComponents: any[];
|
|
||||||
public hydrationDirectives: Set<string>;
|
|
||||||
|
|
||||||
private metadataCache: Map<any, ComponentMetadata | null>;
|
private metadataCache: Map<any, ComponentMetadata | null>;
|
||||||
|
constructor(fileURL: string, public modules: ModuleInfo[], public hydratedComponents: any[], public hoisted: any[]) {
|
||||||
constructor(filePathname: string, opts: CreateMetadataOptions) {
|
this.fileURL = new URL(fileURL);
|
||||||
this.modules = opts.modules;
|
|
||||||
this.hoisted = opts.hoisted;
|
|
||||||
this.hydratedComponents = opts.hydratedComponents;
|
|
||||||
this.hydrationDirectives = opts.hydrationDirectives;
|
|
||||||
this.fileURL = new URL(filePathname, 'http://example.com');
|
|
||||||
this.metadataCache = new Map<any, ComponentMetadata | null>();
|
this.metadataCache = new Map<any, ComponentMetadata | null>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,50 +30,24 @@ export class Metadata {
|
||||||
return metadata?.componentExport || null;
|
return metadata?.componentExport || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Recursively collect all of the hydrated components' paths.
|
||||||
* Gets the paths of all hydrated components within this component
|
getAllHydratedComponentPaths(): Set<string> {
|
||||||
* and children components.
|
const paths = new Set<string>();
|
||||||
*/
|
for (const component of this.hydratedComponents) {
|
||||||
*hydratedComponentPaths() {
|
const path = this.getPath(component);
|
||||||
const found = new Set<string>();
|
if (path) {
|
||||||
for (const metadata of this.deepMetadata()) {
|
paths.add(path);
|
||||||
for (const component of metadata.hydratedComponents) {
|
|
||||||
const path = this.getPath(component);
|
|
||||||
if (path && !found.has(path)) {
|
|
||||||
found.add(path);
|
|
||||||
yield 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) {
|
for (const { module: mod } of this.modules) {
|
||||||
if (typeof mod.$$metadata !== 'undefined') {
|
if (typeof mod.$$metadata !== 'undefined') {
|
||||||
const md = mod.$$metadata as Metadata;
|
for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
|
||||||
// Call children deepMetadata() which will yield the child metadata
|
paths.add(path);
|
||||||
// and any of its children metadatas
|
|
||||||
for (const childMetdata of md.deepMetadata()) {
|
|
||||||
if (!seen.has(childMetdata)) {
|
|
||||||
seen.add(childMetdata);
|
|
||||||
yield childMetdata;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
private getComponentMetadata(Component: any): ComponentMetadata | null {
|
private getComponentMetadata(Component: any): ComponentMetadata | null {
|
||||||
|
@ -128,6 +83,12 @@ export class Metadata {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createMetadata(filePathname: string, options: CreateMetadataOptions) {
|
interface CreateMetadataOptions {
|
||||||
return new Metadata(filePathname, options);
|
modules: ModuleInfo[];
|
||||||
|
hydratedComponents: any[];
|
||||||
|
hoisted: any[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createMetadata(fileURL: string, options: CreateMetadataOptions) {
|
||||||
|
return new Metadata(fileURL, options.modules, options.hydratedComponents, options.hoisted);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
export function serializeListValue(value: any) {
|
||||||
const hash: Record<string, 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`;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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) {
|
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)
|
// pages and layouts should be transformed as full documents (implicit <head> <body> etc)
|
||||||
// everything else is treated as a fragment
|
// everything else is treated as a fragment
|
||||||
const filenameURL = new URL(`file://${filename}`);
|
const normalizedID = fileURLToPath(new URL(`file://${filename}`));
|
||||||
const normalizedID = fileURLToPath(filenameURL);
|
|
||||||
const isPage = normalizedID.startsWith(fileURLToPath(config.pages)) || normalizedID.startsWith(fileURLToPath(config.layouts));
|
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;
|
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.
|
// result passed to esbuild, but also available in the catch handler.
|
||||||
const transformResult = await transform(source, {
|
const transformResult = await transform(source, {
|
||||||
as: isPage ? 'document' : 'fragment',
|
as: isPage ? 'document' : 'fragment',
|
||||||
pathname,
|
|
||||||
projectRoot: config.projectRoot.toString(),
|
projectRoot: config.projectRoot.toString(),
|
||||||
site: config.buildOptions.site,
|
site: config.buildOptions.site,
|
||||||
sourcefile: filename,
|
sourcefile: filename,
|
||||||
|
|
|
@ -69,7 +69,7 @@ export function rollupPluginAstroBuildHTML(options: PluginOptions): VitePlugin {
|
||||||
const [renderers, mod] = pageData.preload;
|
const [renderers, mod] = pageData.preload;
|
||||||
|
|
||||||
// Hydrated components are statically identified.
|
// Hydrated components are statically identified.
|
||||||
for (const path of mod.$$metadata.hydratedComponentPaths()) {
|
for (const path of mod.$$metadata.getAllHydratedComponentPaths()) {
|
||||||
jsInput.add(path);
|
jsInput.add(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,12 +51,8 @@ ${setup}`.trim();
|
||||||
astroResult = `${prelude}\n${astroResult}`;
|
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`
|
// Transform from `.astro` to valid `.ts`
|
||||||
let { code: tsResult } = await transform(astroResult, {
|
let { code: tsResult } = await transform(astroResult, {
|
||||||
pathname,
|
|
||||||
projectRoot: config.projectRoot.toString(),
|
projectRoot: config.projectRoot.toString(),
|
||||||
site: config.buildOptions.site,
|
site: config.buildOptions.site,
|
||||||
sourcefile: id,
|
sourcefile: id,
|
||||||
|
|
|
@ -20,7 +20,7 @@ describe('Client only components', () => {
|
||||||
|
|
||||||
const script = await fixture.readFile(src);
|
const script = await fixture.readFile(src);
|
||||||
// test 2: svelte renderer is on the page
|
// test 2: svelte renderer is on the page
|
||||||
const exp = /import\("(.\/@astrojs_renderer-svelte_client.*)"\)/g;
|
const exp = /import\("(.\/client.*)"\)/g;
|
||||||
let match, svelteRenderer;
|
let match, svelteRenderer;
|
||||||
while ((match = exp.exec(script))) {
|
while ((match = exp.exec(script))) {
|
||||||
svelteRenderer = match[1].replace(/^\./, '/assets/');
|
svelteRenderer = match[1].replace(/^\./, '/assets/');
|
||||||
|
|
|
@ -122,10 +122,10 @@
|
||||||
jsonpointer "^5.0.0"
|
jsonpointer "^5.0.0"
|
||||||
leven "^3.1.0"
|
leven "^3.1.0"
|
||||||
|
|
||||||
"@astrojs/compiler@^0.7.0":
|
"@astrojs/compiler@^0.6.0":
|
||||||
version "0.7.0"
|
version "0.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.7.0.tgz#b2c93c0df6cfe84360043918bb581f8cec1fac31"
|
resolved "https://registry.yarnpkg.com/@astrojs/compiler/-/compiler-0.6.2.tgz#f9f6d2bfabc70921fa2be9da49767f878a1bc1e4"
|
||||||
integrity sha512-lnEdXsGSCMbUfVfXewvYKzTOyHYxsiZHmePNc5YBGjNUvJsFljr4ttMAE958gvwRWltOuaVqc4HuNSx6ylL/hQ==
|
integrity sha512-okzco1cwAPC1Fs1EovCckQpZFLAkuysTM+0qVXQ41fE6mLxmq/4i7fFR7l0Wy/0JapgcRQbK5xN4Y08ku4EPQg==
|
||||||
dependencies:
|
dependencies:
|
||||||
typescript "^4.3.5"
|
typescript "^4.3.5"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue