mirror of
https://github.com/withastro/astro.git
synced 2024-12-30 22:03:56 -05:00
wip
This commit is contained in:
parent
f46afa98c6
commit
73a50c9ce2
9 changed files with 182 additions and 67 deletions
|
@ -1,19 +1,30 @@
|
||||||
---
|
---
|
||||||
export async function getStaticPaths() {
|
|
||||||
|
// NOTE: This should be setup(), however the compiler currently only hoists
|
||||||
|
// getStaticPaths(). So we're using getStaticPaths() for the moment.
|
||||||
|
export async function getStaticPaths({content, buildStaticPaths, rss}) {
|
||||||
|
// content() instructions:
|
||||||
|
// To fetch via glob:
|
||||||
|
// console.log(await content('./en/**/*.md'));
|
||||||
|
// To fetch via glob + filter (filtering improves HMR):
|
||||||
|
// console.log(await content('./en/**/*.md', (f) => f.file.includes('getting-started')));
|
||||||
|
|
||||||
// get english pages that moved from `/` to `/en/`
|
// get english pages that moved from `/` to `/en/`
|
||||||
const englishPages = Astro.fetchContent('./en/**/*.md');
|
const englishPages = await content('./en/**/*.md');
|
||||||
|
|
||||||
// add pages that are `*.astro` files as well
|
// add pages that are `*.astro` files as well
|
||||||
const otherPages = [{ url: "/en/themes" }];
|
const otherPages = [{ url: "/en/themes" }];
|
||||||
return [...englishPages, ...otherPages].map((page) => ({
|
buildStaticPaths([...englishPages, ...otherPages].map((page) => ({
|
||||||
params: {
|
params: {
|
||||||
slug: page.url.slice(4),
|
slug: page.url ? page.url.slice(4) : page.file.replace(/^.*src.pages.en/, ''),
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
englishSlug: page.url,
|
englishSlug: page.url ? page.url : page.file.replace(/^.*src.pages/, ''),
|
||||||
}
|
}
|
||||||
}));
|
})));
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
<!--
|
||||||
<meta http-equiv="refresh" content={`0;url=${Astro.props.englishSlug}`} />
|
Commented out so that the page doesn't redirect while testing.
|
||||||
|
<meta http-equiv="refresh" content={`0;url=${Astro.props.englishSlug}`} />
|
||||||
|
-->
|
||||||
|
|
|
@ -173,9 +173,10 @@ export interface ComponentInstance {
|
||||||
$$metadata: Metadata;
|
$$metadata: Metadata;
|
||||||
default: AstroComponentFactory;
|
default: AstroComponentFactory;
|
||||||
css?: string[];
|
css?: string[];
|
||||||
getStaticPaths?: (options: GetStaticPathsOptions) => GetStaticPathsResult;
|
getStaticPaths?: (options: GetStaticPathsOptions) => void | GetStaticPathsResultKeyed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Astro.fetchContent() result
|
* Astro.fetchContent() result
|
||||||
* Docs: https://docs.astro.build/reference/api-reference/#astrofetchcontent
|
* Docs: https://docs.astro.build/reference/api-reference/#astrofetchcontent
|
||||||
|
@ -191,14 +192,28 @@ export type FetchContentResultBase = {
|
||||||
url: URL;
|
url: URL;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NewFetchContentResult<T = unknown> = {
|
||||||
|
file: string,
|
||||||
|
data: T,
|
||||||
|
Content: any,
|
||||||
|
content: {
|
||||||
|
headers: string[];
|
||||||
|
source: string;
|
||||||
|
html: string;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: string | null) => void>;
|
export type GetHydrateCallback = () => Promise<(element: Element, innerHTML: string | null) => void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getStaticPaths() options
|
* getStaticPaths() options
|
||||||
* Docs: https://docs.astro.build/reference/api-reference/#getstaticpaths
|
* Docs: https://docs.astro.build/reference/api-reference/#getstaticpaths
|
||||||
*/ export interface GetStaticPathsOptions {
|
*/
|
||||||
paginate?: PaginateFunction;
|
export interface GetStaticPathsOptions {
|
||||||
rss?: (...args: any[]) => any;
|
content<T = any>(globStr: string, filter?: (data: any) => boolean): Promise<NewFetchContentResult<T>[]>;
|
||||||
|
paginate: PaginateFunction;
|
||||||
|
buildStaticPaths: (paths: GetStaticPathsResultKeyed) => void;
|
||||||
|
rss: (...args: any[]) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetStaticPathsItem = { params: Params; props?: Props };
|
export type GetStaticPathsItem = { params: Params; props?: Props };
|
||||||
|
@ -206,6 +221,12 @@ export type GetStaticPathsResult = GetStaticPathsItem[];
|
||||||
export type GetStaticPathsResultKeyed = GetStaticPathsResult & {
|
export type GetStaticPathsResultKeyed = GetStaticPathsResult & {
|
||||||
keyed: Map<string, GetStaticPathsItem>;
|
keyed: Map<string, GetStaticPathsItem>;
|
||||||
};
|
};
|
||||||
|
export type GetStaticPathsResultObject = {
|
||||||
|
filePath: URL;
|
||||||
|
rss: undefined | RSS;
|
||||||
|
staticPaths: GetStaticPathsResultKeyed; // TODO: if setup(), this is optional
|
||||||
|
linkedContent: string[];
|
||||||
|
};
|
||||||
|
|
||||||
export interface HydrateOptions {
|
export interface HydrateOptions {
|
||||||
value?: string;
|
value?: string;
|
||||||
|
@ -341,7 +362,7 @@ export interface RouteData {
|
||||||
type: 'page';
|
type: 'page';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RouteCache = Record<string, GetStaticPathsResultKeyed>;
|
export type RouteCache = Record<string, GetStaticPathsResultObject>;
|
||||||
|
|
||||||
export type RuntimeMode = 'development' | 'production';
|
export type RuntimeMode = 'development' | 'production';
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,8 @@ import * as colors from 'kleur/colors';
|
||||||
import { debug } from '../logger.js';
|
import { debug } from '../logger.js';
|
||||||
import { preload as ssrPreload } from '../ssr/index.js';
|
import { preload as ssrPreload } from '../ssr/index.js';
|
||||||
import { validateGetStaticPathsModule, validateGetStaticPathsResult } from '../ssr/routing.js';
|
import { validateGetStaticPathsModule, validateGetStaticPathsResult } from '../ssr/routing.js';
|
||||||
import { generatePaginateFunction } from '../ssr/paginate.js';
|
|
||||||
import { generateRssFunction } from '../ssr/rss.js';
|
import { generateRssFunction } from '../ssr/rss.js';
|
||||||
import { assignStaticPaths } from '../ssr/route-cache.js';
|
import { callGetStaticPaths } from '../ssr/route-cache.js';
|
||||||
|
|
||||||
export interface CollectPagesDataOptions {
|
export interface CollectPagesDataOptions {
|
||||||
astroConfig: AstroConfig;
|
astroConfig: AstroConfig;
|
||||||
|
@ -131,9 +130,12 @@ async function getStaticPathsForRoute(opts: CollectPagesDataOptions, route: Rout
|
||||||
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
|
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
|
||||||
validateGetStaticPathsModule(mod);
|
validateGetStaticPathsModule(mod);
|
||||||
const rss = generateRssFunction(astroConfig.buildOptions.site, route);
|
const rss = generateRssFunction(astroConfig.buildOptions.site, route);
|
||||||
await assignStaticPaths(routeCache, route, mod, rss.generator);
|
routeCache[route.component] = routeCache[route.component] || await callGetStaticPaths(filePath, mod, route, (f) => viteServer.ssrLoadModule(f));
|
||||||
const staticPaths = routeCache[route.component];
|
if (routeCache[route.component].rss) {
|
||||||
validateGetStaticPathsResult(staticPaths, logging);
|
rss.generator(routeCache[route.component].rss!);
|
||||||
|
}
|
||||||
|
validateGetStaticPathsResult(routeCache[route.component], logging);
|
||||||
|
const staticPaths = routeCache[route.component].staticPaths;
|
||||||
return {
|
return {
|
||||||
paths: staticPaths.map((staticPath) => staticPath.params && route.generate(staticPath.params)).filter(Boolean),
|
paths: staticPaths.map((staticPath) => staticPath.params && route.generate(staticPath.params)).filter(Boolean),
|
||||||
rss: rss.rss,
|
rss: rss.rss,
|
||||||
|
|
|
@ -27,7 +27,7 @@ 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';
|
import { createResult } from './result.js';
|
||||||
import { assignStaticPaths, ensureRouteCached, findPathItemByKey } from './route-cache.js';
|
import { callGetStaticPaths, findPathItemByKey } from './route-cache.js';
|
||||||
|
|
||||||
const svelteStylesRE = /svelte\?svelte&type=style/;
|
const svelteStylesRE = /svelte\?svelte&type=style/;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ interface SSROptions {
|
||||||
/** the web request (needed for dynamic routes) */
|
/** the web request (needed for dynamic routes) */
|
||||||
pathname: string;
|
pathname: string;
|
||||||
/** optional, in case we need to render something outside of a dev server */
|
/** optional, in case we need to render something outside of a dev server */
|
||||||
route?: RouteData;
|
route: RouteData;
|
||||||
/** pass in route cache because SSR can’t manage cache-busting */
|
/** pass in route cache because SSR can’t manage cache-busting */
|
||||||
routeCache: RouteCache;
|
routeCache: RouteCache;
|
||||||
/** Vite instance */
|
/** Vite instance */
|
||||||
|
@ -130,12 +130,11 @@ ${err.frame}
|
||||||
|
|
||||||
export type ComponentPreload = [Renderer[], ComponentInstance];
|
export type ComponentPreload = [Renderer[], ComponentInstance];
|
||||||
|
|
||||||
export async function preload({ astroConfig, filePath, viteServer }: SSROptions): Promise<ComponentPreload> {
|
export async function preload({ astroConfig, route, routeCache, filePath, viteServer }: SSROptions): Promise<ComponentPreload> {
|
||||||
// Important: This needs to happen first, in case a renderer provides polyfills.
|
// Important: This needs to happen first, in case a renderer provides polyfills.
|
||||||
const renderers = await resolveRenderers(viteServer, astroConfig);
|
const renderers = await resolveRenderers(viteServer, astroConfig);
|
||||||
// Load the module from the Vite SSR Runtime.
|
// Load the module from the Vite SSR Runtime.
|
||||||
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
|
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
|
||||||
|
|
||||||
return [renderers, mod];
|
return [renderers, mod];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,13 +167,13 @@ export async function getParamsAndProps({
|
||||||
validateGetStaticPathsModule(mod);
|
validateGetStaticPathsModule(mod);
|
||||||
}
|
}
|
||||||
if (!routeCache[route.component]) {
|
if (!routeCache[route.component]) {
|
||||||
await assignStaticPaths(routeCache, route, mod);
|
throw new Error(`[${route.component}] Internal error: route cache was empty, but expected to be full.`);
|
||||||
}
|
}
|
||||||
if (validate) {
|
if (validate) {
|
||||||
// This validation is expensive so we only want to do it in dev.
|
// This validation is expensive so we only want to do it in dev.
|
||||||
validateGetStaticPathsResult(routeCache[route.component], logging);
|
validateGetStaticPathsResult(routeCache[route.component], logging);
|
||||||
}
|
}
|
||||||
const staticPaths: GetStaticPathsResultKeyed = routeCache[route.component];
|
const staticPaths: GetStaticPathsResultKeyed = routeCache[route.component].staticPaths;
|
||||||
const paramsKey = JSON.stringify(params);
|
const paramsKey = JSON.stringify(params);
|
||||||
const matchedStaticPath = findPathItemByKey(staticPaths, paramsKey, logging);
|
const matchedStaticPath = findPathItemByKey(staticPaths, paramsKey, logging);
|
||||||
if (!matchedStaticPath) {
|
if (!matchedStaticPath) {
|
||||||
|
@ -204,9 +203,9 @@ export async function render(renderers: Renderer[], mod: ComponentInstance, ssrO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
validateGetStaticPathsModule(mod);
|
validateGetStaticPathsModule(mod);
|
||||||
await ensureRouteCached(routeCache, route, mod);
|
routeCache[route.component] = routeCache[route.component] || (await callGetStaticPaths(filePath, mod, route, (f) => viteServer.ssrLoadModule(f)));
|
||||||
validateGetStaticPathsResult(routeCache[route.component], logging);
|
validateGetStaticPathsResult(routeCache[route.component], logging);
|
||||||
const routePathParams: GetStaticPathsResult = routeCache[route.component];
|
const routePathParams: GetStaticPathsResult = routeCache[route.component].staticPaths;
|
||||||
const matchedStaticPath = routePathParams.find(({ params: _params }) => JSON.stringify(_params) === JSON.stringify(params));
|
const matchedStaticPath = routePathParams.find(({ params: _params }) => JSON.stringify(_params) === JSON.stringify(params));
|
||||||
if (!matchedStaticPath) {
|
if (!matchedStaticPath) {
|
||||||
throw new Error(`[getStaticPaths] route pattern matched, but no matching static path found. (${pathname})`);
|
throw new Error(`[getStaticPaths] route pattern matched, but no matching static path found. (${pathname})`);
|
||||||
|
|
|
@ -1,47 +1,48 @@
|
||||||
import type { ComponentInstance, GetStaticPathsItem, GetStaticPathsResult, GetStaticPathsResultKeyed, RouteCache, RouteData } from '../../@types/astro';
|
import type { ComponentInstance, GetStaticPathsOptions, GetStaticPathsResult, RouteCache, RouteData, GetStaticPathsItem, GetStaticPathsResultKeyed, GetStaticPathsResultObject } from '../../@types/astro';
|
||||||
import type { LogOptions } from '../logger';
|
import type { LogOptions } from '../logger';
|
||||||
|
|
||||||
import { debug } from '../logger.js';
|
import { debug } from '../logger.js';
|
||||||
import { generatePaginateFunction } from '../ssr/paginate.js';
|
import { generatePaginateFunction } from '../ssr/paginate.js';
|
||||||
|
import { createNewFetchContentFn } from '../../runtime/server/content.js';
|
||||||
|
|
||||||
type RSSFn = (...args: any[]) => any;
|
export async function callGetStaticPaths(filePath: URL, mod: ComponentInstance, route: RouteData, loadContent: (filePath: string) => Promise<any>): Promise<GetStaticPathsResultObject> {
|
||||||
|
let result: GetStaticPathsResultObject = {
|
||||||
export async function callGetStaticPaths(mod: ComponentInstance, route: RouteData, rssFn?: RSSFn): Promise<GetStaticPathsResultKeyed> {
|
filePath,
|
||||||
const staticPaths: GetStaticPathsResult = await (
|
rss: undefined,
|
||||||
|
// @ts-expect-error
|
||||||
|
staticPaths: undefined,
|
||||||
|
linkedContent: [],
|
||||||
|
};
|
||||||
|
let staticPaths: GetStaticPathsResult = [];
|
||||||
|
const newFetchContentFn = createNewFetchContentFn(filePath, mod, loadContent);
|
||||||
|
await (
|
||||||
await mod.getStaticPaths!({
|
await mod.getStaticPaths!({
|
||||||
|
content: async (globStr, filter) => {
|
||||||
|
const [fetchContentResults, linkedContentIds] = await newFetchContentFn(globStr, filter);
|
||||||
|
result.linkedContent.push(...linkedContentIds);
|
||||||
|
return fetchContentResults;
|
||||||
|
},
|
||||||
paginate: generatePaginateFunction(route),
|
paginate: generatePaginateFunction(route),
|
||||||
rss:
|
buildStaticPaths: (result) => {
|
||||||
rssFn ||
|
staticPaths = result;
|
||||||
(() => {
|
},
|
||||||
/* noop */
|
rss: (fn) => {
|
||||||
}),
|
result.rss = fn;
|
||||||
|
},
|
||||||
})
|
})
|
||||||
).flat();
|
);
|
||||||
|
|
||||||
const keyedStaticPaths = staticPaths as GetStaticPathsResultKeyed;
|
const keyedStaticPaths: GetStaticPathsResultKeyed = (staticPaths || []) as any;
|
||||||
keyedStaticPaths.keyed = new Map<string, GetStaticPathsItem>();
|
keyedStaticPaths.keyed = new Map<string, GetStaticPathsItem>();
|
||||||
for (const sp of keyedStaticPaths) {
|
for (const sp of keyedStaticPaths) {
|
||||||
const paramsKey = JSON.stringify(sp.params);
|
const paramsKey = JSON.stringify(sp.params);
|
||||||
keyedStaticPaths.keyed.set(paramsKey, sp);
|
keyedStaticPaths.keyed.set(paramsKey, sp);
|
||||||
}
|
}
|
||||||
|
result.staticPaths = keyedStaticPaths;
|
||||||
|
|
||||||
return keyedStaticPaths;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function assignStaticPaths(routeCache: RouteCache, route: RouteData, mod: ComponentInstance, rssFn?: RSSFn): Promise<void> {
|
|
||||||
const staticPaths = await callGetStaticPaths(mod, route, rssFn);
|
|
||||||
routeCache[route.component] = staticPaths;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function ensureRouteCached(routeCache: RouteCache, route: RouteData, mod: ComponentInstance, rssFn?: RSSFn): Promise<GetStaticPathsResultKeyed> {
|
|
||||||
if (!routeCache[route.component]) {
|
|
||||||
const staticPaths = await callGetStaticPaths(mod, route, rssFn);
|
|
||||||
routeCache[route.component] = staticPaths;
|
|
||||||
return staticPaths;
|
|
||||||
} else {
|
|
||||||
return routeCache[route.component];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findPathItemByKey(staticPaths: GetStaticPathsResultKeyed, paramsKey: string, logging: LogOptions) {
|
export function findPathItemByKey(staticPaths: GetStaticPathsResultKeyed, paramsKey: string, logging: LogOptions) {
|
||||||
let matchedStaticPath = staticPaths.keyed.get(paramsKey);
|
let matchedStaticPath = staticPaths.keyed.get(paramsKey);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { AstroConfig, ComponentInstance, GetStaticPathsResult, ManifestData, Params, RouteData } from '../../@types/astro';
|
import type { AstroConfig, ComponentInstance, GetStaticPathsResult, GetStaticPathsResultObject, ManifestData, Params, RouteData } from '../../@types/astro';
|
||||||
import type { LogOptions } from '../logger';
|
import type { LogOptions } from '../logger';
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
@ -45,7 +45,8 @@ export function validateGetStaticPathsModule(mod: ComponentInstance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Throw error for malformed getStaticPaths() response */
|
/** Throw error for malformed getStaticPaths() response */
|
||||||
export function validateGetStaticPathsResult(result: GetStaticPathsResult, logging: LogOptions) {
|
export function validateGetStaticPathsResult(resultObj: GetStaticPathsResultObject, logging: LogOptions) {
|
||||||
|
const result = resultObj.staticPaths;
|
||||||
if (!Array.isArray(result)) {
|
if (!Array.isArray(result)) {
|
||||||
throw new Error(`[getStaticPaths] invalid return value. Expected an array of path objects, but got \`${JSON.stringify(result)}\`.`);
|
throw new Error(`[getStaticPaths] invalid return value. Expected an array of path objects, but got \`${JSON.stringify(result)}\`.`);
|
||||||
}
|
}
|
||||||
|
|
73
packages/astro/src/runtime/server/content.ts
Normal file
73
packages/astro/src/runtime/server/content.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import type { AstroComponentMetadata, Renderer, AstroGlobalPartial, SSRResult, SSRElement, GetStaticPathsOptions, ComponentInstance } from '../../@types/astro';
|
||||||
|
import glob from 'fast-glob';
|
||||||
|
import { pathToFileURL, fileURLToPath } from 'url';
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
|
/** Create the Astro.content() runtime function. */
|
||||||
|
export function createNewFetchContentFn(fileUrl: URL, mod: ComponentInstance, loadContent: (filePath: string) => Promise<any>): any {
|
||||||
|
const fetchResults: string[][] = [];
|
||||||
|
const filePath = fileURLToPath(fileUrl);
|
||||||
|
const cwd = dirname(filePath);
|
||||||
|
console.log(filePath, cwd, mod);
|
||||||
|
return (async (pattern: string, filter?: (data: any) => boolean) => {
|
||||||
|
const files = await glob(pattern, {
|
||||||
|
cwd,
|
||||||
|
absolute: true,
|
||||||
|
// Ignore node_modules by default unless explicitly indicated in the pattern
|
||||||
|
ignore: /(^|\/)node_modules\//.test(pattern) ? [] : ['**/node_modules/**'],
|
||||||
|
});
|
||||||
|
|
||||||
|
// for each file, import it and pass it to filter
|
||||||
|
const modules =
|
||||||
|
(await Promise.all(files.map((f) => loadContent(f))))
|
||||||
|
.map((mod, i) => {
|
||||||
|
// Only return Markdown files for now.
|
||||||
|
if (!mod.frontmatter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filePath = files[i];
|
||||||
|
return {
|
||||||
|
file: filePath,
|
||||||
|
data: mod.frontmatter,
|
||||||
|
Content: mod.default,
|
||||||
|
content: mod.metadata,
|
||||||
|
// TODO: figure out if we want to do the url property
|
||||||
|
// We would need to use some Vite resolution logic, I think
|
||||||
|
// but we may not even want to bring this along
|
||||||
|
// url: urlSpec.includes('/pages/') ? urlSpec.replace(/^.*\/pages\//, site.pathname).replace(/(\/index)?\.md$/, '') : undefined,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(Boolean)
|
||||||
|
.filter(filter || (() => true));
|
||||||
|
console.log(files, modules);
|
||||||
|
|
||||||
|
return [modules as any[], files];
|
||||||
|
});
|
||||||
|
|
||||||
|
// PREVIOUS CODE - to be deleted before merging
|
||||||
|
// let allEntries = [...Object.entries(importMetaGlobResult)];
|
||||||
|
// if (allEntries.length === 0) {
|
||||||
|
// throw new Error(`[${url.pathname}] Astro.fetchContent() no matches found.`);
|
||||||
|
// }
|
||||||
|
// return allEntries
|
||||||
|
// .map(([spec, mod]) => {
|
||||||
|
// // Only return Markdown files for now.
|
||||||
|
// if (!mod.frontmatter) {
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// const urlSpec = new URL(spec, url).pathname;
|
||||||
|
// return {
|
||||||
|
// ...mod.frontmatter,
|
||||||
|
// Content: mod.default,
|
||||||
|
// content: mod.metadata,
|
||||||
|
// file: new URL(spec, url),
|
||||||
|
// url: urlSpec.includes('/pages/') ? urlSpec.replace(/^.*\/pages\//, site.pathname).replace(/(\/index)?\.md$/, '') : undefined,
|
||||||
|
// };
|
||||||
|
// })
|
||||||
|
// .filter(Boolean);
|
||||||
|
// };
|
||||||
|
// // This has to be cast because the type of fetchContent is the type of the function
|
||||||
|
// // that receives the import.meta.glob result, but the user is using it as
|
||||||
|
// // another type.
|
||||||
|
// return fetchContent as unknown as AstroGlobalPartial['fetchContent'];
|
||||||
|
}
|
|
@ -1,5 +1,4 @@
|
||||||
import type { AstroComponentMetadata, Renderer } from '../../@types/astro';
|
import type { AstroComponentMetadata, Renderer, AstroGlobalPartial, SSRResult, SSRElement } from '../../@types/astro';
|
||||||
import type { AstroGlobalPartial, SSRResult, SSRElement } from '../../@types/astro';
|
|
||||||
|
|
||||||
import shorthash from 'shorthash';
|
import shorthash from 'shorthash';
|
||||||
import { extractDirectives, generateHydrateScript } from './hydration.js';
|
import { extractDirectives, generateHydrateScript } from './hydration.js';
|
||||||
|
@ -264,6 +263,7 @@ If you're still stuck, please open an issue on GitHub or join us at https://astr
|
||||||
/** Create the Astro.fetchContent() runtime function. */
|
/** Create the Astro.fetchContent() runtime function. */
|
||||||
function createFetchContentFn(url: URL, site: URL) {
|
function createFetchContentFn(url: URL, site: URL) {
|
||||||
const fetchContent = (importMetaGlobResult: Record<string, any>) => {
|
const fetchContent = (importMetaGlobResult: Record<string, any>) => {
|
||||||
|
//
|
||||||
let allEntries = [...Object.entries(importMetaGlobResult)];
|
let allEntries = [...Object.entries(importMetaGlobResult)];
|
||||||
if (allEntries.length === 0) {
|
if (allEntries.length === 0) {
|
||||||
throw new Error(`[${url.pathname}] Astro.fetchContent() no matches found.`);
|
throw new Error(`[${url.pathname}] Astro.fetchContent() no matches found.`);
|
||||||
|
|
|
@ -128,20 +128,27 @@ export default function createPlugin({ config, logging }: AstroPluginOptions): v
|
||||||
const pagesDirectory = fileURLToPath(config.pages);
|
const pagesDirectory = fileURLToPath(config.pages);
|
||||||
let routeCache: RouteCache = {};
|
let routeCache: RouteCache = {};
|
||||||
let manifest: ManifestData = createRouteManifest({ config: config }, logging);
|
let manifest: ManifestData = createRouteManifest({ config: config }, logging);
|
||||||
/** rebuild the route cache + manifest if the changed file impacts routing. */
|
/** rebuild the route cache + manifest, as needed. */
|
||||||
function rebuildManifestIfNeeded(file: string) {
|
function rebuildManifest(needsManifestRebuild: boolean, file: string) {
|
||||||
if (file.startsWith(pagesDirectory)) {
|
for (const [routeComponent, routeCacheEntry] of Object.entries(routeCache)) {
|
||||||
routeCache = {};
|
if (fileURLToPath(routeCacheEntry.filePath) === file || routeCacheEntry.linkedContent.includes(file)) {
|
||||||
|
delete routeCache[routeComponent];
|
||||||
|
// Vite doesn't give us a way to trigger a page-specific HMR event manually.
|
||||||
|
// Instead, just trigger a full page reload, which should be fine for most users.
|
||||||
|
// The routeCache entry deletion prevents stale pages from reloading.
|
||||||
|
viteServer.ws.send({
|
||||||
|
type: 'full-reload',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needsManifestRebuild) {
|
||||||
manifest = createRouteManifest({ config: config }, logging);
|
manifest = createRouteManifest({ config: config }, logging);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Rebuild route manifest on file change, if needed.
|
// Rebuild route manifest on file change, if needed.
|
||||||
viteServer.watcher.on('add', rebuildManifestIfNeeded);
|
viteServer.watcher.on('add', rebuildManifest.bind(null, true));
|
||||||
viteServer.watcher.on('unlink', rebuildManifestIfNeeded);
|
viteServer.watcher.on('unlink', rebuildManifest.bind(null, true));
|
||||||
// No need to rebuild routes on content-only changes.
|
viteServer.watcher.on('change', rebuildManifest.bind(null, false));
|
||||||
// However, we DO want to clear the cache in case
|
|
||||||
// the change caused a getStaticPaths() return to change.
|
|
||||||
viteServer.watcher.on('change', () => (routeCache = {}));
|
|
||||||
return () => {
|
return () => {
|
||||||
removeViteHttpMiddleware(viteServer.middlewares);
|
removeViteHttpMiddleware(viteServer.middlewares);
|
||||||
viteServer.middlewares.use(async (req, res) => {
|
viteServer.middlewares.use(async (req, res) => {
|
||||||
|
|
Loading…
Reference in a new issue