Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Warn when getStaticPaths exists without a prerender statement #7713

Merged
merged 13 commits into from
Jul 19, 2023
5 changes: 5 additions & 0 deletions .changeset/sixty-plums-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Update warning when `getStaticPaths` is detected but a route is not prerendered.
2 changes: 1 addition & 1 deletion packages/astro/src/core/create-vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export async function createVite(
mode === 'dev' && astroIntegrationsContainerPlugin({ settings, logging }),
astroScriptsPageSSRPlugin({ settings }),
astroHeadPlugin(),
astroScannerPlugin({ settings }),
astroScannerPlugin({ settings, logging }),
astroInjectEnvTsPlugin({ settings, logging, fs }),
astroContentVirtualModPlugin({ settings }),
astroContentImportPlugin({ fs, settings }),
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/core/render/route-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export async function callGetStaticPaths({
const cached = routeCache.get(route);
if (cached?.staticPaths) return cached.staticPaths;

validateDynamicRouteModule(mod, { ssr, logging, route });
validateDynamicRouteModule(mod, { ssr, route });

// No static paths in SSR mode. Return an empty RouteCacheEntry.
if (ssr && !route.prerender) {
Expand Down
12 changes: 1 addition & 11 deletions packages/astro/src/core/routing/validation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { bold } from 'kleur/colors';
import type { ComponentInstance, GetStaticPathsResult, RouteData } from '../../@types/astro';
import { AstroError, AstroErrorData } from '../errors/index.js';
import type { LogOptions } from '../logger/core';
Expand All @@ -19,26 +18,17 @@ export function validateGetStaticPathsParameter([key, value]: [string, any], rou
}
}

/** Warn or error for deprecated or malformed route components */
/** Error for deprecated or malformed route components */
export function validateDynamicRouteModule(
mod: ComponentInstance,
{
ssr,
logging,
route,
}: {
ssr: boolean;
logging: LogOptions;
route: RouteData;
}
) {
if (ssr && mod.getStaticPaths && !route.prerender) {
warn(
logging,
'getStaticPaths',
`getStaticPaths() in ${bold(route.component)} is ignored when "output: server" is set.`
);
}
Comment on lines -35 to -41
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of checking this at runtime like we do here...

if ((!ssr || route.prerender) && !mod.getStaticPaths) {
throw new AstroError({
...AstroErrorData.GetStaticPathsRequired,
Expand Down
25 changes: 20 additions & 5 deletions packages/astro/src/vite-plugin-scanner/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { normalizePath, type Plugin as VitePlugin } from 'vite';
import type { Plugin as VitePlugin } from 'vite';
import type { AstroSettings } from '../@types/astro.js';
import { isEndpoint, isPage } from '../core/util.js';
import { type LogOptions } from '../core/logger/core.js';

import { getPrerenderDefault } from '../prerender/utils.js';
import { normalizePath } from 'vite';
import { bold } from 'kleur/colors';
import { warn } from '../core/logger/core.js';
import { isEndpoint, isPage, rootRelativePath } from '../core/util.js';
import { getPrerenderDefault, isServerLikeOutput } from '../prerender/utils.js';
import { scan } from './scan.js';

export default function astroScannerPlugin({ settings }: { settings: AstroSettings }): VitePlugin {
export interface AstroPluginScannerOptions {
settings: AstroSettings;
logging: LogOptions;
}

export default function astroScannerPlugin({ settings, logging }: AstroPluginScannerOptions): VitePlugin {
return {
name: 'astro:scanner',
enforce: 'post',
Expand All @@ -26,11 +35,17 @@ export default function astroScannerPlugin({ settings }: { settings: AstroSettin
const fileIsEndpoint = isEndpoint(fileURL, settings);
if (!(fileIsPage || fileIsEndpoint)) return;
const defaultPrerender = getPrerenderDefault(settings.config);
const pageOptions = await scan(code, id, settings.config.output === 'hybrid');
const pageOptions = await scan(code, id, settings);

if (typeof pageOptions.prerender === 'undefined') {
pageOptions.prerender = defaultPrerender;
}

// `getStaticPaths` warning is just a string check, should be good enough for most cases
if (!pageOptions.prerender && isServerLikeOutput(settings.config) && code.includes('getStaticPaths')) {
const reason = ` because \`output: "${settings.config.output}"\` is set`
warn(logging, "getStaticPaths", `The getStaticPaths() statement in ${bold(rootRelativePath(settings.config.root, fileURL, true))} has been ignored${reason}.\n\nAdd \`export const prerender = true;\` to prerender this page.`);
}

const { meta = {} } = this.getModuleInfo(id) ?? {};
return {
Expand Down
10 changes: 7 additions & 3 deletions packages/astro/src/vite-plugin-scanner/scan.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { PageOptions } from '../vite-plugin-astro/types.js';
import type { AstroSettings } from '../@types/astro.js';

import * as eslexer from 'es-module-lexer';
import { AstroError, AstroErrorData } from '../core/errors/index.js';
import type { PageOptions } from '../vite-plugin-astro/types.js';

const BOOLEAN_EXPORTS = new Set(['prerender']);

Expand Down Expand Up @@ -34,14 +36,15 @@ function isFalsy(value: string) {

let didInit = false;

export async function scan(code: string, id: string, isHybridOutput = false): Promise<PageOptions> {
export async function scan(code: string, id: string, settings?: AstroSettings): Promise<PageOptions> {
if (!includesExport(code)) return {};
if (!didInit) {
await eslexer.init;
didInit = true;
}

const [, exports] = eslexer.parse(code, id);

let pageOptions: PageOptions = {};
for (const _export of exports) {
const { n: name, le: endOfLocalName } = _export;
Expand All @@ -62,13 +65,14 @@ export async function scan(code: string, id: string, isHybridOutput = false): Pr
if (prefix !== 'const' || !(isTruthy(suffix) || isFalsy(suffix))) {
throw new AstroError({
...AstroErrorData.InvalidPrerenderExport,
message: AstroErrorData.InvalidPrerenderExport.message(prefix, suffix, isHybridOutput),
message: AstroErrorData.InvalidPrerenderExport.message(prefix, suffix, settings?.config.output === 'hybrid' ?? false),
location: { file: id },
});
} else {
pageOptions[name as keyof PageOptions] = isTruthy(suffix);
}
}
}

return pageOptions;
}