mirror of
https://github.com/withastro/astro.git
synced 2025-03-24 23:21:57 -05:00
feat(hybrid): Clean logging and misc tweaks for hybrid removal (#11942)
* feat(hybrid): Properly warn on every feature when used in wrong contexts * fix: smoke tests * fix: tests
This commit is contained in:
parent
50a0146e9a
commit
d7e950f35f
22 changed files with 194 additions and 78 deletions
|
@ -43,7 +43,7 @@ test.describe('Custom Client Directives - build server', () => {
|
|||
adapter: testAdapter({
|
||||
extendAdapter: {
|
||||
adapterFeatures: {
|
||||
forceServerOutput: false,
|
||||
buildOutput: 'static',
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
|
|
@ -80,7 +80,6 @@ export async function generatePages(options: StaticBuildOptions, internals: Buil
|
|||
// If we don't delete it here, it's technically not impossible (albeit improbable) for it to leak
|
||||
if (ssr && !hasPrerenderedPages(internals)) {
|
||||
delete globalThis?.astroAsset?.addStaticImage;
|
||||
return;
|
||||
}
|
||||
|
||||
const verb = ssr ? 'prerendering' : 'generating';
|
||||
|
@ -417,7 +416,7 @@ async function generatePath(
|
|||
url,
|
||||
headers: new Headers(),
|
||||
logger,
|
||||
staticLike: true,
|
||||
isPrerendered: true,
|
||||
});
|
||||
const renderContext = RenderContext.create({ pipeline, pathname, request, routeData: route });
|
||||
|
||||
|
|
|
@ -125,7 +125,10 @@ class AstroBuilder {
|
|||
injectImageEndpoint(this.settings, this.manifest, 'build');
|
||||
}
|
||||
|
||||
await runHookConfigDone({ settings: this.settings, logger: logger, command: 'build' });
|
||||
|
||||
// If we're building for the server, we need to ensure that an adapter is installed.
|
||||
// If the adapter installed does not support a server output, an error will be thrown when the adapter is added, so no need to check here.
|
||||
if (!this.settings.config.adapter && this.settings.buildOutput === 'server') {
|
||||
throw new AstroError(AstroErrorData.NoAdapterInstalled);
|
||||
}
|
||||
|
@ -147,7 +150,6 @@ class AstroBuilder {
|
|||
manifest: this.manifest,
|
||||
},
|
||||
);
|
||||
await runHookConfigDone({ settings: this.settings, logger: logger });
|
||||
|
||||
const { syncInternal } = await import('../sync/index.js');
|
||||
await syncInternal({
|
||||
|
@ -239,7 +241,7 @@ class AstroBuilder {
|
|||
logger: this.logger,
|
||||
timeStart: this.timer.init,
|
||||
pageCount: pageNames.length,
|
||||
buildMode: this.settings.config.output,
|
||||
buildMode: this.settings.buildOutput!, // buildOutput is always set at this point
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import path from 'node:path';
|
|||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { teardown } from '@astrojs/compiler';
|
||||
import glob from 'fast-glob';
|
||||
import { bgGreen, bgMagenta, black, green } from 'kleur/colors';
|
||||
import { bgGreen, black, green } from 'kleur/colors';
|
||||
import * as vite from 'vite';
|
||||
import { PROPAGATED_ASSET_FLAG } from '../../content/consts.js';
|
||||
import {
|
||||
|
@ -150,12 +150,11 @@ export async function staticBuild(
|
|||
settings.timer.start('Server generate');
|
||||
await generatePages(opts, internals);
|
||||
await cleanStaticOutput(opts, internals);
|
||||
opts.logger.info(null, `\n${bgMagenta(black(' finalizing server assets '))}\n`);
|
||||
await ssrMoveAssets(opts);
|
||||
settings.timer.end('Server generate');
|
||||
return;
|
||||
}
|
||||
default: // `settings.buildOutput` will always be one of the above, but TS doesn't know that
|
||||
default: // `settings.buildOutput` will always be one of the above at this point, but TS doesn't know that
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
50
packages/astro/src/core/dev/adapter-validation.ts
Normal file
50
packages/astro/src/core/dev/adapter-validation.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { getAdapterStaticRecommendation } from '../../integrations/features-validation.js';
|
||||
import type { AstroSettings } from '../../types/astro.js';
|
||||
import type { AstroAdapter } from '../../types/public/integrations.js';
|
||||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
import type { Logger } from '../logger/core.js';
|
||||
|
||||
let hasWarnedMissingAdapter = false;
|
||||
|
||||
export function warnMissingAdapter(logger: Logger, settings: AstroSettings) {
|
||||
if (hasWarnedMissingAdapter) return;
|
||||
if (settings.buildOutput === 'server' && !settings.config.adapter) {
|
||||
logger.warn(
|
||||
'config',
|
||||
'This project contains server-rendered routes, but no adapter is installed. This is fine for development, but an adapter will be required to build your site for production.',
|
||||
);
|
||||
hasWarnedMissingAdapter = true;
|
||||
}
|
||||
}
|
||||
|
||||
export function validateSetAdapter(
|
||||
logger: Logger,
|
||||
settings: AstroSettings,
|
||||
adapter: AstroAdapter,
|
||||
maybeConflictingIntegration: string,
|
||||
command?: 'dev' | 'build' | string,
|
||||
) {
|
||||
if (settings.adapter && settings.adapter.name !== adapter.name) {
|
||||
throw new Error(
|
||||
`Integration "${maybeConflictingIntegration}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (settings.buildOutput === 'server' && adapter.adapterFeatures?.buildOutput === 'static') {
|
||||
// If the adapter is not compatible with the build output, throw an error
|
||||
if (command === 'build') {
|
||||
const adapterRecommendation = getAdapterStaticRecommendation(adapter.name);
|
||||
|
||||
throw new AstroError({
|
||||
...AstroErrorData.AdapterSupportOutputMismatch,
|
||||
message: AstroErrorData.AdapterSupportOutputMismatch.message(adapter.name),
|
||||
hint: adapterRecommendation ? adapterRecommendation : undefined,
|
||||
});
|
||||
} else if (command === 'dev') {
|
||||
logger.warn(
|
||||
null,
|
||||
`The adapter ${adapter.name} does not support emitting a server output, but the project contain server-rendered pages. Your project will not build correctly.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@ import { apply as applyPolyfill } from '../polyfill.js';
|
|||
import { injectDefaultDevRoutes } from '../routing/dev-default.js';
|
||||
import { createRouteManifest } from '../routing/index.js';
|
||||
import { syncInternal } from '../sync/index.js';
|
||||
import { warnMissingAdapter } from './adapter-validation.js';
|
||||
|
||||
export interface Container {
|
||||
fs: typeof nodeFs;
|
||||
|
@ -87,6 +88,10 @@ export async function createContainer({
|
|||
|
||||
manifest = injectDefaultDevRoutes(settings, devSSRManifest, manifest);
|
||||
|
||||
await runHookConfigDone({ settings, logger, command: 'dev' });
|
||||
|
||||
warnMissingAdapter(logger, settings);
|
||||
|
||||
const viteConfig = await createVite(
|
||||
{
|
||||
mode: 'development',
|
||||
|
@ -107,7 +112,6 @@ export async function createContainer({
|
|||
},
|
||||
);
|
||||
|
||||
await runHookConfigDone({ settings, logger });
|
||||
await syncInternal({
|
||||
settings,
|
||||
logger,
|
||||
|
|
|
@ -73,10 +73,8 @@ export const PrerenderClientAddressNotAvailable = {
|
|||
*/
|
||||
export const StaticClientAddressNotAvailable = {
|
||||
name: 'StaticClientAddressNotAvailable',
|
||||
title: '`Astro.clientAddress` is not available in static mode.',
|
||||
// TODO: Update this for the new static mode? I'm not sure this error can even still happen.
|
||||
message:
|
||||
"`Astro.clientAddress` is only available when using `output: 'server'` or `output: 'hybrid'`. Update your Astro config if you need SSR features.",
|
||||
title: '`Astro.clientAddress` is not available in prerendered pages.',
|
||||
message: '`Astro.clientAddress` is only available on pages that are server-rendered.',
|
||||
hint: 'See https://docs.astro.build/en/guides/server-side-rendering/ for more information on how to enable SSR.',
|
||||
} satisfies ErrorData;
|
||||
/**
|
||||
|
@ -396,6 +394,24 @@ export const NoAdapterInstalled = {
|
|||
message: `Cannot use server-rendered pages without an adapter. Please install and configure the appropriate server adapter for your final deployment.`,
|
||||
hint: 'See https://docs.astro.build/en/guides/server-side-rendering/ for more information.',
|
||||
} satisfies ErrorData;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @see
|
||||
* - [Server-side Rendering](https://docs.astro.build/en/guides/server-side-rendering/)
|
||||
* @description
|
||||
* The currently configured adapter does not support server-side rendering, which is required for the current project setup.
|
||||
*
|
||||
* Depending on your adapter, there may be a different entrypoint to use for server-side rendering. For example, the `@astrojs/vercel` adapter has a `@astrojs/vercel/static` entrypoint for static rendering, and a `@astrojs/vercel/serverless` entrypoint for server-side rendering.
|
||||
*
|
||||
*/
|
||||
export const AdapterSupportOutputMismatch = {
|
||||
name: 'AdapterSupportOutputMismatch',
|
||||
title: 'Adapter does not support server output.',
|
||||
message: (adapterName: string) =>
|
||||
`The \`${adapterName}\` adapter is configured to output a static website, but the project contains server-rendered pages. Please install and configure the appropriate server adapter for your final deployment.`,
|
||||
} satisfies ErrorData;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @description
|
||||
|
|
|
@ -40,7 +40,7 @@ export default async function preview(inlineConfig: AstroInlineConfig): Promise<
|
|||
// Create a route manifest so we can know if the build output is a static site or not
|
||||
await createRouteManifest({ settings: settings, cwd: inlineConfig.root }, logger);
|
||||
|
||||
await runHookConfigDone({ settings: settings, logger: logger });
|
||||
await runHookConfigDone({ settings: settings, logger: logger, command: 'preview' });
|
||||
|
||||
if (settings.buildOutput === 'static') {
|
||||
if (!fs.existsSync(settings.config.outDir)) {
|
||||
|
|
|
@ -480,17 +480,15 @@ export class RenderContext {
|
|||
return Reflect.get(request, clientAddressSymbol) as string;
|
||||
}
|
||||
|
||||
if (pipeline.serverLike) {
|
||||
if (request.body === null) {
|
||||
throw new AstroError(AstroErrorData.PrerenderClientAddressNotAvailable);
|
||||
}
|
||||
if (request.body === null) {
|
||||
throw new AstroError(AstroErrorData.PrerenderClientAddressNotAvailable);
|
||||
}
|
||||
|
||||
if (pipeline.adapterName) {
|
||||
throw new AstroError({
|
||||
...AstroErrorData.ClientAddressNotAvailable,
|
||||
message: AstroErrorData.ClientAddressNotAvailable.message(pipeline.adapterName),
|
||||
});
|
||||
}
|
||||
if (pipeline.adapterName) {
|
||||
throw new AstroError({
|
||||
...AstroErrorData.ClientAddressNotAvailable,
|
||||
message: AstroErrorData.ClientAddressNotAvailable.message(pipeline.adapterName),
|
||||
});
|
||||
}
|
||||
|
||||
throw new AstroError(AstroErrorData.StaticClientAddressNotAvailable);
|
||||
|
|
|
@ -20,7 +20,7 @@ export interface CreateRequestOptions {
|
|||
*
|
||||
* @default false
|
||||
*/
|
||||
staticLike?: boolean;
|
||||
isPrerendered?: boolean;
|
||||
}
|
||||
|
||||
const clientAddressSymbol = Symbol.for('astro.clientAddress');
|
||||
|
@ -41,10 +41,10 @@ export function createRequest({
|
|||
body = undefined,
|
||||
logger,
|
||||
locals,
|
||||
staticLike = false,
|
||||
isPrerendered = false,
|
||||
}: CreateRequestOptions): Request {
|
||||
// headers are made available on the created request only if the request is for a page that will be on-demand rendered
|
||||
const headersObj = staticLike
|
||||
const headersObj = isPrerendered
|
||||
? undefined
|
||||
: headers instanceof Headers
|
||||
? headers
|
||||
|
@ -58,7 +58,8 @@ export function createRequest({
|
|||
|
||||
if (typeof url === 'string') url = new URL(url);
|
||||
|
||||
if (staticLike) {
|
||||
// Remove search parameters if the request is for a page that will be on-demand rendered
|
||||
if (isPrerendered) {
|
||||
url.search = '';
|
||||
}
|
||||
|
||||
|
@ -66,11 +67,11 @@ export function createRequest({
|
|||
method: method,
|
||||
headers: headersObj,
|
||||
// body is made available only if the request is for a page that will be on-demand rendered
|
||||
body: staticLike ? null : body,
|
||||
body: isPrerendered ? null : body,
|
||||
});
|
||||
|
||||
if (staticLike) {
|
||||
// Warn when accessing headers in SSG mode
|
||||
if (isPrerendered) {
|
||||
// Warn when accessing headers in prerendered pages
|
||||
const _headers = request.headers;
|
||||
const headersDesc = Object.getOwnPropertyDescriptor(request, 'headers') || {};
|
||||
Object.defineProperty(request, 'headers', {
|
||||
|
@ -78,7 +79,7 @@ export function createRequest({
|
|||
get() {
|
||||
logger.warn(
|
||||
null,
|
||||
`\`Astro.request.headers\` is unavailable in "static" output mode, and in prerendered pages within "hybrid" and "server" output modes. If you need access to request headers, make sure that \`output\` is configured as either \`"server"\` or \`output: "hybrid"\` in your config file, and that the page accessing the headers is rendered on-demand.`,
|
||||
`\`Astro.request.headers\` is not available on prerendered pages. If you need access to request headers, make sure that the page is server rendered using \`export const prerender = false;\` or by setting \`output\` to \`"server"\` in your Astro config to make all your pages server rendered.`,
|
||||
);
|
||||
return _headers;
|
||||
},
|
||||
|
|
|
@ -8,7 +8,6 @@ import { fileURLToPath } from 'node:url';
|
|||
import { bold } from 'kleur/colors';
|
||||
import pLimit from 'p-limit';
|
||||
import { toRoutingStrategy } from '../../../i18n/utils.js';
|
||||
import { runHookRouteSetup } from '../../../integrations/hooks.js';
|
||||
import { getPrerenderDefault } from '../../../prerender/utils.js';
|
||||
import type { AstroConfig } from '../../../types/public/config.js';
|
||||
import type { RouteData, RoutePart } from '../../../types/public/internal.js';
|
||||
|
@ -20,6 +19,7 @@ import { resolvePages } from '../../util.js';
|
|||
import { routeComparator } from '../priority.js';
|
||||
import { getRouteGenerator } from './generator.js';
|
||||
import { getPattern } from './pattern.js';
|
||||
import { getRoutePrerenderOption } from './prerender.js';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
interface Item {
|
||||
|
@ -506,7 +506,16 @@ export async function createRouteManifest(
|
|||
let promises = [];
|
||||
for (const route of routes) {
|
||||
promises.push(
|
||||
limit(async () => await getRoutePrerenderOption(route, settings, params.fsMod, logger)),
|
||||
limit(async () => {
|
||||
if (route.type !== 'page' && route.type !== 'endpoint') return;
|
||||
const localFs = params.fsMod ?? nodeFs;
|
||||
const content = await localFs.promises.readFile(
|
||||
fileURLToPath(new URL(route.component, settings.config.root)),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
await getRoutePrerenderOption(content, route, settings, logger);
|
||||
}),
|
||||
);
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
@ -714,35 +723,6 @@ export async function createRouteManifest(
|
|||
};
|
||||
}
|
||||
|
||||
async function getRoutePrerenderOption(
|
||||
route: RouteData,
|
||||
settings: AstroSettings,
|
||||
fsMod: typeof nodeFs | undefined,
|
||||
logger: Logger,
|
||||
) {
|
||||
if (route.type !== 'page' && route.type !== 'endpoint') return;
|
||||
const localFs = fsMod ?? nodeFs;
|
||||
const content = await localFs.promises.readFile(
|
||||
fileURLToPath(new URL(route.component, settings.config.root)),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
// Check if the route is pre-rendered or not
|
||||
const match = /^\s*export\s+const\s+prerender\s*=\s*(true|false);?/m.exec(content);
|
||||
if (match) {
|
||||
route.prerender = match[1] === 'true';
|
||||
}
|
||||
|
||||
await runHookRouteSetup({ route, settings, logger });
|
||||
|
||||
// If not explicitly set, default to the global setting
|
||||
if (typeof route.prerender === undefined) {
|
||||
route.prerender = getPrerenderDefault(settings.config);
|
||||
}
|
||||
|
||||
if (!route.prerender) settings.buildOutput = 'server';
|
||||
}
|
||||
|
||||
export function resolveInjectedRoute(entrypoint: string, root: URL, cwd?: string) {
|
||||
let resolved;
|
||||
try {
|
||||
|
|
29
packages/astro/src/core/routing/manifest/prerender.ts
Normal file
29
packages/astro/src/core/routing/manifest/prerender.ts
Normal file
|
@ -0,0 +1,29 @@
|
|||
import { runHookRouteSetup } from '../../../integrations/hooks.js';
|
||||
import { getPrerenderDefault } from '../../../prerender/utils.js';
|
||||
import type { AstroSettings } from '../../../types/astro.js';
|
||||
import type { RouteData } from '../../../types/public/internal.js';
|
||||
import type { Logger } from '../../logger/core.js';
|
||||
|
||||
const PRERENDER_REGEX = /^\s*export\s+const\s+prerender\s*=\s*(true|false);?/m;
|
||||
|
||||
export async function getRoutePrerenderOption(
|
||||
content: string,
|
||||
route: RouteData,
|
||||
settings: AstroSettings,
|
||||
logger: Logger,
|
||||
) {
|
||||
// Check if the route is pre-rendered or not
|
||||
const match = PRERENDER_REGEX.exec(content);
|
||||
if (match) {
|
||||
route.prerender = match[1] === 'true';
|
||||
}
|
||||
|
||||
await runHookRouteSetup({ route, settings, logger });
|
||||
|
||||
// If not explicitly set, default to the global setting
|
||||
if (typeof route.prerender === undefined) {
|
||||
route.prerender = getPrerenderDefault(settings.config);
|
||||
}
|
||||
|
||||
if (!route.prerender) settings.buildOutput = 'server';
|
||||
}
|
|
@ -66,7 +66,7 @@ export default async function sync(
|
|||
logger,
|
||||
});
|
||||
const manifest = await createRouteManifest({ settings, fsMod: fs }, logger);
|
||||
await runHookConfigDone({ settings, logger });
|
||||
await runHookConfigDone({ settings, logger, command: 'sync' });
|
||||
return await syncInternal({ settings, logger, fs, force: inlineConfig.force, manifest });
|
||||
}
|
||||
|
||||
|
|
|
@ -157,3 +157,10 @@ function validateAssetsFeature(
|
|||
|
||||
return validateSupportKind(supportKind, adapterName, logger, 'assets', () => true);
|
||||
}
|
||||
|
||||
export function getAdapterStaticRecommendation(adapterName: string): string | undefined {
|
||||
return {
|
||||
'@astrojs/vercel/static':
|
||||
'Update your configuration to use `@astrojs/vercel/serverless` to unlock server-side rendering capabilities.',
|
||||
}[adapterName];
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import type { SerializedSSRManifest } from '../core/app/types.js';
|
|||
import type { PageBuildData } from '../core/build/types.js';
|
||||
import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js';
|
||||
import { mergeConfig } from '../core/config/index.js';
|
||||
import { validateSetAdapter } from '../core/dev/adapter-validation.js';
|
||||
import type { AstroIntegrationLogger, Logger } from '../core/logger/core.js';
|
||||
import type { AstroSettings } from '../types/astro.js';
|
||||
import type { AstroConfig } from '../types/public/config.js';
|
||||
|
@ -296,9 +297,11 @@ export async function runHookConfigSetup({
|
|||
export async function runHookConfigDone({
|
||||
settings,
|
||||
logger,
|
||||
command,
|
||||
}: {
|
||||
settings: AstroSettings;
|
||||
logger: Logger;
|
||||
command?: 'dev' | 'build' | 'preview' | 'sync';
|
||||
}) {
|
||||
for (const integration of settings.config.integrations) {
|
||||
if (integration?.hooks?.['astro:config:done']) {
|
||||
|
@ -308,13 +311,9 @@ export async function runHookConfigDone({
|
|||
hookResult: integration.hooks['astro:config:done']({
|
||||
config: settings.config,
|
||||
setAdapter(adapter) {
|
||||
if (settings.adapter && settings.adapter.name !== adapter.name) {
|
||||
throw new Error(
|
||||
`Integration "${integration.name}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.`,
|
||||
);
|
||||
}
|
||||
validateSetAdapter(logger, settings, adapter, integration.name, command);
|
||||
|
||||
if (adapter.adapterFeatures?.forceServerOutput) {
|
||||
if (adapter.adapterFeatures?.buildOutput !== 'static') {
|
||||
settings.buildOutput = 'server';
|
||||
}
|
||||
|
||||
|
|
|
@ -64,13 +64,13 @@ export type AdapterSupportsKind = 'unsupported' | 'stable' | 'experimental' | 'd
|
|||
|
||||
export interface AstroAdapterFeatures {
|
||||
/**
|
||||
* Creates an edge function that will communiate with the Astro middleware
|
||||
* Creates an edge function that will communicate with the Astro middleware
|
||||
*/
|
||||
edgeMiddleware: boolean;
|
||||
/**
|
||||
* Force Astro to output a server output, even if all the pages are prerendered
|
||||
* Determine the type of build output the adapter is intended for. Defaults to `server`;
|
||||
*/
|
||||
forceServerOutput?: boolean;
|
||||
buildOutput?: 'static' | 'server';
|
||||
}
|
||||
|
||||
export interface AstroAdapter {
|
||||
|
|
|
@ -3,6 +3,7 @@ import type fs from 'node:fs';
|
|||
import { IncomingMessage } from 'node:http';
|
||||
import type * as vite from 'vite';
|
||||
import type { SSRManifest, SSRManifestI18n } from '../core/app/types.js';
|
||||
import { warnMissingAdapter } from '../core/dev/adapter-validation.js';
|
||||
import { createKey } from '../core/encryption.js';
|
||||
import { getViteErrorPayload } from '../core/errors/dev/index.js';
|
||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||
|
@ -57,6 +58,7 @@ export default function createVitePluginAstroServer({
|
|||
devSSRManifest,
|
||||
await createRouteManifest({ settings, fsMod }, logger), // TODO: Handle partial updates to the manifest
|
||||
);
|
||||
warnMissingAdapter(logger, settings);
|
||||
pipeline.setManifestData(routeManifest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -175,7 +175,7 @@ export async function handleRoute({
|
|||
body,
|
||||
logger,
|
||||
clientAddress: incomingRequest.socket.remoteAddress,
|
||||
staticLike: route.prerender,
|
||||
isPrerendered: route.prerender,
|
||||
});
|
||||
|
||||
// Set user specified headers to response object.
|
||||
|
|
|
@ -3,7 +3,9 @@ import { fileURLToPath } from 'node:url';
|
|||
import { bold } from 'kleur/colors';
|
||||
import type { Plugin as VitePlugin } from 'vite';
|
||||
import { normalizePath } from 'vite';
|
||||
import { warnMissingAdapter } from '../core/dev/adapter-validation.js';
|
||||
import type { Logger } from '../core/logger/core.js';
|
||||
import { getRoutePrerenderOption } from '../core/routing/manifest/prerender.js';
|
||||
import { isEndpoint, isPage } from '../core/util.js';
|
||||
import { rootRelativePath } from '../core/viteUtils.js';
|
||||
import type { AstroSettings, ManifestData } from '../types/astro.js';
|
||||
|
@ -80,5 +82,33 @@ export default function astroScannerPlugin({
|
|||
},
|
||||
};
|
||||
},
|
||||
|
||||
// Handle hot updates to update the prerender option
|
||||
async handleHotUpdate(ctx) {
|
||||
const filename = normalizePath(ctx.file);
|
||||
let fileURL: URL;
|
||||
try {
|
||||
fileURL = new URL(`file://${filename}`);
|
||||
} catch {
|
||||
// If we can't construct a valid URL, exit early
|
||||
return;
|
||||
}
|
||||
|
||||
const fileIsPage = isPage(fileURL, settings);
|
||||
const fileIsEndpoint = isEndpoint(fileURL, settings);
|
||||
if (!(fileIsPage || fileIsEndpoint)) return;
|
||||
|
||||
const route = manifest.routes.find((r) => {
|
||||
const filePath = new URL(`./${r.component}`, settings.config.root);
|
||||
return normalizePath(fileURLToPath(filePath)) === filename;
|
||||
});
|
||||
|
||||
if (!route) {
|
||||
return;
|
||||
}
|
||||
|
||||
await getRoutePrerenderOption(await ctx.read(), route, settings, logger);
|
||||
warnMissingAdapter(logger, settings);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ describe('build assets (server)', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/build-assets/',
|
||||
integrations: [preact()],
|
||||
adapter: testAdapter({ extendAdapter: { adapterFeatures: { forceServerOutput: false } } }),
|
||||
adapter: testAdapter({ extendAdapter: { adapterFeatures: { buildOutput: 'static' } } }),
|
||||
// test suite was authored when inlineStylesheets defaulted to never
|
||||
build: { inlineStylesheets: 'never' },
|
||||
});
|
||||
|
@ -151,7 +151,7 @@ describe('build assets (server)', () => {
|
|||
adapter: testAdapter({
|
||||
extendAdapter: {
|
||||
adapterFeatures: {
|
||||
forceServerOutput: false,
|
||||
buildOutput: 'static',
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
|
|
@ -186,7 +186,7 @@ describe('Static build', () => {
|
|||
it('warns when accessing headers', async () => {
|
||||
let found = false;
|
||||
for (const log of logs) {
|
||||
if (/`Astro\.request\.headers` is unavailable in "static" output mode/.test(log.message)) {
|
||||
if (/`Astro\.request\.headers` is not available on prerendered pages./.test(log.message)) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ export default function ({
|
|||
i18nDomains: 'stable',
|
||||
},
|
||||
adapterFeatures: {
|
||||
forceServerOutput: true,
|
||||
buildOutput: 'server',
|
||||
},
|
||||
...extendAdapter,
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue