From a33f3abd01dadcc6709002df8158b9432753a6e7 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:31:05 +0200 Subject: [PATCH 01/27] feat: merge hybrid and static --- .../fixtures/actions-blog/astro.config.mjs | 2 +- .../actions-react-19/astro.config.mjs | 2 +- .../fixtures/server-islands/astro.config.mjs | 2 +- .../view-transitions/astro.config.mjs | 2 +- packages/astro/src/actions/index.ts | 4 +-- packages/astro/src/assets/build/generate.ts | 8 ++--- .../astro/src/assets/vite-plugin-assets.ts | 5 ++- .../content/vite-plugin-content-imports.ts | 5 ++- .../vite-plugin-content-virtual-mod.ts | 3 +- packages/astro/src/core/build/common.ts | 15 ++++---- packages/astro/src/core/build/generate.ts | 10 +++--- packages/astro/src/core/build/index.ts | 13 +++++-- packages/astro/src/core/build/page-data.ts | 2 +- packages/astro/src/core/build/pipeline.ts | 9 ++--- .../src/core/build/plugins/plugin-content.ts | 4 +-- .../src/core/build/plugins/plugin-manifest.ts | 2 +- .../core/build/plugins/plugin-prerender.ts | 2 +- .../src/core/build/plugins/plugin-ssr.ts | 4 +-- packages/astro/src/core/build/static-build.ts | 35 ++++++++----------- packages/astro/src/core/config/schema.ts | 2 +- packages/astro/src/core/config/settings.ts | 1 + packages/astro/src/core/errors/errors-data.ts | 1 + .../astro/src/core/middleware/vite-plugin.ts | 2 +- .../astro/src/core/routing/manifest/create.ts | 18 ++++++++++ packages/astro/src/core/util.ts | 14 ++------ .../src/integrations/features-validation.ts | 2 +- packages/astro/src/integrations/hooks.ts | 19 +++++----- packages/astro/src/prerender/utils.ts | 11 +++--- packages/astro/src/types/astro.ts | 5 +++ .../src/vite-plugin-astro-server/pipeline.ts | 6 ++-- .../src/vite-plugin-astro-server/route.ts | 2 +- .../astro/src/vite-plugin-scanner/index.ts | 7 ++-- .../astro/src/vite-plugin-scanner/scan.ts | 2 +- .../server-islands/hybrid/astro.config.mjs | 3 +- packages/astro/test/i18n-routing.test.js | 2 +- .../ssr-prerender-get-static-paths.test.js | 2 +- .../test/underscore-in-folder-name.test.js | 2 +- .../astro/test/units/integrations/api.test.js | 10 +++--- .../test/units/routing/route-matching.test.js | 2 +- .../units/routing/route-sanitization.test.js | 2 +- packages/db/src/core/integration/index.ts | 6 +--- .../integrations/vercel/src/lib/prerender.ts | 5 --- .../integrations/vercel/src/static/adapter.ts | 5 --- packages/integrations/web-vitals/src/index.ts | 14 ++++---- 44 files changed, 138 insertions(+), 136 deletions(-) delete mode 100644 packages/integrations/vercel/src/lib/prerender.ts diff --git a/packages/astro/e2e/fixtures/actions-blog/astro.config.mjs b/packages/astro/e2e/fixtures/actions-blog/astro.config.mjs index acbed1768b3c..b2d1e29819af 100644 --- a/packages/astro/e2e/fixtures/actions-blog/astro.config.mjs +++ b/packages/astro/e2e/fixtures/actions-blog/astro.config.mjs @@ -7,7 +7,7 @@ import node from '@astrojs/node'; export default defineConfig({ site: 'https://example.com', integrations: [db(), react()], - output: 'hybrid', + output: 'static', adapter: node({ mode: 'standalone', }), diff --git a/packages/astro/e2e/fixtures/actions-react-19/astro.config.mjs b/packages/astro/e2e/fixtures/actions-react-19/astro.config.mjs index acbed1768b3c..b2d1e29819af 100644 --- a/packages/astro/e2e/fixtures/actions-react-19/astro.config.mjs +++ b/packages/astro/e2e/fixtures/actions-react-19/astro.config.mjs @@ -7,7 +7,7 @@ import node from '@astrojs/node'; export default defineConfig({ site: 'https://example.com', integrations: [db(), react()], - output: 'hybrid', + output: 'static', adapter: node({ mode: 'standalone', }), diff --git a/packages/astro/e2e/fixtures/server-islands/astro.config.mjs b/packages/astro/e2e/fixtures/server-islands/astro.config.mjs index 2175a1bf8fe9..6b3c2a146eac 100644 --- a/packages/astro/e2e/fixtures/server-islands/astro.config.mjs +++ b/packages/astro/e2e/fixtures/server-islands/astro.config.mjs @@ -6,7 +6,7 @@ import nodejs from '@astrojs/node'; // https://astro.build/config export default defineConfig({ base: '/base', - output: 'hybrid', + output: 'static', adapter: nodejs({ mode: 'standalone' }), integrations: [react(), mdx()], trailingSlash: process.env.TRAILING_SLASH ?? 'always', diff --git a/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs b/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs index b7cfd434abb4..4fdc70ff8725 100644 --- a/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs +++ b/packages/astro/e2e/fixtures/view-transitions/astro.config.mjs @@ -6,7 +6,7 @@ import { defineConfig } from 'astro/config'; // https://astro.build/config export default defineConfig({ - output: 'hybrid', + output: 'static', adapter: nodejs({ mode: 'standalone' }), integrations: [react(),vue(),svelte()], redirects: { diff --git a/packages/astro/src/actions/index.ts b/packages/astro/src/actions/index.ts index 61f5a00cc3ab..029bf1133914 100644 --- a/packages/astro/src/actions/index.ts +++ b/packages/astro/src/actions/index.ts @@ -2,7 +2,7 @@ import fsMod from 'node:fs'; import type { Plugin as VitePlugin } from 'vite'; import { ActionsWithoutServerOutputError } from '../core/errors/errors-data.js'; import { AstroError } from '../core/errors/errors.js'; -import { isServerLikeOutput, viteID } from '../core/util.js'; +import { viteID } from '../core/util.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroIntegration } from '../types/public/integrations.js'; import { @@ -25,7 +25,7 @@ export default function astroActions({ name: VIRTUAL_MODULE_ID, hooks: { async 'astro:config:setup'(params) { - if (!isServerLikeOutput(params.config)) { + if (settings.buildOutput !== 'server') { const error = new AstroError(ActionsWithoutServerOutputError); error.stack = undefined; throw error; diff --git a/packages/astro/src/assets/build/generate.ts b/packages/astro/src/assets/build/generate.ts index 1c4866592828..c38ebb75a359 100644 --- a/packages/astro/src/assets/build/generate.ts +++ b/packages/astro/src/assets/build/generate.ts @@ -9,7 +9,6 @@ import { AstroError } from '../../core/errors/errors.js'; import { AstroErrorData } from '../../core/errors/index.js'; import type { Logger } from '../../core/logger/core.js'; import { isRemotePath, removeLeadingForwardSlash } from '../../core/path.js'; -import { isServerLikeOutput } from '../../core/util.js'; import type { MapValue } from '../../type-utils.js'; import type { AstroConfig } from '../../types/public/config.js'; import { getConfiguredImageService } from '../internal.js'; @@ -50,7 +49,7 @@ export async function prepareAssetsGenerationEnv( pipeline: BuildPipeline, totalCount: number, ): Promise { - const { config, logger } = pipeline; + const { config, logger, settings } = pipeline; let useCache = true; const assetsCacheDir = new URL('assets/', config.cacheDir); const count = { total: totalCount, current: 1 }; @@ -66,8 +65,9 @@ export async function prepareAssetsGenerationEnv( useCache = false; } + const isServerOutput = settings.buildOutput === 'server'; let serverRoot: URL, clientRoot: URL; - if (isServerLikeOutput(config)) { + if (isServerOutput) { serverRoot = config.build.server; clientRoot = config.build.client; } else { @@ -77,7 +77,7 @@ export async function prepareAssetsGenerationEnv( return { logger, - isSSR: isServerLikeOutput(config), + isSSR: isServerOutput, count, useCache, assetsCacheDir, diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index f7c541eb6265..5c40d98b4f8e 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -10,7 +10,6 @@ import { removeBase, removeQueryString, } from '../core/path.js'; -import { isServerLikeOutput } from '../core/util.js'; import type { AstroPluginOptions, AstroSettings } from '../types/astro.js'; import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from './consts.js'; import type { ImageTransform } from './types.js'; @@ -131,7 +130,7 @@ export default function assets({ // so that it's tree-shaken away for all platforms that don't need it. export const outDir = /* #__PURE__ */ new URL(${JSON.stringify( new URL( - isServerLikeOutput(settings.config) + settings.buildOutput === 'server' ? settings.config.build.client : settings.config.outDir, ), @@ -222,7 +221,7 @@ export default function assets({ if (options?.ssr) { return `export default ${getProxyCode( imageMetadata, - isServerLikeOutput(settings.config), + settings.buildOutput === 'server', )}`; } else { globalThis.astroAsset.referencedImages.add(imageMetadata.fsPath); diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts index 62f129052a8d..4950d2d9b2e7 100644 --- a/packages/astro/src/content/vite-plugin-content-imports.ts +++ b/packages/astro/src/content/vite-plugin-content-imports.ts @@ -8,7 +8,6 @@ import { getProxyCode } from '../assets/utils/proxy.js'; import { AstroError } from '../core/errors/errors.js'; import { AstroErrorData } from '../core/errors/index.js'; import type { Logger } from '../core/logger/core.js'; -import { isServerLikeOutput } from '../core/util.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroConfig } from '../types/public/config.js'; import type { @@ -115,7 +114,7 @@ export function astroContentImportPlugin({ const code = ` export const id = ${JSON.stringify(id)}; export const collection = ${JSON.stringify(collection)}; -export const data = ${stringifyEntryData(data, isServerLikeOutput(settings.config))}; +export const data = ${stringifyEntryData(data, settings.buildOutput === 'server')}; export const _internal = { type: 'data', filePath: ${JSON.stringify(_internal.filePath)}, @@ -140,7 +139,7 @@ export const _internal = { export const collection = ${JSON.stringify(collection)}; export const slug = ${JSON.stringify(slug)}; export const body = ${JSON.stringify(body)}; - export const data = ${stringifyEntryData(data, isServerLikeOutput(settings.config))}; + export const data = ${stringifyEntryData(data, settings.buildOutput === 'server')}; export const _internal = { type: 'content', filePath: ${JSON.stringify(_internal.filePath)}, diff --git a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts index 832ca0b171ad..2be051d5bc68 100644 --- a/packages/astro/src/content/vite-plugin-content-virtual-mod.ts +++ b/packages/astro/src/content/vite-plugin-content-virtual-mod.ts @@ -8,7 +8,6 @@ import type { Plugin } from 'vite'; import { encodeName } from '../core/build/util.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { appendForwardSlash, removeFileExtension } from '../core/path.js'; -import { isServerLikeOutput } from '../core/util.js'; import { rootRelativePath } from '../core/viteUtils.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroPluginMetadata } from '../vite-plugin-astro/index.js'; @@ -53,7 +52,7 @@ export function astroContentVirtualModPlugin({ fs, }: AstroContentVirtualModPluginParams): Plugin { let IS_DEV = false; - const IS_SERVER = isServerLikeOutput(settings.config); + const IS_SERVER = settings.buildOutput === 'server'; const dataStoreFile = new URL(DATA_STORE_FILE, settings.config.cacheDir); return { name: 'astro-content-virtual-mod-plugin', diff --git a/packages/astro/src/core/build/common.ts b/packages/astro/src/core/build/common.ts index f9ed45836181..4ee826f8b221 100644 --- a/packages/astro/src/core/build/common.ts +++ b/packages/astro/src/core/build/common.ts @@ -1,26 +1,27 @@ import npath from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { appendForwardSlash } from '../../core/path.js'; +import type { AstroSettings } from '../../types/astro.js'; import type { AstroConfig } from '../../types/public/config.js'; import type { RouteData } from '../../types/public/internal.js'; const STATUS_CODE_PAGES = new Set(['/404', '/500']); const FALLBACK_OUT_DIR_NAME = './.astro/'; -function getOutRoot(astroConfig: AstroConfig): URL { - if (astroConfig.output === 'static') { - return new URL('./', astroConfig.outDir); +function getOutRoot(astroSettings: AstroSettings): URL { + if (astroSettings.buildOutput === 'static') { + return new URL('./', astroSettings.config.outDir); } else { - return new URL('./', astroConfig.build.client); + return new URL('./', astroSettings.config.build.client); } } export function getOutFolder( - astroConfig: AstroConfig, + astroSettings: AstroSettings, pathname: string, routeData: RouteData, ): URL { - const outRoot = getOutRoot(astroConfig); + const outRoot = getOutRoot(astroSettings); const routeType = routeData.type; // This is the root folder to write to. @@ -30,7 +31,7 @@ export function getOutFolder( case 'fallback': case 'page': case 'redirect': - switch (astroConfig.build.format) { + switch (astroSettings.config.build.format) { case 'directory': { if (STATUS_CODE_PAGES.has(pathname)) { return new URL('.' + appendForwardSlash(npath.dirname(pathname)), outRoot); diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 5c9ee20b6c12..0573c88b3a3b 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -35,7 +35,7 @@ import { callGetStaticPaths } from '../render/route-cache.js'; import { createRequest } from '../request.js'; import { matchRoute } from '../routing/match.js'; import { stringifyParams } from '../routing/params.js'; -import { getOutputFilename, isServerLikeOutput } from '../util.js'; +import { getOutputFilename } from '../util.js'; import { getOutFile, getOutFolder } from './common.js'; import { cssOrder, mergeInlineCss } from './internal.js'; import { BuildPipeline } from './pipeline.js'; @@ -49,12 +49,12 @@ import { getTimeStat, shouldAppendForwardSlash } from './util.js'; export async function generatePages(options: StaticBuildOptions, internals: BuildInternals) { const generatePagesTimer = performance.now(); - const ssr = isServerLikeOutput(options.settings.config); + const ssr = options.settings.buildOutput === 'server'; let manifest: SSRManifest; if (ssr) { manifest = await BuildPipeline.retrieveManifest(options, internals); } else { - const baseDirectory = getOutputDirectory(options.settings.config); + const baseDirectory = getOutputDirectory(options.settings); const renderersEntryUrl = new URL('renderers.mjs', baseDirectory); const renderers = await import(renderersEntryUrl.toString()); let middleware: MiddlewareHandler = (_, next) => next(); @@ -138,7 +138,7 @@ export async function generatePages(options: StaticBuildOptions, internals: Buil delete globalThis?.astroAsset?.addStaticImage; } - await runHookBuildGenerated({ config, logger }); + await runHookBuildGenerated({ settings: options.settings, logger }); } const THRESHOLD_SLOW_RENDER_TIME_MS = 500; @@ -466,7 +466,7 @@ async function generatePath( body = Buffer.from(await response.arrayBuffer()); } - const outFolder = getOutFolder(config, pathname, route); + const outFolder = getOutFolder(pipeline.settings, pathname, route); const outFile = getOutFile(config, outFolder, pathname, route); route.distURL = outFile; diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 74a648304789..20efa816456a 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -19,13 +19,14 @@ import { createNodeLogger } from '../config/logging.js'; import { createSettings } from '../config/settings.js'; import { createVite } from '../create-vite.js'; import { createKey } from '../encryption.js'; +import { AstroError, AstroErrorData } from '../errors/index.js'; import type { Logger } from '../logger/core.js'; import { levels, timerMessage } from '../logger/core.js'; import { apply as applyPolyfill } from '../polyfill.js'; import { createRouteManifest } from '../routing/index.js'; import { getServerIslandRouteData } from '../server-islands/endpoint.js'; import { clearContentLayerCache } from '../sync/index.js'; -import { ensureProcessNodeEnv, isServerLikeOutput } from '../util.js'; +import { ensureProcessNodeEnv } from '../util.js'; import { collectPagesData } from './page-data.js'; import { staticBuild, viteBuild } from './static-build.js'; import type { StaticBuildOptions } from './types.js'; @@ -117,12 +118,18 @@ class AstroBuilder { logger: logger, }); - if (isServerLikeOutput(this.settings.config)) { + // TODO: Make injectImageEndpoint work from the manifest instead of the settings, so that we can add it to the manifest only in server builds + if (this.settings.buildOutput === 'server') { this.settings = injectImageEndpoint(this.settings, 'build'); } this.manifest = createRouteManifest({ settings: this.settings }, this.logger); + // If we're building for the server, we need to ensure that an adapter is installed. + if (!this.settings.config.adapter && this.settings.buildOutput === 'server') { + throw new AstroError(AstroErrorData.NoAdapterInstalled); + } + const viteConfig = await createVite( { mode: this.mode, @@ -211,7 +218,7 @@ class AstroBuilder { // You're done! Time to clean up. await runHookBuildDone({ - config: this.settings.config, + settings: this.settings, pages: pageNames, routes: Object.values(allPages) .flat() diff --git a/packages/astro/src/core/build/page-data.ts b/packages/astro/src/core/build/page-data.ts index 06ed629f2ce6..f3af07043094 100644 --- a/packages/astro/src/core/build/page-data.ts +++ b/packages/astro/src/core/build/page-data.ts @@ -42,7 +42,7 @@ export function collectPagesData(opts: CollectPagesDataOptions): CollectPagesDat hoistedScript: undefined, }; - if (settings.config.output === 'static') { + if (settings.buildOutput === 'static') { const html = `${route.pathname}`.replace(/\/?$/, '/index.html'); debug( 'build', diff --git a/packages/astro/src/core/build/pipeline.ts b/packages/astro/src/core/build/pipeline.ts index ae00da5a0ede..949c27f561bb 100644 --- a/packages/astro/src/core/build/pipeline.ts +++ b/packages/astro/src/core/build/pipeline.ts @@ -14,7 +14,6 @@ import { } from '../render/ssr-element.js'; import { createDefaultRoutes } from '../routing/default.js'; import { findRouteToRewrite } from '../routing/rewrite.js'; -import { isServerLikeOutput } from '../util.js'; import { getOutDirWithinCwd } from './common.js'; import { type BuildInternals, cssOrder, getPageData, mergeInlineCss } from './internal.js'; import { ASTRO_PAGE_MODULE_ID, ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugins/plugin-pages.js'; @@ -37,8 +36,7 @@ export class BuildPipeline extends Pipeline { #routesByFilePath: WeakMap = new WeakMap(); get outFolder() { - const ssr = isServerLikeOutput(this.settings.config); - return ssr + return this.settings.buildOutput === 'server' ? this.settings.config.build.server : getOutDirWithinCwd(this.settings.config.outDir); } @@ -72,7 +70,7 @@ export class BuildPipeline extends Pipeline { return assetLink; } - const serverLike = isServerLikeOutput(config); + const serverLike = settings.buildOutput === 'server'; // We can skip streaming in SSG for performance as writing as strings are faster const streaming = serverLike; super( @@ -111,8 +109,7 @@ export class BuildPipeline extends Pipeline { staticBuildOptions: StaticBuildOptions, internals: BuildInternals, ): Promise { - const config = staticBuildOptions.settings.config; - const baseDirectory = getOutputDirectory(config); + const baseDirectory = getOutputDirectory(staticBuildOptions.settings); const manifestEntryUrl = new URL( `${internals.manifestFileName}?time=${Date.now()}`, baseDirectory, diff --git a/packages/astro/src/core/build/plugins/plugin-content.ts b/packages/astro/src/core/build/plugins/plugin-content.ts index 5fe0b6792036..e8ca70741af3 100644 --- a/packages/astro/src/core/build/plugins/plugin-content.ts +++ b/packages/astro/src/core/build/plugins/plugin-content.ts @@ -496,7 +496,7 @@ export function pluginContent( targets: ['server'], hooks: { async 'build:before'() { - if (!isContentCollectionsCacheEnabled(opts.settings.config)) { + if (!isContentCollectionsCacheEnabled(opts.settings)) { return { vitePlugin: undefined }; } const lookupMap = await generateLookupMap({ settings: opts.settings, fs: fsMod }); @@ -506,7 +506,7 @@ export function pluginContent( }, async 'build:post'() { - if (!isContentCollectionsCacheEnabled(opts.settings.config)) { + if (!isContentCollectionsCacheEnabled(opts.settings)) { return; } // Cache build output of chunks and assets diff --git a/packages/astro/src/core/build/plugins/plugin-manifest.ts b/packages/astro/src/core/build/plugins/plugin-manifest.ts index 5aee33337b25..2f1e5b66c41f 100644 --- a/packages/astro/src/core/build/plugins/plugin-manifest.ts +++ b/packages/astro/src/core/build/plugins/plugin-manifest.ts @@ -176,7 +176,7 @@ function buildManifest( if (!route.prerender) continue; if (!route.pathname) continue; - const outFolder = getOutFolder(opts.settings.config, route.pathname, route); + const outFolder = getOutFolder(opts.settings, route.pathname, route); const outFile = getOutFile(opts.settings.config, outFolder, route.pathname, route); const file = outFile.toString().replace(opts.settings.config.build.client.toString(), ''); routes.push({ diff --git a/packages/astro/src/core/build/plugins/plugin-prerender.ts b/packages/astro/src/core/build/plugins/plugin-prerender.ts index c7552db9b485..f915c927083b 100644 --- a/packages/astro/src/core/build/plugins/plugin-prerender.ts +++ b/packages/astro/src/core/build/plugins/plugin-prerender.ts @@ -90,7 +90,7 @@ export function pluginPrerender( internals: BuildInternals, ): AstroBuildPlugin { // Static output can skip prerender completely because we're already rendering all pages - if (opts.settings.config.output === 'static') { + if (opts.settings.buildOutput === 'static') { return { targets: ['server'] }; } diff --git a/packages/astro/src/core/build/plugins/plugin-ssr.ts b/packages/astro/src/core/build/plugins/plugin-ssr.ts index 992dc65614f4..3ddd1d127424 100644 --- a/packages/astro/src/core/build/plugins/plugin-ssr.ts +++ b/packages/astro/src/core/build/plugins/plugin-ssr.ts @@ -3,7 +3,6 @@ import type { AstroSettings } from '../../../types/astro.js'; import type { AstroAdapter } from '../../../types/public/integrations.js'; import { routeIsRedirect } from '../../redirects/index.js'; import { VIRTUAL_ISLAND_MAP_ID } from '../../server-islands/vite-plugin-server-islands.js'; -import { isServerLikeOutput } from '../../util.js'; import { addRollupInput } from '../add-rollup-input.js'; import type { BuildInternals } from '../internal.js'; import type { AstroBuildPlugin } from '../plugin.js'; @@ -131,11 +130,12 @@ export function pluginSSR( options: StaticBuildOptions, internals: BuildInternals, ): AstroBuildPlugin { - const ssr = isServerLikeOutput(options.settings.config); + const ssr = options.settings.buildOutput === 'server'; return { targets: ['server'], hooks: { 'build:before': () => { + // We check before this point if there's an adapter, so we can safely assume it exists here. const adapter = options.settings.adapter!; const ssrPlugin = ssr && vitePluginSSR(internals, adapter, options); const vitePlugin = [vitePluginAdapter(adapter)]; diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 725f919baeda..50963850de2c 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -18,12 +18,10 @@ import { } from '../../core/build/internal.js'; import { emptyDir, removeEmptyDirs } from '../../core/fs/index.js'; import { appendForwardSlash, prependForwardSlash, removeFileExtension } from '../../core/path.js'; -import { isModeServerWithNoAdapter, isServerLikeOutput } from '../../core/util.js'; import { runHookBuildSetup } from '../../integrations/hooks.js'; import { getOutputDirectory } from '../../prerender/utils.js'; import type { RouteData } from '../../types/public/internal.js'; import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js'; -import { AstroError, AstroErrorData } from '../errors/index.js'; import type { Logger } from '../logger/core.js'; import { routeIsRedirect } from '../redirects/index.js'; import { getOutDirWithinCwd } from './common.js'; @@ -43,10 +41,6 @@ import { encodeName, getTimeStat, viteBuildReturnToRollupOutputs } from './util. export async function viteBuild(opts: StaticBuildOptions) { const { allPages, settings, logger } = opts; - // Make sure we have an adapter before building - if (isModeServerWithNoAdapter(opts.settings)) { - throw new AstroError(AstroErrorData.NoAdapterInstalled); - } settings.timer.start('SSR build'); @@ -144,15 +138,15 @@ export async function staticBuild( contentFileNames?: string[], ) { const { settings } = opts; - switch (true) { - case settings.config.output === 'static': { + switch (settings.buildOutput) { + case 'static': { settings.timer.start('Static generate'); await generatePages(opts, internals); await cleanServerOutput(opts, ssrOutputChunkNames, contentFileNames, internals); settings.timer.end('Static generate'); return; } - case isServerLikeOutput(settings.config): { + case 'server': { settings.timer.start('Server generate'); await generatePages(opts, internals); await cleanStaticOutput(opts, internals); @@ -161,8 +155,6 @@ export async function staticBuild( settings.timer.end('Server generate'); return; } - default: - return; } } @@ -175,8 +167,10 @@ async function ssrBuild( ) { const buildID = Date.now().toString(); const { allPages, settings, viteConfig } = opts; - const ssr = isServerLikeOutput(settings.config); - const out = getOutputDirectory(settings.config); + + // TODO: This is inaccurate, we don't know yet if we're building SSR or not + const ssr = settings.buildOutput === 'server'; + const out = getOutputDirectory(settings); const routes = Object.values(allPages).flatMap((pageData) => pageData.route); const isContentCache = !ssr && settings.config.experimental.contentCollectionCache; const { lastVitePlugins, vitePlugins } = await container.runBeforeHook('server', input); @@ -306,7 +300,7 @@ async function clientBuild( container: AstroBuildPluginContainer, ) { const { settings, viteConfig } = opts; - const ssr = isServerLikeOutput(settings.config); + const ssr = settings.buildOutput === 'server'; const out = ssr ? settings.config.build.client : getOutDirWithinCwd(settings.config.outDir); // Nothing to do if there is no client-side JS. @@ -370,11 +364,12 @@ async function runPostBuildHooks( const config = container.options.settings.config; const build = container.options.settings.config.build; for (const [fileName, mutation] of mutations) { - const root = isServerLikeOutput(config) - ? mutation.targets.includes('server') - ? build.server - : build.client - : getOutDirWithinCwd(config.outDir); + const root = + container.options.settings.buildOutput === 'server' + ? mutation.targets.includes('server') + ? build.server + : build.client + : getOutDirWithinCwd(config.outDir); const fullPath = path.join(fileURLToPath(root), fileName); const fileURL = pathToFileURL(fullPath); await fs.promises.mkdir(new URL('./', fileURL), { recursive: true }); @@ -386,7 +381,7 @@ async function runPostBuildHooks( * Remove chunks that are used for prerendering only */ async function cleanStaticOutput(opts: StaticBuildOptions, internals: BuildInternals) { - const ssr = isServerLikeOutput(opts.settings.config); + const ssr = opts.settings.buildOutput === 'server'; const out = ssr ? opts.settings.config.build.server : getOutDirWithinCwd(opts.settings.config.outDir); diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index 7940e4eb69d6..2c909e353648 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -132,7 +132,7 @@ export const AstroConfigSchema = z.object({ .optional() .default(ASTRO_CONFIG_DEFAULTS.trailingSlash), output: z - .union([z.literal('static'), z.literal('server'), z.literal('hybrid')]) + .union([z.literal('static'), z.literal('server')]) .optional() .default('static'), scopedStyleStrategy: z diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts index 902ff7d03318..e333d6b06182 100644 --- a/packages/astro/src/core/config/settings.ts +++ b/packages/astro/src/core/config/settings.ts @@ -111,6 +111,7 @@ export function createBaseSettings(config: AstroConfig): AstroSettings { dotAstroDir, latestAstroVersion: undefined, // Will be set later if applicable when the dev server starts injectedTypes: [], + buildOutput: undefined, }; } diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 475acdbbf7de..3036328ae3ed 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -426,6 +426,7 @@ export const ReservedSlotName = { export const NoAdapterInstalled = { name: 'NoAdapterInstalled', title: 'Cannot use Server-side Rendering without an adapter.', + // TODO: Update this error message message: `Cannot use \`output: 'server'\` or \`output: 'hybrid'\` 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; diff --git a/packages/astro/src/core/middleware/vite-plugin.ts b/packages/astro/src/core/middleware/vite-plugin.ts index 8a76bed921c9..f80af2467e5a 100644 --- a/packages/astro/src/core/middleware/vite-plugin.ts +++ b/packages/astro/src/core/middleware/vite-plugin.ts @@ -126,7 +126,7 @@ export function vitePluginMiddlewareBuild( writeBundle(_, bundle) { for (const [chunkName, chunk] of Object.entries(bundle)) { if (chunk.type !== 'asset' && chunk.fileName === 'middleware.mjs') { - const outputDirectory = getOutputDirectory(opts.settings.config); + const outputDirectory = getOutputDirectory(opts.settings); internals.middlewareEntryPoint = new URL(chunkName, outputDirectory); } } diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 14980f63e5ad..1cb2fb75917a 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -521,6 +521,24 @@ export function createRouteManifest( ...redirectRoutes['legacy'].sort(routeComparator), ]; + // Assume static output unless we find a server-rendered route + settings.buildOutput = 'static'; + + // Scan all the routes to see if they're prerendered or not + for (const route of routes) { + const content = nodeFs.readFileSync( + fileURLToPath(new URL(route.component, config.root)), + 'utf-8', + ); + + // Check if the route is pre-rendered or not + const match = /^export\s+const\s+prerender\s*=\s*(true|false);?/m.exec(content); + if (match) { + route.prerender = match[1] === 'true'; + if (!route.prerender) settings.buildOutput = 'server'; + } + } + // Report route collisions if (config.experimental.globalRoutePriority) { for (const [index, higherRoute] of routes.entries()) { diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts index 497d54610262..0904cf6ac855 100644 --- a/packages/astro/src/core/util.ts +++ b/packages/astro/src/core/util.ts @@ -158,19 +158,11 @@ export function isEndpoint(file: URL, settings: AstroSettings): boolean { return !endsWithPageExt(file, settings) && !file.toString().includes('?astro'); } -export function isServerLikeOutput(config: AstroConfig) { - return config.output === 'server' || config.output === 'hybrid'; -} - -export function isModeServerWithNoAdapter(settings: AstroSettings): boolean { - return isServerLikeOutput(settings.config) && !settings.adapter; -} - -export function isContentCollectionsCacheEnabled(config: AstroConfig): boolean { +export function isContentCollectionsCacheEnabled(settings: AstroSettings): boolean { return ( - config.experimental.contentCollectionCache && + settings.config.experimental.contentCollectionCache && // contentCollectionsCache is an SSG only feature - !isServerLikeOutput(config) + settings.buildOutput !== 'static' ); } diff --git a/packages/astro/src/integrations/features-validation.ts b/packages/astro/src/integrations/features-validation.ts index a9386090d129..0a59f48a3b25 100644 --- a/packages/astro/src/integrations/features-validation.ts +++ b/packages/astro/src/integrations/features-validation.ts @@ -58,7 +58,7 @@ export function validateSupportedFeatures( adapterName, logger, 'hybridOutput', - () => config?.output === 'hybrid', + () => false, ); validationResult.serverOutput = validateSupportKind( diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 1dc681db6ea6..41c7a9bdd09b 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -8,7 +8,6 @@ import type { PageBuildData } from '../core/build/types.js'; import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js'; import { mergeConfig } from '../core/config/index.js'; import type { AstroIntegrationLogger, Logger } from '../core/logger/core.js'; -import { isServerLikeOutput } from '../core/util.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroConfig } from '../types/public/config.js'; import type { ContentEntryType, DataEntryType } from '../types/public/content.js'; @@ -519,15 +518,16 @@ export async function runHookBuildSsr({ } export async function runHookBuildGenerated({ - config, + settings, logger, }: { - config: AstroConfig; + settings: AstroSettings; logger: Logger; }) { - const dir = isServerLikeOutput(config) ? config.build.client : config.outDir; + const dir = + settings.buildOutput === 'server' ? settings.config.build.client : settings.config.outDir; - for (const integration of config.integrations) { + for (const integration of settings.config.integrations) { if (integration?.hooks?.['astro:build:generated']) { await withTakingALongTimeMsg({ name: integration.name, @@ -543,7 +543,7 @@ export async function runHookBuildGenerated({ } type RunHookBuildDone = { - config: AstroConfig; + settings: AstroSettings; pages: string[]; routes: RouteData[]; logging: Logger; @@ -551,16 +551,17 @@ type RunHookBuildDone = { }; export async function runHookBuildDone({ - config, + settings, pages, routes, logging, cacheManifest, }: RunHookBuildDone) { - const dir = isServerLikeOutput(config) ? config.build.client : config.outDir; + const dir = + settings.buildOutput === 'server' ? settings.config.build.client : settings.config.outDir; await fsMod.promises.mkdir(dir, { recursive: true }); - for (const integration of config.integrations) { + for (const integration of settings.config.integrations) { if (integration?.hooks?.['astro:build:done']) { const logger = getLogger(integration, logging); diff --git a/packages/astro/src/prerender/utils.ts b/packages/astro/src/prerender/utils.ts index e34e0d5fd024..06ddc09efbaa 100644 --- a/packages/astro/src/prerender/utils.ts +++ b/packages/astro/src/prerender/utils.ts @@ -1,5 +1,5 @@ import { getOutDirWithinCwd } from '../core/build/common.js'; -import { isServerLikeOutput } from '../core/util.js'; +import type { AstroSettings } from '../types/astro.js'; import type { AstroConfig } from '../types/public/config.js'; export function getPrerenderDefault(config: AstroConfig) { @@ -9,11 +9,10 @@ export function getPrerenderDefault(config: AstroConfig) { /** * Returns the correct output directory of the SSR build based on the configuration */ -export function getOutputDirectory(config: AstroConfig): URL { - const ssr = isServerLikeOutput(config); - if (ssr) { - return config.build.server; +export function getOutputDirectory(settings: AstroSettings): URL { + if (settings.buildOutput === 'server') { + return settings.config.build.server; } else { - return getOutDirWithinCwd(config.outDir); + return getOutDirWithinCwd(settings.config.outDir); } } diff --git a/packages/astro/src/types/astro.ts b/packages/astro/src/types/astro.ts index 1d48536d07a1..7e2f2af7e073 100644 --- a/packages/astro/src/types/astro.ts +++ b/packages/astro/src/types/astro.ts @@ -67,6 +67,11 @@ export interface AstroSettings { serverIslandMap: NonNullable; serverIslandNameMap: NonNullable; injectedTypes: Array; + /** + * Determine if the build output should be a static, dist folder or a adapter-based server output + * undefined when unknown + */ + buildOutput: undefined | 'static' | 'server'; } /** Generic interface for a component (Astro, Svelte, React, etc.) */ diff --git a/packages/astro/src/vite-plugin-astro-server/pipeline.ts b/packages/astro/src/vite-plugin-astro-server/pipeline.ts index 5a672279ede8..98fa40b2da3a 100644 --- a/packages/astro/src/vite-plugin-astro-server/pipeline.ts +++ b/packages/astro/src/vite-plugin-astro-server/pipeline.ts @@ -9,7 +9,7 @@ import type { ModuleLoader } from '../core/module-loader/index.js'; import { Pipeline, loadRenderer } from '../core/render/index.js'; import { createDefaultRoutes } from '../core/routing/default.js'; import { findRouteToRewrite } from '../core/routing/rewrite.js'; -import { isPage, isServerLikeOutput, viteID } from '../core/util.js'; +import { isPage, viteID } from '../core/util.js'; import { resolveIdToUrl } from '../core/viteUtils.js'; import type { AstroSettings, ComponentInstance, ManifestData } from '../types/astro.js'; import type { RewritePayload } from '../types/public/common.js'; @@ -48,7 +48,7 @@ export class DevPipeline extends Pipeline { ) { const mode = 'development'; const resolve = createResolve(loader, config.root); - const serverLike = isServerLikeOutput(config); + const serverLike = settings.buildOutput === 'server'; const streaming = true; super(logger, manifest, mode, [], resolve, serverLike, streaming); manifest.serverIslandMap = settings.serverIslandMap; @@ -223,7 +223,7 @@ export class DevPipeline extends Pipeline { } rewriteKnownRoute(route: string, sourceRoute: RouteData): ComponentInstance { - if (isServerLikeOutput(this.config) && sourceRoute.prerender) { + if (this.serverLike && sourceRoute.prerender) { for (let def of this.defaultRoutes) { if (route === def.route) { return def.instance; diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index c59af5c80c7a..56118b52d45f 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -174,7 +174,7 @@ export async function handleRoute({ body, logger, clientAddress: incomingRequest.socket.remoteAddress, - staticLike: config.output === 'static' || route.prerender, + staticLike: route.prerender, }); // Set user specified headers to response object. diff --git a/packages/astro/src/vite-plugin-scanner/index.ts b/packages/astro/src/vite-plugin-scanner/index.ts index 2633743ae554..f21af5816e1f 100644 --- a/packages/astro/src/vite-plugin-scanner/index.ts +++ b/packages/astro/src/vite-plugin-scanner/index.ts @@ -3,7 +3,7 @@ import { bold } from 'kleur/colors'; import type { Plugin as VitePlugin } from 'vite'; import { normalizePath } from 'vite'; import { type Logger } from '../core/logger/core.js'; -import { isEndpoint, isPage, isServerLikeOutput } from '../core/util.js'; +import { isEndpoint, isPage } from '../core/util.js'; import { rootRelativePath } from '../core/viteUtils.js'; import { runHookRouteSetup } from '../integrations/hooks.js'; import { getPrerenderDefault } from '../prerender/utils.js'; @@ -44,10 +44,13 @@ export default function astroScannerPlugin({ if (!(fileIsPage || fileIsEndpoint)) return; const pageOptions = await getPageOptions(code, id, fileURL, settings, logger); + if (pageOptions.prerender === false) { + settings.buildOutput = 'server'; + } + // `getStaticPaths` warning is just a string check, should be good enough for most cases if ( !pageOptions.prerender && - isServerLikeOutput(settings.config) && code.includes('getStaticPaths') && // this should only be valid for `.astro`, `.js` and `.ts` files KNOWN_FILE_EXTENSIONS.includes(extname(filename)) diff --git a/packages/astro/src/vite-plugin-scanner/scan.ts b/packages/astro/src/vite-plugin-scanner/scan.ts index 35e3de20a84f..c44af5c1df89 100644 --- a/packages/astro/src/vite-plugin-scanner/scan.ts +++ b/packages/astro/src/vite-plugin-scanner/scan.ts @@ -78,7 +78,7 @@ export async function scan( message: AstroErrorData.InvalidPrerenderExport.message( prefix, suffix, - settings?.config.output === 'hybrid', + settings?.config.output === 'static', ), location: { file: id }, }); diff --git a/packages/astro/test/fixtures/server-islands/hybrid/astro.config.mjs b/packages/astro/test/fixtures/server-islands/hybrid/astro.config.mjs index 70d0e6d6aadb..b01b674f53de 100644 --- a/packages/astro/test/fixtures/server-islands/hybrid/astro.config.mjs +++ b/packages/astro/test/fixtures/server-islands/hybrid/astro.config.mjs @@ -2,7 +2,7 @@ import svelte from '@astrojs/svelte'; import { defineConfig } from 'astro/config'; export default defineConfig({ - output: 'hybrid', + output: 'static', integrations: [ svelte() ], @@ -10,4 +10,3 @@ export default defineConfig({ serverIslands: true, } }); - diff --git a/packages/astro/test/i18n-routing.test.js b/packages/astro/test/i18n-routing.test.js index df8083b81c1a..a18a987b3686 100644 --- a/packages/astro/test/i18n-routing.test.js +++ b/packages/astro/test/i18n-routing.test.js @@ -1763,7 +1763,7 @@ describe('[SSR] i18n routing', () => { before(async () => { fixture = await loadFixture({ root: './fixtures/i18n-routing-prefix-always/', - output: 'hybrid', + output: 'static', adapter: testAdapter(), }); await fixture.build(); diff --git a/packages/astro/test/ssr-prerender-get-static-paths.test.js b/packages/astro/test/ssr-prerender-get-static-paths.test.js index 50b403891d11..aa4532ca0b84 100644 --- a/packages/astro/test/ssr-prerender-get-static-paths.test.js +++ b/packages/astro/test/ssr-prerender-get-static-paths.test.js @@ -145,7 +145,7 @@ describe('Prerender', () => { site: 'https://mysite.dev/', adapter: testAdapter(), base: '/blog', - output: 'hybrid', + output: 'static', vite: { plugins: [vitePluginRemovePrerenderExport()], }, diff --git a/packages/astro/test/underscore-in-folder-name.test.js b/packages/astro/test/underscore-in-folder-name.test.js index 622ba977d126..b804a715cd8b 100644 --- a/packages/astro/test/underscore-in-folder-name.test.js +++ b/packages/astro/test/underscore-in-folder-name.test.js @@ -9,7 +9,7 @@ describe('Projects with a underscore in the folder name', () => { before(async () => { fixture = await loadFixture({ root: './fixtures/_underscore in folder name/', - output: 'hybrid', + output: 'static', adapter: testAdapter(), }); await fixture.build(); diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js index c9f948abc88d..10b282b7ce99 100644 --- a/packages/astro/test/units/integrations/api.test.js +++ b/packages/astro/test/units/integrations/api.test.js @@ -128,7 +128,7 @@ describe('Astro feature map', function () { hybridOutput: 'stable', }, { - output: 'hybrid', + output: 'static', }, {}, defaultLogger, @@ -141,7 +141,7 @@ describe('Astro feature map', function () { 'test', {}, { - output: 'hybrid', + output: 'static', }, {}, defaultLogger, @@ -154,7 +154,7 @@ describe('Astro feature map', function () { 'test', {}, { - output: 'hybrid', + output: 'static', }, {}, defaultLogger, @@ -195,7 +195,7 @@ describe('Astro feature map', function () { 'test', { hybridOutput: 'stable' }, { - output: 'hybrid', + output: 'static', }, {}, defaultLogger, @@ -210,7 +210,7 @@ describe('Astro feature map', function () { hybridOutput: 'unsupported', }, { - output: 'hybrid', + output: 'static', }, {}, defaultLogger, diff --git a/packages/astro/test/units/routing/route-matching.test.js b/packages/astro/test/units/routing/route-matching.test.js index a9a0e48a3d67..f8001c5723e9 100644 --- a/packages/astro/test/units/routing/route-matching.test.js +++ b/packages/astro/test/units/routing/route-matching.test.js @@ -135,7 +135,7 @@ describe('Route matching', () => { settings = await createBasicSettings({ root: fileURLToPath(root), trailingSlash: 'never', - output: 'hybrid', + output: 'static', adapter: testAdapter(), }); container = await createContainer({ diff --git a/packages/astro/test/units/routing/route-sanitization.test.js b/packages/astro/test/units/routing/route-sanitization.test.js index 802418868173..45020d141511 100644 --- a/packages/astro/test/units/routing/route-sanitization.test.js +++ b/packages/astro/test/units/routing/route-sanitization.test.js @@ -38,7 +38,7 @@ describe('Route sanitization', () => { settings = await createBasicSettings({ root: fileURLToPath(root), trailingSlash: 'never', - output: 'hybrid', + output: 'static', adapter: testAdapter(), }); container = await createContainer({ diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index de8eb6169246..540df03540c6 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -159,11 +159,7 @@ function astroDBIntegration(): AstroIntegration { }, 100); }, 'astro:build:start': async ({ logger }) => { - if ( - !connectToStudio && - !databaseFileEnvDefined() && - (output === 'server' || output === 'hybrid') - ) { + if (!connectToStudio && !databaseFileEnvDefined()) { const message = `Attempting to build without the --remote flag or the ASTRO_DATABASE_FILE environment variable defined. You probably want to pass --remote to astro build.`; const hint = 'Learn more connecting to Studio: https://docs.astro.build/en/guides/astro-db/#connect-to-astro-studio'; diff --git a/packages/integrations/vercel/src/lib/prerender.ts b/packages/integrations/vercel/src/lib/prerender.ts deleted file mode 100644 index f69f3b5d470c..000000000000 --- a/packages/integrations/vercel/src/lib/prerender.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { AstroConfig } from 'astro'; - -export function isServerLikeOutput(config: AstroConfig) { - return config.output === 'server' || config.output === 'hybrid'; -} diff --git a/packages/integrations/vercel/src/static/adapter.ts b/packages/integrations/vercel/src/static/adapter.ts index 4969d55d1610..b8dbe878a115 100644 --- a/packages/integrations/vercel/src/static/adapter.ts +++ b/packages/integrations/vercel/src/static/adapter.ts @@ -7,7 +7,6 @@ import { getAstroImageConfig, getDefaultImageConfig, } from '../image/shared.js'; -import { isServerLikeOutput } from '../lib/prerender.js'; import { getRedirects } from '../lib/redirects.js'; import { type VercelSpeedInsightsConfig, @@ -100,10 +99,6 @@ export default function vercelStatic({ 'astro:config:done': ({ setAdapter, config }) => { setAdapter(getAdapter()); _config = config; - - if (isServerLikeOutput(config)) { - throw new Error(`${PACKAGE_NAME} should be used with output: 'static'`); - } }, 'astro:build:start': async () => { // Ensure to have `.vercel/output` empty. diff --git a/packages/integrations/web-vitals/src/index.ts b/packages/integrations/web-vitals/src/index.ts index 02293ac6f0c6..84837e3edb93 100644 --- a/packages/integrations/web-vitals/src/index.ts +++ b/packages/integrations/web-vitals/src/index.ts @@ -19,13 +19,13 @@ export default function webVitals({ deprecated }: { deprecated?: boolean } = {}) ); } - if (config.output !== 'hybrid' && config.output !== 'server') { - throw new AstroError( - 'No SSR adapter found.', - '`@astrojs/web-vitals` requires your site to be built with `hybrid` or `server` output.\n' + - 'Please add an SSR adapter: https://docs.astro.build/en/guides/server-side-rendering/', - ); - } + // if (config.output !== 'hybrid' && config.output !== 'server') { + // throw new AstroError( + // 'No SSR adapter found.', + // '`@astrojs/web-vitals` requires your site to be built with `hybrid` or `server` output.\n' + + // 'Please add an SSR adapter: https://docs.astro.build/en/guides/server-side-rendering/', + // ); + // } // Middleware that adds a `` tag to each page. addMiddleware({ entrypoint: '@astrojs/web-vitals/middleware', order: 'post' }); From 415a9803fdd39f59848c7f82a03a07c2073d1096 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 23 Aug 2024 16:37:37 +0200 Subject: [PATCH 02/27] fix: linting --- packages/astro/src/core/build/static-build.ts | 2 ++ packages/db/src/core/integration/index.ts | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 50963850de2c..9b293b87aef4 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -155,6 +155,8 @@ export async function staticBuild( settings.timer.end('Server generate'); return; } + default: // `settings.buildOutput` will always be one of the above, but TS doesn't know that + return; } } diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index 540df03540c6..53aa022092e5 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -4,7 +4,7 @@ import { dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; import { type ManagedAppToken, getManagedAppTokenOrExit } from '@astrojs/studio'; import { LibsqlError } from '@libsql/client'; -import type { AstroConfig, AstroIntegration } from 'astro'; +import type { AstroIntegration } from 'astro'; import { blue, yellow } from 'kleur/colors'; import { type HMRPayload, @@ -59,14 +59,12 @@ function astroDBIntegration(): AstroIntegration { }; let command: 'dev' | 'build' | 'preview'; - let output: AstroConfig['output'] = 'server'; return { name: 'astro:db', hooks: { 'astro:config:setup': async ({ updateConfig, config, command: _command, logger }) => { command = _command; root = config.root; - output = config.output; if (command === 'preview') return; From 38fe245afc1a1919292c0303cea6c16f71eb7286 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Sat, 24 Aug 2024 01:32:47 +0200 Subject: [PATCH 03/27] fix: get a bunch of tests passing --- packages/astro/src/actions/index.ts | 12 ++++---- packages/astro/src/assets/endpoint/config.ts | 26 +++++++++++----- packages/astro/src/core/build/index.ts | 7 +++-- packages/astro/src/core/create-vite.ts | 7 +++-- packages/astro/src/core/dev/container.ts | 11 +++++-- packages/astro/src/core/preview/index.ts | 9 +++++- .../astro/src/core/routing/manifest/create.ts | 30 ++++++++++++++----- packages/astro/src/integrations/hooks.ts | 7 +++++ .../astro/src/types/public/integrations.ts | 5 ++++ .../src/vite-plugin-astro-server/plugin.ts | 21 ++++++++----- .../test/fixtures/error-bad-js/src/env.d.ts | 1 + .../error-build-location/src/env.d.ts | 1 + .../fixtures/error-non-error/src/env.d.ts | 1 + .../fixtures/streaming/src/pages/index.astro | 2 ++ .../streaming/src/pages/multiple-errors.astro | 2 ++ .../fixtures/streaming/src/pages/slot.astro | 2 ++ packages/astro/test/test-adapter.js | 3 ++ 17 files changed, 109 insertions(+), 38 deletions(-) diff --git a/packages/astro/src/actions/index.ts b/packages/astro/src/actions/index.ts index 029bf1133914..fbe321cad626 100644 --- a/packages/astro/src/actions/index.ts +++ b/packages/astro/src/actions/index.ts @@ -25,12 +25,6 @@ export default function astroActions({ name: VIRTUAL_MODULE_ID, hooks: { async 'astro:config:setup'(params) { - if (settings.buildOutput !== 'server') { - const error = new AstroError(ActionsWithoutServerOutputError); - error.stack = undefined; - throw error; - } - params.updateConfig({ vite: { plugins: [vitePluginUserActions({ settings }), vitePluginActions(fs)], @@ -49,6 +43,12 @@ export default function astroActions({ }); }, 'astro:config:done': (params) => { + if (params.buildOutput !== 'server') { + const error = new AstroError(ActionsWithoutServerOutputError); + error.stack = undefined; + throw error; + } + const stringifiedActionsImport = JSON.stringify( viteID(new URL('./actions', params.config.srcDir)), ); diff --git a/packages/astro/src/assets/endpoint/config.ts b/packages/astro/src/assets/endpoint/config.ts index ff9dcc79a807..965b82812a9e 100644 --- a/packages/astro/src/assets/endpoint/config.ts +++ b/packages/astro/src/assets/endpoint/config.ts @@ -1,15 +1,27 @@ -import type { AstroSettings } from '../../types/astro.js'; +import { resolveInjectedRoute } from '../../core/routing/manifest/create.js'; +import type { AstroSettings, ManifestData } from '../../types/astro.js'; -export function injectImageEndpoint(settings: AstroSettings, mode: 'dev' | 'build') { +export function injectImageEndpoint( + settings: AstroSettings, + manifest: ManifestData, + mode: 'dev' | 'build', + cwd?: string, +) { const endpointEntrypoint = settings.config.image.endpoint ?? (mode === 'dev' ? 'astro/assets/endpoint/node' : 'astro/assets/endpoint/generic'); - settings.injectedRoutes.push({ - pattern: '/_image', - entrypoint: endpointEntrypoint, + manifest.routes.push({ + type: 'endpoint', + isIndex: false, + route: '/_image', + pattern: /^\/_image$/, + segments: [[{ content: '_image', dynamic: false, spread: false }]], + params: [], + component: resolveInjectedRoute(endpointEntrypoint, settings.config.root, cwd).component, + generate: () => '', + pathname: '/_image', prerender: false, + fallbackRoutes: [], }); - - return settings; } diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 20efa816456a..f93705ce7c7a 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -119,12 +119,13 @@ class AstroBuilder { }); // TODO: Make injectImageEndpoint work from the manifest instead of the settings, so that we can add it to the manifest only in server builds - if (this.settings.buildOutput === 'server') { - this.settings = injectImageEndpoint(this.settings, 'build'); - } this.manifest = createRouteManifest({ settings: this.settings }, this.logger); + if (this.settings.buildOutput === 'server') { + injectImageEndpoint(this.settings, this.manifest, 'build'); + } + // If we're building for the server, we need to ensure that an adapter is installed. if (!this.settings.config.adapter && this.settings.buildOutput === 'server') { throw new AstroError(AstroErrorData.NoAdapterInstalled); diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index 6a5995e3f912..53376d3b2a52 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -16,7 +16,7 @@ import astroInternationalization from '../i18n/vite-plugin-i18n.js'; import astroPrefetch from '../prefetch/vite-plugin-prefetch.js'; import astroDevToolbar from '../toolbar/vite-plugin-dev-toolbar.js'; import astroTransitions from '../transitions/vite-plugin-transitions.js'; -import type { AstroSettings } from '../types/astro.js'; +import type { AstroSettings, ManifestData } from '../types/astro.js'; import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.js'; import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js'; import astroVitePlugin from '../vite-plugin-astro/index.js'; @@ -47,6 +47,7 @@ interface CreateViteOptions { command?: 'dev' | 'build'; fs?: typeof nodeFs; sync: boolean; + manifest?: ManifestData; } const ALWAYS_NOEXTERNAL = [ @@ -74,7 +75,7 @@ const ONLY_DEV_EXTERNAL = [ /** Return a base vite config as a common starting point for all Vite commands. */ export async function createVite( commandConfig: vite.InlineConfig, - { settings, logger, mode, command, fs = nodeFs, sync }: CreateViteOptions, + { settings, logger, mode, command, fs = nodeFs, sync, manifest }: CreateViteOptions, ): Promise { const astroPkgsConfig = await crawlFrameworkPkgs({ root: fileURLToPath(settings.config.root), @@ -130,7 +131,7 @@ export async function createVite( astroScriptsPlugin({ settings }), // The server plugin is for dev only and having it run during the build causes // the build to run very slow as the filewatcher is triggered often. - mode !== 'build' && vitePluginAstroServer({ settings, logger, fs }), + mode !== 'build' && vitePluginAstroServer({ settings, logger, fs, manifest: manifest! }), // The manifest is always defined in dev mode envVitePlugin({ settings, logger }), astroEnv({ settings, mode, sync }), markdownVitePlugin({ settings, logger }), diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts index f89e331970c5..f6092eda97ca 100644 --- a/packages/astro/src/core/dev/container.ts +++ b/packages/astro/src/core/dev/container.ts @@ -15,6 +15,7 @@ import type { AstroInlineConfig } from '../../types/public/config.js'; import { createVite } from '../create-vite.js'; import type { Logger } from '../logger/core.js'; import { apply as applyPolyfill } from '../polyfill.js'; +import { createRouteManifest } from '../routing/index.js'; import { syncInternal } from '../sync/index.js'; export interface Container { @@ -52,8 +53,6 @@ export async function createContainer({ isRestart, }); - settings = injectImageEndpoint(settings, 'dev'); - const { base, server: { host, headers, open: serverOpen }, @@ -81,6 +80,11 @@ export async function createContainer({ .map((r) => r.clientEntrypoint) .filter(Boolean) as string[]; + // Create the route manifest already outside of Vite so that `runHookConfigDone` can use it to inform integrations of the build output + const manifest = createRouteManifest({ settings, fsMod: fs }, logger); + + injectImageEndpoint(settings, manifest, 'dev'); + const viteConfig = await createVite( { mode: 'development', @@ -89,8 +93,9 @@ export async function createContainer({ include: rendererClientEntries, }, }, - { settings, logger, mode: 'dev', command: 'dev', fs, sync: false }, + { settings, logger, mode: 'dev', command: 'dev', fs, sync: false, manifest }, ); + await runHookConfigDone({ settings, logger }); await syncInternal({ settings, diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index a8a533fe85b7..a1a58866a5eb 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -11,6 +11,7 @@ import { resolveConfig } from '../config/config.js'; import { createNodeLogger } from '../config/logging.js'; import { createSettings } from '../config/settings.js'; import { apply as applyPolyfills } from '../polyfill.js'; +import { createRouteManifest } from '../routing/index.js'; import { ensureProcessNodeEnv } from '../util.js'; import createStaticPreviewServer from './static-preview-server.js'; import { getResolvedHostForHttpServer } from './util.js'; @@ -35,9 +36,13 @@ export default async function preview(inlineConfig: AstroInlineConfig): Promise< command: 'preview', logger: logger, }); + + // Create a route manifest so we can know if the build output is a static site or not + createRouteManifest({ settings: settings, cwd: inlineConfig.root }, logger); + await runHookConfigDone({ settings: settings, logger: logger }); - if (settings.config.output === 'static') { + if (settings.buildOutput === 'static') { if (!fs.existsSync(settings.config.outDir)) { const outDirPath = fileURLToPath(settings.config.outDir); throw new Error( @@ -47,9 +52,11 @@ export default async function preview(inlineConfig: AstroInlineConfig): Promise< const server = await createStaticPreviewServer(settings, logger); return server; } + if (!settings.adapter) { throw new Error(`[preview] No adapter found.`); } + if (!settings.adapter.previewEntrypoint) { throw new Error( `[preview] The ${settings.adapter.name} adapter does not support the preview command.`, diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 1cb2fb75917a..b6cd8d161627 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -286,13 +286,7 @@ function createInjectedRoutes({ settings, cwd }: CreateRouteManifestParams): Pri for (const injectedRoute of settings.injectedRoutes) { const { pattern: name, entrypoint, prerender: prerenderInjected } = injectedRoute; - let resolved: string; - try { - resolved = require.resolve(entrypoint, { paths: [cwd || fileURLToPath(config.root)] }); - } catch { - resolved = fileURLToPath(new URL(entrypoint, config.root)); - } - const component = slash(path.relative(cwd || fileURLToPath(config.root), resolved)); + const { resolved, component } = resolveInjectedRoute(entrypoint, config.root, cwd); const segments = removeLeadingForwardSlash(name) .split(path.posix.sep) @@ -526,7 +520,8 @@ export function createRouteManifest( // Scan all the routes to see if they're prerendered or not for (const route of routes) { - const content = nodeFs.readFileSync( + const localFs = params.fsMod ?? nodeFs; + const content = localFs.readFileSync( fileURLToPath(new URL(route.component, config.root)), 'utf-8', ); @@ -539,6 +534,11 @@ export function createRouteManifest( } } + // Edge case: If there's no routes, but the config is set to server output, assume it's a server + if (routes.length === 0 && settings.config.output === 'server') { + settings.buildOutput = 'server'; + } + // Report route collisions if (config.experimental.globalRoutePriority) { for (const [index, higherRoute] of routes.entries()) { @@ -744,6 +744,20 @@ export function createRouteManifest( }; } +export function resolveInjectedRoute(entrypoint: string, root: URL, cwd?: string) { + let resolved; + try { + resolved = require.resolve(entrypoint, { paths: [cwd || fileURLToPath(root)] }); + } catch { + resolved = fileURLToPath(new URL(entrypoint, root)); + } + + return { + resolved: resolved, + component: slash(path.relative(cwd || fileURLToPath(root), resolved)), + }; +} + function computeRoutePriority(config: AstroConfig): RoutePriorityOverride { if (config.experimental.globalRoutePriority) { return 'normal'; diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 41c7a9bdd09b..87b3b7f10c71 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -1,3 +1,4 @@ +import assert from 'node:assert'; import fsMod from 'node:fs'; import type { AddressInfo } from 'node:net'; import { fileURLToPath } from 'node:url'; @@ -294,6 +295,7 @@ export async function runHookConfigDone({ }) { for (const integration of settings.config.integrations) { if (integration?.hooks?.['astro:config:done']) { + assert(settings.buildOutput, 'buildOutput must be set before running astro:config:done'); await withTakingALongTimeMsg({ name: integration.name, hookName: 'astro:config:done', @@ -332,6 +334,10 @@ export async function runHookConfigDone({ } } settings.adapter = adapter; + + if (adapter.adapterFeatures?.forceServerOutput) { + settings.buildOutput = 'server'; + } }, injectTypes(injectedType) { const normalizedFilename = normalizeInjectedTypeFilename( @@ -349,6 +355,7 @@ export async function runHookConfigDone({ return new URL(normalizedFilename, settings.dotAstroDir); }, logger: getLogger(integration, logger), + buildOutput: settings.buildOutput, }), logger, }); diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts index 129d11215881..27e8e0a04ab5 100644 --- a/packages/astro/src/types/public/integrations.ts +++ b/packages/astro/src/types/public/integrations.ts @@ -66,6 +66,10 @@ export interface AstroAdapterFeatures { * Creates an edge function that will communiate with the Astro middleware */ edgeMiddleware: boolean; + /** + * Force Astro to output a server output, even if all the pages are prerendered + */ + forceServerOutput: boolean; } export interface AstroAdapter { @@ -191,6 +195,7 @@ export interface BaseIntegrationHooks { setAdapter: (adapter: AstroAdapter) => void; injectTypes: (injectedType: InjectedType) => URL; logger: AstroIntegrationLogger; + buildOutput: 'static' | 'server'; }) => void | Promise; 'astro:server:setup': (options: { server: ViteDevServer; diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index 41403456176b..1b909559677a 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -24,23 +24,27 @@ export interface AstroPluginOptions { settings: AstroSettings; logger: Logger; fs: typeof fs; + manifest: ManifestData; } export default function createVitePluginAstroServer({ settings, logger, fs: fsMod, + manifest: routeManifest, }: AstroPluginOptions): vite.Plugin { return { name: 'astro:server', configureServer(viteServer) { const loader = createViteLoader(viteServer); - const manifest = createDevelopmentManifest(settings); - let manifestData: ManifestData = injectDefaultRoutes( - manifest, - createRouteManifest({ settings, fsMod }, logger), - ); - const pipeline = DevPipeline.create(manifestData, { loader, logger, manifest, settings }); + const devSSRManifest = createDevelopmentManifest(settings); + let manifestData: ManifestData = injectDefaultRoutes(devSSRManifest, routeManifest); + const pipeline = DevPipeline.create(manifestData, { + loader, + logger, + manifest: devSSRManifest, + settings, + }); const controller = createController({ loader }); const localStorage = new AsyncLocalStorage(); @@ -48,7 +52,10 @@ export default function createVitePluginAstroServer({ function rebuildManifest(needsManifestRebuild: boolean) { pipeline.clearRouteCache(); if (needsManifestRebuild) { - manifestData = injectDefaultRoutes(manifest, createRouteManifest({ settings }, logger)); + manifestData = injectDefaultRoutes( + devSSRManifest, + createRouteManifest({ settings, fsMod }, logger), + ); pipeline.setManifestData(manifestData); } } diff --git a/packages/astro/test/fixtures/error-bad-js/src/env.d.ts b/packages/astro/test/fixtures/error-bad-js/src/env.d.ts index f964fe0cffd8..acef35f175aa 100644 --- a/packages/astro/test/fixtures/error-bad-js/src/env.d.ts +++ b/packages/astro/test/fixtures/error-bad-js/src/env.d.ts @@ -1 +1,2 @@ +/// /// diff --git a/packages/astro/test/fixtures/error-build-location/src/env.d.ts b/packages/astro/test/fixtures/error-build-location/src/env.d.ts index f964fe0cffd8..acef35f175aa 100644 --- a/packages/astro/test/fixtures/error-build-location/src/env.d.ts +++ b/packages/astro/test/fixtures/error-build-location/src/env.d.ts @@ -1 +1,2 @@ +/// /// diff --git a/packages/astro/test/fixtures/error-non-error/src/env.d.ts b/packages/astro/test/fixtures/error-non-error/src/env.d.ts index f964fe0cffd8..acef35f175aa 100644 --- a/packages/astro/test/fixtures/error-non-error/src/env.d.ts +++ b/packages/astro/test/fixtures/error-non-error/src/env.d.ts @@ -1 +1,2 @@ +/// /// diff --git a/packages/astro/test/fixtures/streaming/src/pages/index.astro b/packages/astro/test/fixtures/streaming/src/pages/index.astro index dd680eba7650..c40dfe95cd86 100644 --- a/packages/astro/test/fixtures/streaming/src/pages/index.astro +++ b/packages/astro/test/fixtures/streaming/src/pages/index.astro @@ -3,6 +3,8 @@ import AsyncEach from '../components/AsyncEach.astro'; import Header from '../components/Header.astro'; import { wait } from '../wait'; +export const prerender = false; + async function * list() { const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; for(const num of nums) { diff --git a/packages/astro/test/fixtures/streaming/src/pages/multiple-errors.astro b/packages/astro/test/fixtures/streaming/src/pages/multiple-errors.astro index 7d922ef0d809..0bfefd2959f9 100644 --- a/packages/astro/test/fixtures/streaming/src/pages/multiple-errors.astro +++ b/packages/astro/test/fixtures/streaming/src/pages/multiple-errors.astro @@ -1,6 +1,8 @@ --- import ReactComp from '../components/react.tsx'; +export const prerender = false; + const foo = { bar: null } as any; --- diff --git a/packages/astro/test/fixtures/streaming/src/pages/slot.astro b/packages/astro/test/fixtures/streaming/src/pages/slot.astro index 8c41d3a5bbb7..a4f13e4d231c 100644 --- a/packages/astro/test/fixtures/streaming/src/pages/slot.astro +++ b/packages/astro/test/fixtures/streaming/src/pages/slot.astro @@ -1,6 +1,8 @@ --- import BareComponent from '../components/BareComponent.astro'; import Wait from '../components/Wait.astro'; + +export const prerender = false; --- diff --git a/packages/astro/test/test-adapter.js b/packages/astro/test/test-adapter.js index 4a2ac65d0047..fc39237199d9 100644 --- a/packages/astro/test/test-adapter.js +++ b/packages/astro/test/test-adapter.js @@ -113,6 +113,9 @@ export default function ({ assets: 'stable', i18nDomains: 'stable', }, + adapterFeatures: { + forceServerOutput: true, + }, ...extendAdapter, }); }, From 00c0fd2f7ef7d6ff03ad6df398fae03276009254 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Sat, 24 Aug 2024 01:39:36 +0200 Subject: [PATCH 04/27] fix: make forceServerOutput optional --- packages/astro/src/types/public/integrations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts index a2c090ae01f3..373c43907875 100644 --- a/packages/astro/src/types/public/integrations.ts +++ b/packages/astro/src/types/public/integrations.ts @@ -69,7 +69,7 @@ export interface AstroAdapterFeatures { /** * Force Astro to output a server output, even if all the pages are prerendered */ - forceServerOutput: boolean; + forceServerOutput?: boolean; } export interface AstroAdapter { From dcd3d4de7df2a6b93d882c41a88a65e349d06d1c Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Sat, 24 Aug 2024 02:14:56 +0200 Subject: [PATCH 05/27] fix: more tests passing --- packages/astro/src/core/routing/manifest/create.ts | 9 ++------- packages/astro/src/core/sync/index.ts | 2 ++ packages/astro/test/build-assets.test.js | 8 ++++++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index a61b13de2e06..60e6e014c629 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -497,11 +497,11 @@ export function createRouteManifest( ...[...fileBasedRoutes, ...injectedRoutes, ...redirectRoutes].sort(routeComparator), ]; - // Assume static output unless we find a server-rendered route - settings.buildOutput = 'static'; + settings.buildOutput = getPrerenderDefault(config) ? 'static' : 'server'; // Scan all the routes to see if they're prerendered or not for (const route of routes) { + if (route.type !== 'page' && route.type !== 'endpoint') continue; const localFs = params.fsMod ?? nodeFs; const content = localFs.readFileSync( fileURLToPath(new URL(route.component, config.root)), @@ -516,11 +516,6 @@ export function createRouteManifest( } } - // Edge case: If there's no routes, but the config is set to server output, assume it's a server - if (routes.length === 0 && settings.config.output === 'server') { - settings.buildOutput = 'server'; - } - // Report route collisions for (const [index, higherRoute] of routes.entries()) { for (const lowerRoute of routes.slice(index + 1)) { diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index c9769443ee51..f96e66fc64e2 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -29,6 +29,7 @@ import { } from '../errors/index.js'; import type { Logger } from '../logger/core.js'; import { formatErrorMessage } from '../messages.js'; +import { createRouteManifest } from '../routing/index.js'; import { ensureProcessNodeEnv } from '../util.js'; import { writeFiles } from './write-files.js'; @@ -62,6 +63,7 @@ export default async function sync( settings, logger, }); + createRouteManifest({ settings, fsMod: fs }, logger); await runHookConfigDone({ settings, logger }); return await syncInternal({ settings, logger, fs, force: inlineConfig.force }); } diff --git a/packages/astro/test/build-assets.test.js b/packages/astro/test/build-assets.test.js index b33b0a0d1eba..4507345da427 100644 --- a/packages/astro/test/build-assets.test.js +++ b/packages/astro/test/build-assets.test.js @@ -99,7 +99,7 @@ describe('build assets (server)', () => { fixture = await loadFixture({ root: './fixtures/build-assets/', integrations: [preact()], - adapter: testAdapter(), + adapter: testAdapter({ extendAdapter: { adapterFeatures: { forceServerOutput: false } } }), // test suite was authored when inlineStylesheets defaulted to never build: { inlineStylesheets: 'never' }, }); @@ -148,7 +148,11 @@ describe('build assets (server)', () => { assets: 'custom-assets', inlineStylesheets: 'never', }, - adapter: testAdapter(), + adapter: testAdapter({extendAdapter: { + adapterFeatures: { + forceServerOutput: false, + } + }}), }); await fixture.build(); }); From 2ae0754e78e235c5a0c7cd80d2ade84b97f1718c Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Sat, 24 Aug 2024 02:30:25 +0200 Subject: [PATCH 06/27] fix: http2 test --- packages/astro/test/fixtures/astro-dev-http2/astro.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/test/fixtures/astro-dev-http2/astro.config.ts b/packages/astro/test/fixtures/astro-dev-http2/astro.config.ts index b02d07ce5de8..3b6406ec5db8 100644 --- a/packages/astro/test/fixtures/astro-dev-http2/astro.config.ts +++ b/packages/astro/test/fixtures/astro-dev-http2/astro.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from "astro/config"; import { readFileSync } from "fs"; // https://astro.build/config export default defineConfig({ - output: "hybrid", + output: "static", vite: { server: { https: { From 84b6d231b52f66eaa21ba25ed5b66440cabdc388 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Sat, 24 Aug 2024 13:33:50 +0200 Subject: [PATCH 07/27] fix: CCC --- packages/astro/src/core/build/plugins/plugin-pages.ts | 2 +- packages/astro/src/core/build/static-build.ts | 4 +--- packages/astro/src/core/util.ts | 2 +- packages/astro/src/integrations/features-validation.ts | 2 ++ 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/astro/src/core/build/plugins/plugin-pages.ts b/packages/astro/src/core/build/plugins/plugin-pages.ts index 2dc400a6c25d..5ac60fe5695d 100644 --- a/packages/astro/src/core/build/plugins/plugin-pages.ts +++ b/packages/astro/src/core/build/plugins/plugin-pages.ts @@ -14,7 +14,7 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V return { name: '@astro/plugin-build-pages', options(options) { - if (opts.settings.config.output === 'static') { + if (opts.settings.buildOutput === 'static') { const inputs = new Set(); for (const pageData of Object.values(opts.allPages)) { diff --git a/packages/astro/src/core/build/static-build.ts b/packages/astro/src/core/build/static-build.ts index 9b293b87aef4..3fffe9626424 100644 --- a/packages/astro/src/core/build/static-build.ts +++ b/packages/astro/src/core/build/static-build.ts @@ -169,8 +169,6 @@ async function ssrBuild( ) { const buildID = Date.now().toString(); const { allPages, settings, viteConfig } = opts; - - // TODO: This is inaccurate, we don't know yet if we're building SSR or not const ssr = settings.buildOutput === 'server'; const out = getOutputDirectory(settings); const routes = Object.values(allPages).flatMap((pageData) => pageData.route); @@ -475,7 +473,7 @@ export async function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles async function ssrMoveAssets(opts: StaticBuildOptions) { opts.logger.info('build', 'Rearranging server assets...'); const serverRoot = - opts.settings.config.output === 'static' + opts.settings.buildOutput === 'static' ? opts.settings.config.build.client : opts.settings.config.build.server; const clientRoot = opts.settings.config.build.client; diff --git a/packages/astro/src/core/util.ts b/packages/astro/src/core/util.ts index 0904cf6ac855..3375deaef3e3 100644 --- a/packages/astro/src/core/util.ts +++ b/packages/astro/src/core/util.ts @@ -162,7 +162,7 @@ export function isContentCollectionsCacheEnabled(settings: AstroSettings): boole return ( settings.config.experimental.contentCollectionCache && // contentCollectionsCache is an SSG only feature - settings.buildOutput !== 'static' + settings.buildOutput !== 'server' ); } diff --git a/packages/astro/src/integrations/features-validation.ts b/packages/astro/src/integrations/features-validation.ts index 0a59f48a3b25..a0e8758a408e 100644 --- a/packages/astro/src/integrations/features-validation.ts +++ b/packages/astro/src/integrations/features-validation.ts @@ -53,6 +53,8 @@ export function validateSupportedFeatures( () => config?.output === 'static', ); + // TODO: Update these checks to make sense for the new hybrid output + validationResult.hybridOutput = validateSupportKind( hybridOutput, adapterName, From afc869bf95663b345621b1dd2e7f111c604551a9 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:28:11 +0200 Subject: [PATCH 08/27] fix: get unit tests passing --- packages/astro/package.json | 3 +- .../src/integrations/features-validation.ts | 17 +++++---- packages/astro/src/integrations/hooks.ts | 2 +- .../astro/test/units/integrations/api.test.js | 38 +++++++++++-------- .../astro/test/units/routing/manifest.test.js | 19 ++++++---- 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/packages/astro/package.json b/packages/astro/package.json index a448146eedea..c7d58c6d4b94 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -118,7 +118,8 @@ "test:e2e:chrome": "playwright test", "test:e2e:firefox": "playwright test --config playwright.firefox.config.js", "test:types": "tsc --project tsconfig.tests.json", - "test:node": "astro-scripts test \"test/**/*.test.js\"" + "test:node": "astro-scripts test \"test/**/*.test.js\"", + "test:units": "astro-scripts test \"test/**/units/**/*.test.js\"" }, "dependencies": { "@astrojs/compiler": "^2.10.3", diff --git a/packages/astro/src/integrations/features-validation.ts b/packages/astro/src/integrations/features-validation.ts index a0e8758a408e..8d4400e8044b 100644 --- a/packages/astro/src/integrations/features-validation.ts +++ b/packages/astro/src/integrations/features-validation.ts @@ -1,4 +1,5 @@ import type { Logger } from '../core/logger/core.js'; +import type { AstroSettings } from '../types/astro.js'; import type { AstroConfig } from '../types/public/config.js'; import type { AdapterSupportsKind, @@ -31,7 +32,7 @@ type ValidationResult = { export function validateSupportedFeatures( adapterName: string, featureMap: AstroAdapterFeatureMap, - config: AstroConfig, + settings: AstroSettings, adapterFeatures: AstroAdapterFeatures | undefined, logger: Logger, ): ValidationResult { @@ -50,7 +51,7 @@ export function validateSupportedFeatures( adapterName, logger, 'staticOutput', - () => config?.output === 'static', + () => settings.buildOutput === 'static', ); // TODO: Update these checks to make sense for the new hybrid output @@ -60,7 +61,7 @@ export function validateSupportedFeatures( adapterName, logger, 'hybridOutput', - () => false, + () => settings.config.output == 'static' && settings.buildOutput === 'server', ); validationResult.serverOutput = validateSupportKind( @@ -68,18 +69,18 @@ export function validateSupportedFeatures( adapterName, logger, 'serverOutput', - () => config?.output === 'server', + () => settings.config?.output === 'server', ); - validationResult.assets = validateAssetsFeature(assets, adapterName, config, logger); + validationResult.assets = validateAssetsFeature(assets, adapterName, settings.config, logger); - if (config.i18n?.domains) { + if (settings.config.i18n?.domains) { validationResult.i18nDomains = validateSupportKind( i18nDomains, adapterName, logger, 'i18nDomains', () => { - return config?.output === 'server' && !config?.site; + return settings.config?.output === 'server' && !settings.config?.site; }, ); } @@ -89,7 +90,7 @@ export function validateSupportedFeatures( adapterName, logger, 'astro:env getSecret', - () => Object.keys(config?.env?.schema ?? {}).length !== 0, + () => Object.keys(settings.config?.env?.schema ?? {}).length !== 0, ); return validationResult; diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 82f88cf8fc52..26cc971fdebb 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -315,7 +315,7 @@ export async function runHookConfigDone({ const validationResult = validateSupportedFeatures( adapter.name, adapter.supportedAstroFeatures, - settings.config, + settings, // SAFETY: we checked before if it's not present, and we throw an error adapter.adapterFeatures, logger, diff --git a/packages/astro/test/units/integrations/api.test.js b/packages/astro/test/units/integrations/api.test.js index 10b282b7ce99..ca41a23b0a06 100644 --- a/packages/astro/test/units/integrations/api.test.js +++ b/packages/astro/test/units/integrations/api.test.js @@ -128,7 +128,7 @@ describe('Astro feature map', function () { hybridOutput: 'stable', }, { - output: 'static', + config: { output: 'static' }, }, {}, defaultLogger, @@ -141,7 +141,8 @@ describe('Astro feature map', function () { 'test', {}, { - output: 'static', + buildOutput: 'server', + config: { output: 'static' }, }, {}, defaultLogger, @@ -154,7 +155,8 @@ describe('Astro feature map', function () { 'test', {}, { - output: 'static', + buildOutput: 'server', + config: { output: 'static' }, }, {}, defaultLogger, @@ -168,7 +170,7 @@ describe('Astro feature map', function () { 'test', { staticOutput: 'stable' }, { - output: 'static', + config: { output: 'static' }, }, {}, defaultLogger, @@ -181,7 +183,8 @@ describe('Astro feature map', function () { 'test', { staticOutput: 'unsupported' }, { - output: 'static', + buildOutput: 'static', + config: { output: 'static' }, }, {}, defaultLogger, @@ -195,7 +198,7 @@ describe('Astro feature map', function () { 'test', { hybridOutput: 'stable' }, { - output: 'static', + config: { output: 'static' }, }, {}, defaultLogger, @@ -210,7 +213,8 @@ describe('Astro feature map', function () { hybridOutput: 'unsupported', }, { - output: 'static', + buildOutput: 'server', + config: { output: 'static' }, }, {}, defaultLogger, @@ -224,7 +228,7 @@ describe('Astro feature map', function () { 'test', { serverOutput: 'stable' }, { - output: 'server', + config: { output: 'server' }, }, {}, defaultLogger, @@ -239,7 +243,7 @@ describe('Astro feature map', function () { serverOutput: 'unsupported', }, { - output: 'server', + config: { output: 'server' }, }, {}, defaultLogger, @@ -259,9 +263,11 @@ describe('Astro feature map', function () { }, }, { - image: { - service: { - entrypoint: 'astro/assets/services/sharp', + config: { + image: { + service: { + entrypoint: 'astro/assets/services/sharp', + }, }, }, }, @@ -281,9 +287,11 @@ describe('Astro feature map', function () { }, }, { - image: { - service: { - entrypoint: 'astro/assets/services/sharp', + config: { + image: { + service: { + entrypoint: 'astro/assets/services/sharp', + }, }, }, }, diff --git a/packages/astro/test/units/routing/manifest.test.js b/packages/astro/test/units/routing/manifest.test.js index 518a2fc8f0e3..9f6710a4bf9c 100644 --- a/packages/astro/test/units/routing/manifest.test.js +++ b/packages/astro/test/units/routing/manifest.test.js @@ -67,6 +67,8 @@ describe('routing - createRouteManifest', () => { { '/src/pages/[contact].astro': `

test

`, '/src/pages/[contact].ts': `

test

`, + '/src/entrypoint.astro': `

test

`, + '/src/entrypoint.ts': `

test

`, }, root, ); @@ -79,11 +81,11 @@ describe('routing - createRouteManifest', () => { settings.injectedRoutes = [ { pattern: '/about', - entrypoint: '@lib/legacy/static.astro', + entrypoint: 'src/entrypoint.astro', }, { pattern: '/api', - entrypoint: '@lib/legacy/static.ts', + entrypoint: 'src/entrypoint.ts', }, ]; @@ -255,6 +257,7 @@ describe('routing - createRouteManifest', () => { { '/src/pages/index.astro': `

test

`, '/src/pages/blog/[...slug].astro': `

test

`, + '/src/entrypoint.astro': `

test

`, }, root, ); @@ -268,11 +271,11 @@ describe('routing - createRouteManifest', () => { settings.injectedRoutes = [ { pattern: '/contributing', - entrypoint: '@lib/legacy/static.astro', + entrypoint: 'src/entrypoint.astro', }, { pattern: '/[...slug]', - entrypoint: '@lib/legacy/dynamic.astro', + entrypoint: 'src/entrypoint.astro', priority: 'normal', }, ]; @@ -357,6 +360,7 @@ describe('routing - createRouteManifest', () => { const fs = createFs( { '/src/pages/contributing.astro': `

test

`, + '/src/entrypoint.astro': `

test

`, }, root, ); @@ -371,7 +375,7 @@ describe('routing - createRouteManifest', () => { settings.injectedRoutes = [ { pattern: '/contributing', - entrypoint: '@lib/legacy/static.astro', + entrypoint: 'src/entrypoint.astro', }, ]; @@ -390,7 +394,7 @@ describe('routing - createRouteManifest', () => { label: 'router', level: 'warn', message: - 'The route "/contributing" is defined in both "src/pages/contributing.astro" and "@lib/legacy/static.astro". A static route cannot be defined more than once.', + 'The route "/contributing" is defined in both "src/pages/contributing.astro" and "src/entrypoint.astro". A static route cannot be defined more than once.', newLine: true, }, { @@ -450,6 +454,7 @@ describe('routing - createRouteManifest', () => { { '/src/pages/a-[b].astro': `

test

`, '/src/pages/blog/a-[b].233.ts': ``, + '/src/entrypoint.astro': `

test

`, }, root, ); @@ -467,7 +472,7 @@ describe('routing - createRouteManifest', () => { settings.injectedRoutes = [ { pattern: '/[c]-d', - entrypoint: '@lib/legacy/dynamic.astro', + entrypoint: 'src/entrypoint.astro', priority: 'normal', }, ]; From 9fb39b8448cbe19700f3dce5417633136c50c9eb Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:34:23 +0200 Subject: [PATCH 09/27] fix: lint --- packages/astro/src/vite-plugin-scanner/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/vite-plugin-scanner/index.ts b/packages/astro/src/vite-plugin-scanner/index.ts index f21af5816e1f..b70b4fc4860f 100644 --- a/packages/astro/src/vite-plugin-scanner/index.ts +++ b/packages/astro/src/vite-plugin-scanner/index.ts @@ -2,7 +2,7 @@ import { extname } from 'node:path'; import { bold } from 'kleur/colors'; import type { Plugin as VitePlugin } from 'vite'; import { normalizePath } from 'vite'; -import { type Logger } from '../core/logger/core.js'; +import type { Logger } from '../core/logger/core.js'; import { isEndpoint, isPage } from '../core/util.js'; import { rootRelativePath } from '../core/viteUtils.js'; import { runHookRouteSetup } from '../integrations/hooks.js'; From a892e5053c08a74fb196a4d9053d1d12d58e18cd Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Thu, 29 Aug 2024 13:04:12 +0200 Subject: [PATCH 10/27] fix: vercel --- packages/astro/src/core/build/index.ts | 2 -- packages/astro/src/integrations/hooks.ts | 11 ++++---- .../astro/src/types/public/integrations.ts | 2 +- packages/db/test/local-prod.test.js | 2 +- packages/integrations/node/src/index.ts | 4 +++ .../node/test/prerender-404-500.test.js | 4 +-- .../integrations/node/test/prerender.test.js | 6 ++--- .../node/test/trailing-slash.test.js | 12 ++++----- .../vercel/src/serverless/adapter.ts | 8 +----- .../redirects-serverless/astro.config.mjs | 2 +- .../src/pages/server-rendered.astro | 7 ++++++ packages/integrations/vercel/test/isr.test.js | 8 +++--- .../vercel/test/no-output.test.js | 25 ------------------- .../vercel/test/serverless-prerender.test.js | 2 +- .../serverless-with-dynamic-routes.test.js | 2 +- 15 files changed, 38 insertions(+), 59 deletions(-) create mode 100644 packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/server-rendered.astro delete mode 100644 packages/integrations/vercel/test/no-output.test.js diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index f93705ce7c7a..56f74622bca6 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -118,8 +118,6 @@ class AstroBuilder { logger: logger, }); - // TODO: Make injectImageEndpoint work from the manifest instead of the settings, so that we can add it to the manifest only in server builds - this.manifest = createRouteManifest({ settings: this.settings }, this.logger); if (this.settings.buildOutput === 'server') { diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 26cc971fdebb..61a8daa5d13a 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -307,6 +307,11 @@ export async function runHookConfigDone({ `Integration "${integration.name}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.`, ); } + + if (adapter.adapterFeatures?.forceServerOutput) { + settings.buildOutput = 'server'; + } + if (!adapter.supportedAstroFeatures) { throw new Error( `The adapter ${adapter.name} doesn't provide a feature map. It is required in Astro 4.0.`, @@ -334,10 +339,6 @@ export async function runHookConfigDone({ } } settings.adapter = adapter; - - if (adapter.adapterFeatures?.forceServerOutput) { - settings.buildOutput = 'server'; - } }, injectTypes(injectedType) { const normalizedFilename = normalizeInjectedTypeFilename( @@ -355,7 +356,7 @@ export async function runHookConfigDone({ return new URL(normalizedFilename, settings.dotAstroDir); }, logger: getLogger(integration, logger), - buildOutput: settings.buildOutput, + buildOutput: () => settings.buildOutput!, // settings.buildOutput is always set at this point }), logger, }); diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts index 822183a9ad5c..8ed61e88181c 100644 --- a/packages/astro/src/types/public/integrations.ts +++ b/packages/astro/src/types/public/integrations.ts @@ -186,7 +186,7 @@ export interface BaseIntegrationHooks { setAdapter: (adapter: AstroAdapter) => void; injectTypes: (injectedType: InjectedType) => URL; logger: AstroIntegrationLogger; - buildOutput: 'static' | 'server'; + buildOutput: () => 'static' | 'server'; }) => void | Promise; 'astro:server:setup': (options: { server: ViteDevServer; diff --git a/packages/db/test/local-prod.test.js b/packages/db/test/local-prod.test.js index 6513aeb08572..9bd56dad05f0 100644 --- a/packages/db/test/local-prod.test.js +++ b/packages/db/test/local-prod.test.js @@ -71,7 +71,7 @@ describe('astro:db local database', () => { it('should throw during the build for hybrid output', async () => { let fixture2 = await loadFixture({ root: new URL('./fixtures/local-prod/', import.meta.url), - output: 'hybrid', + output: 'static', adapter: testAdapter(), }); diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index 42a2ed91f24f..f76747279162 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -9,6 +9,10 @@ export function getAdapter(options: Options): AstroAdapter { previewEntrypoint: '@astrojs/node/preview.js', exports: ['handler', 'startServer', 'options'], args: options, + adapterFeatures: { + forceServerOutput: true, + edgeMiddleware: false, + }, supportedAstroFeatures: { hybridOutput: 'stable', staticOutput: 'stable', diff --git a/packages/integrations/node/test/prerender-404-500.test.js b/packages/integrations/node/test/prerender-404-500.test.js index 2535fcb35bf7..b318c5d83ddd 100644 --- a/packages/integrations/node/test/prerender-404-500.test.js +++ b/packages/integrations/node/test/prerender-404-500.test.js @@ -183,7 +183,7 @@ describe('Hybrid 404', () => { site: 'https://test.com/', base: '/some-base', root: './fixtures/prerender-404-500/', - output: 'hybrid', + output: 'static', outDir: './dist/hybrid-with-base', build: { client: './dist/hybrid-with-base/client', @@ -245,7 +245,7 @@ describe('Hybrid 404', () => { // from being reused site: 'https://test.net/', root: './fixtures/prerender-404-500/', - output: 'hybrid', + output: 'static', outDir: './dist/hybrid-without-base', build: { client: './dist/hybrid-without-base/client', diff --git a/packages/integrations/node/test/prerender.test.js b/packages/integrations/node/test/prerender.test.js index e699a1b3c01b..ae5220c54f3d 100644 --- a/packages/integrations/node/test/prerender.test.js +++ b/packages/integrations/node/test/prerender.test.js @@ -267,7 +267,7 @@ describe('Hybrid rendering', () => { fixture = await loadFixture({ base: '/some-base', root: './fixtures/prerender/', - output: 'hybrid', + output: 'static', outDir: './dist/hybrid-with-base', build: { client: './dist/hybrid-with-base/client', @@ -340,7 +340,7 @@ describe('Hybrid rendering', () => { process.env.PRERENDER = false; fixture = await loadFixture({ root: './fixtures/prerender/', - output: 'hybrid', + output: 'static', outDir: './dist/hybrid-without-base', build: { client: './dist/hybrid-without-base/client', @@ -405,7 +405,7 @@ describe('Hybrid rendering', () => { fixture = await loadFixture({ root: './fixtures/prerender/', - output: 'hybrid', + output: 'static', outDir: './dist/hybrid-shared-modules', build: { client: './dist/hybrid-shared-modules/client', diff --git a/packages/integrations/node/test/trailing-slash.test.js b/packages/integrations/node/test/trailing-slash.test.js index 9ea8fcdddc39..050ddfa47b2e 100644 --- a/packages/integrations/node/test/trailing-slash.test.js +++ b/packages/integrations/node/test/trailing-slash.test.js @@ -21,7 +21,7 @@ describe('Trailing slash', () => { fixture = await loadFixture({ root: './fixtures/trailing-slash/', base: '/some-base', - output: 'hybrid', + output: 'static', trailingSlash: 'always', outDir: './dist/always-with-base', build: { @@ -92,7 +92,7 @@ describe('Trailing slash', () => { fixture = await loadFixture({ root: './fixtures/trailing-slash/', - output: 'hybrid', + output: 'static', trailingSlash: 'always', outDir: './dist/always-without-base', build: { @@ -166,7 +166,7 @@ describe('Trailing slash', () => { fixture = await loadFixture({ root: './fixtures/trailing-slash/', base: '/some-base', - output: 'hybrid', + output: 'static', trailingSlash: 'never', outDir: './dist/never-with-base', build: { @@ -230,7 +230,7 @@ describe('Trailing slash', () => { fixture = await loadFixture({ root: './fixtures/trailing-slash/', - output: 'hybrid', + output: 'static', trailingSlash: 'never', outDir: './dist/never-without-base', build: { @@ -297,7 +297,7 @@ describe('Trailing slash', () => { fixture = await loadFixture({ root: './fixtures/trailing-slash/', base: '/some-base', - output: 'hybrid', + output: 'static', trailingSlash: 'ignore', outDir: './dist/ignore-with-base', build: { @@ -379,7 +379,7 @@ describe('Trailing slash', () => { fixture = await loadFixture({ root: './fixtures/trailing-slash/', - output: 'hybrid', + output: 'static', trailingSlash: 'ignore', outDir: './dist/ignore-without-base', build: { diff --git a/packages/integrations/vercel/src/serverless/adapter.ts b/packages/integrations/vercel/src/serverless/adapter.ts index 3b86cec0e160..e52d5854f5ba 100644 --- a/packages/integrations/vercel/src/serverless/adapter.ts +++ b/packages/integrations/vercel/src/serverless/adapter.ts @@ -9,7 +9,6 @@ import type { AstroIntegrationLogger, RouteData, } from 'astro'; -import { AstroError } from 'astro/errors'; import glob from 'fast-glob'; import { type DevImageService, @@ -84,6 +83,7 @@ function getAdapter({ args: { middlewareSecret, skewProtection }, adapterFeatures: { edgeMiddleware, + forceServerOutput: true, }, supportedAstroFeatures: { hybridOutput: 'stable', @@ -276,12 +276,6 @@ export default function vercelServerless({ _config = config; _buildTempFolder = config.build.server; _serverEntry = config.build.serverEntry; - - if (config.output === 'static') { - throw new AstroError( - '`output: "server"` or `output: "hybrid"` is required to use the serverless adapter.', - ); - } }, 'astro:build:ssr': async ({ entryPoints, middlewareEntryPoint }) => { _entryPoints = new Map( diff --git a/packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs b/packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs index 1071b7e67160..04e8ffa9dff6 100644 --- a/packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs +++ b/packages/integrations/vercel/test/fixtures/redirects-serverless/astro.config.mjs @@ -2,6 +2,6 @@ import vercel from '@astrojs/vercel/serverless'; import { defineConfig } from 'astro/config'; export default defineConfig({ - output: 'hybrid', + output: 'static', adapter: vercel(), }); diff --git a/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/server-rendered.astro b/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/server-rendered.astro new file mode 100644 index 000000000000..a4a2e0957735 --- /dev/null +++ b/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/server-rendered.astro @@ -0,0 +1,7 @@ +--- +export const prerender = false; +--- + +
+

Server Rendered

+
diff --git a/packages/integrations/vercel/test/isr.test.js b/packages/integrations/vercel/test/isr.test.js index fb957aa78b56..8e08bf5d61c8 100644 --- a/packages/integrations/vercel/test/isr.test.js +++ b/packages/integrations/vercel/test/isr.test.js @@ -37,10 +37,6 @@ describe('ISR', () => { src: '^/excluded/([^/]+?)$', dest: '_render', }, - { - src: '^\\/_image$', - dest: '_render', - }, { src: '^\\/excluded\\/([^/]+?)\\/?$', dest: '/_isr?x_astro_path=$0', @@ -53,6 +49,10 @@ describe('ISR', () => { src: '^\\/two\\/?$', dest: '/_isr?x_astro_path=$0', }, + { + src: '^\\/_image$', + dest: '_render', + }, ]); }); }); diff --git a/packages/integrations/vercel/test/no-output.test.js b/packages/integrations/vercel/test/no-output.test.js deleted file mode 100644 index 34709804d0b8..000000000000 --- a/packages/integrations/vercel/test/no-output.test.js +++ /dev/null @@ -1,25 +0,0 @@ -import assert from 'node:assert/strict'; -import { before, describe, it } from 'node:test'; -import { loadFixture } from './test-utils.js'; - -describe('Missing output config', () => { - /** @type {import('./test-utils').Fixture} */ - let fixture; - - before(async () => { - fixture = await loadFixture({ - root: './fixtures/no-output/', - }); - }); - - it('throws during the build', async () => { - let error = undefined; - try { - await fixture.build(); - } catch (err) { - error = err; - } - assert.notEqual(error, undefined); - assert.match(error.message, /output: "server"/); - }); -}); diff --git a/packages/integrations/vercel/test/serverless-prerender.test.js b/packages/integrations/vercel/test/serverless-prerender.test.js index 61e239300b88..6bf1645d9e4b 100644 --- a/packages/integrations/vercel/test/serverless-prerender.test.js +++ b/packages/integrations/vercel/test/serverless-prerender.test.js @@ -44,7 +44,7 @@ describe('Serverless hybrid rendering', () => { process.env.PRERENDER = true; fixture = await loadFixture({ root: './fixtures/serverless-prerender/', - output: 'hybrid', + output: 'static', }); await fixture.build(); }); diff --git a/packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js b/packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js index 7eac7e875ff4..19ae4af1a8ca 100644 --- a/packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js +++ b/packages/integrations/vercel/test/serverless-with-dynamic-routes.test.js @@ -10,7 +10,7 @@ describe('Serverless with dynamic routes', () => { process.env.PRERENDER = true; fixture = await loadFixture({ root: './fixtures/serverless-with-dynamic-routes/', - output: 'hybrid', + output: 'static', }); await fixture.build(); }); From a7ad17984469834c40a6dd645a2e6a8e1ea4b5a5 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Thu, 29 Aug 2024 13:08:28 +0200 Subject: [PATCH 11/27] fix: build --- packages/astro/src/actions/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/actions/index.ts b/packages/astro/src/actions/index.ts index fbe321cad626..014451a64436 100644 --- a/packages/astro/src/actions/index.ts +++ b/packages/astro/src/actions/index.ts @@ -43,7 +43,7 @@ export default function astroActions({ }); }, 'astro:config:done': (params) => { - if (params.buildOutput !== 'server') { + if (params.buildOutput() !== 'server') { const error = new AstroError(ActionsWithoutServerOutputError); error.stack = undefined; throw error; From 6478ab455defbe3d05ae6f0eb62eee25ce728c89 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Thu, 29 Aug 2024 16:25:01 +0200 Subject: [PATCH 12/27] fix: build --- packages/astro/e2e/custom-client-directives.test.js | 3 ++- packages/astro/src/actions/integration.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/astro/e2e/custom-client-directives.test.js b/packages/astro/e2e/custom-client-directives.test.js index 8f90916f2626..60470b4dd842 100644 --- a/packages/astro/e2e/custom-client-directives.test.js +++ b/packages/astro/e2e/custom-client-directives.test.js @@ -35,12 +35,13 @@ test.describe('Custom Client Directives - build static', () => { testClientDirectivesShared(); }); -test.describe('Custom Client Directives - build server', () => { +test.describe('Custom Client Directives - build server zzz', () => { let previewServer; test.beforeAll(async ({ astro }) => { await astro.build({ adapter: testAdapter(), + output: 'server', }); previewServer = await astro.preview(); }); diff --git a/packages/astro/src/actions/integration.ts b/packages/astro/src/actions/integration.ts index 4c2cc6e04a59..7d185410e5da 100644 --- a/packages/astro/src/actions/integration.ts +++ b/packages/astro/src/actions/integration.ts @@ -1,6 +1,6 @@ import { ActionsWithoutServerOutputError } from '../core/errors/errors-data.js'; import { AstroError } from '../core/errors/errors.js'; -import { isServerLikeOutput, viteID } from '../core/util.js'; +import { viteID } from '../core/util.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroIntegration } from '../types/public/integrations.js'; import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js'; @@ -30,7 +30,7 @@ export default function astroIntegrationActionsRouteHandler({ }); }, 'astro:config:done': async (params) => { - if (!isServerLikeOutput(params.config)) { + if (params.buildOutput() === 'static') { const error = new AstroError(ActionsWithoutServerOutputError); error.stack = undefined; throw error; From 4bb31ff380a9cf6dfb28e394bcfb7df651d5486f Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:08:48 +0200 Subject: [PATCH 13/27] fix: db tests --- packages/astro/src/integrations/hooks.ts | 4 ++-- packages/db/src/core/integration/index.ts | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index bb16d09c9170..6f88415d5f20 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -126,9 +126,9 @@ export async function runHookConfigSetup({ isRestart?: boolean; fs?: typeof fsMod; }): Promise { - // An adapter is an integration, so if one is provided push it. + // An adapter is an integration, so if one is provided add it to the list of integrations. if (settings.config.adapter) { - settings.config.integrations.push(settings.config.adapter); + settings.config.integrations.unshift(settings.config.adapter); } if (await isActionsFilePresent(fs, settings.config.srcDir)) { settings.config.integrations.push(astroIntegrationActionsRouteHandler({ settings })); diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index 519be1b26f7b..a642aaffe147 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -59,6 +59,7 @@ function astroDBIntegration(): AstroIntegration { }; let command: 'dev' | 'build' | 'preview' | 'sync'; + let finalBuildOutput: string; return { name: 'astro:db', hooks: { @@ -103,9 +104,11 @@ function astroDBIntegration(): AstroIntegration { }, }); }, - 'astro:config:done': async ({ config, injectTypes }) => { + 'astro:config:done': async ({ config, injectTypes, buildOutput }) => { if (command === 'preview') return; + finalBuildOutput = buildOutput(); + // TODO: refine where we load tables // @matthewp: may want to load tables by path at runtime const { dbConfig, dependencies, integrationSeedPaths } = await resolveDbConfig(config); @@ -157,7 +160,7 @@ function astroDBIntegration(): AstroIntegration { }, 100); }, 'astro:build:start': async ({ logger }) => { - if (!connectToStudio && !databaseFileEnvDefined()) { + if (!connectToStudio && !databaseFileEnvDefined() && finalBuildOutput === 'server') { const message = `Attempting to build without the --remote flag or the ASTRO_DATABASE_FILE environment variable defined. You probably want to pass --remote to astro build.`; const hint = 'Learn more connecting to Studio: https://docs.astro.build/en/guides/astro-db/#connect-to-astro-studio'; From 0fce8690642e5b412fbe8de6178ba7dcda26948c Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 30 Aug 2024 12:30:26 +0200 Subject: [PATCH 14/27] fix: get all normal tests passing --- .vscode/settings.json | 6 +++--- packages/astro/src/core/errors/errors-data.ts | 7 +++---- packages/astro/src/core/render-context.ts | 7 ++++++- .../astro/src/integrations/features-validation.ts | 2 -- packages/astro/src/runtime/server/endpoint.ts | 6 +++--- packages/astro/src/types/public/config.ts | 15 +++++++-------- .../test/ssr-prerender-get-static-paths.test.js | 2 +- packages/integrations/node/src/index.ts | 8 +------- .../test/fixtures/trailing-slash/astro.config.mjs | 2 +- .../vercel/test/edge-middleware.test.js | 3 ++- packages/integrations/web-vitals/src/index.ts | 8 -------- .../test/fixtures/basics/astro.config.mjs | 2 +- 12 files changed, 28 insertions(+), 40 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index b3e10e7bccee..97aeabec0f36 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -15,7 +15,7 @@ "editor.defaultFormatter": "biomejs.biome" }, "editor.codeActionsOnSave": { - "quickFix.biome": true, - "source.fixAll.biome": true - } + "quickFix.biome": "explicit", + "source.fixAll.biome": "explicit" + } } diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index f4537c000283..fa2c3a04d736 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -92,6 +92,7 @@ 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.", hint: 'See https://docs.astro.build/en/guides/server-side-rendering/ for more information on how to enable SSR.', @@ -400,8 +401,7 @@ export const GetStaticPathsRequired = { '`getStaticPaths()` function is required for dynamic routes. Make sure that you `export` a `getStaticPaths` function from your dynamic route.', hint: `See https://docs.astro.build/en/guides/routing/#dynamic-routes for more information on dynamic routes. -Alternatively, set \`output: "server"\` or \`output: "hybrid"\` in your Astro config file to switch to a non-static server build. This error can also occur if using \`export const prerender = true;\`. -See https://docs.astro.build/en/guides/server-side-rendering/ for more information on non-static rendering.`, + If you meant for this route to be server-rendered, set \`export const prerender = false;\` in the page.`, } satisfies ErrorData; /** * @docs @@ -426,8 +426,7 @@ export const ReservedSlotName = { export const NoAdapterInstalled = { name: 'NoAdapterInstalled', title: 'Cannot use Server-side Rendering without an adapter.', - // TODO: Update this error message - message: `Cannot use \`output: 'server'\` or \`output: 'hybrid'\` without an adapter. Please install and configure the appropriate server adapter for your final deployment.`, + 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; /** diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts index b4b3abb9c2ed..e2f6d3487453 100644 --- a/packages/astro/src/core/render-context.ts +++ b/packages/astro/src/core/render-context.ts @@ -150,7 +150,12 @@ export class RenderContext { switch (this.routeData.type) { case 'endpoint': { - response = await renderEndpoint(componentInstance as any, ctx, serverLike, logger); + response = await renderEndpoint( + componentInstance as any, + ctx, + this.routeData.prerender, + logger, + ); break; } case 'redirect': diff --git a/packages/astro/src/integrations/features-validation.ts b/packages/astro/src/integrations/features-validation.ts index 8d4400e8044b..0d4d081c4e90 100644 --- a/packages/astro/src/integrations/features-validation.ts +++ b/packages/astro/src/integrations/features-validation.ts @@ -54,8 +54,6 @@ export function validateSupportedFeatures( () => settings.buildOutput === 'static', ); - // TODO: Update these checks to make sense for the new hybrid output - validationResult.hybridOutput = validateSupportKind( hybridOutput, adapterName, diff --git a/packages/astro/src/runtime/server/endpoint.ts b/packages/astro/src/runtime/server/endpoint.ts index 674d6209343f..7adc89de0722 100644 --- a/packages/astro/src/runtime/server/endpoint.ts +++ b/packages/astro/src/runtime/server/endpoint.ts @@ -12,7 +12,7 @@ export async function renderEndpoint( [method: string]: APIRoute; }, context: APIContext, - ssr: boolean, + isPrerendered: boolean, logger: Logger, ) { const { request, url } = context; @@ -20,12 +20,12 @@ export async function renderEndpoint( const method = request.method.toUpperCase(); // use the exact match on `method`, fallback to ALL const handler = mod[method] ?? mod['ALL']; - if (!ssr && ssr === false && method !== 'GET') { + if (isPrerendered && method !== 'GET') { logger.warn( 'router', `${url.pathname} ${bold( method, - )} requests are not available for a static site. Update your config to \`output: 'server'\` or \`output: 'hybrid'\` to enable.`, + )} requests are not available in static endpoints. Mark this page as server-rendered (\`export const prerender = false;\`) or update your config to \`output: 'server'\` to make all your pages server-rendered by default.`, ); } if (handler === undefined) { diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts index d39b2f3d69bd..abdd9481d244 100644 --- a/packages/astro/src/types/public/config.ts +++ b/packages/astro/src/types/public/config.ts @@ -240,16 +240,15 @@ export interface AstroUserConfig { /** * @docs * @name output - * @type {('static' | 'server' | 'hybrid')} + * @type {('static' | 'server')} * @default `'static'` * @see adapter * @description * * Specifies the output target for builds. * - * - `'static'` - Building a static site to be deployed to any static host. - * - `'server'` - Building an app to be deployed to a host supporting SSR (server-side rendering). - * - `'hybrid'` - Building a static site with a few server-side rendered pages. + * - `'static'` - Prerender all your pages by default, outputting a completely static site if none of your pages opt out of prerendering. + * - `'server'` - Use server-side rendering (SSR) for all pages by default, always outputting a server-rendered site. * * ```js * import { defineConfig } from 'astro/config'; @@ -259,7 +258,7 @@ export interface AstroUserConfig { * }) * ``` */ - output?: 'static' | 'server' | 'hybrid'; + output?: 'static' | 'server'; /** * @docs @@ -450,7 +449,7 @@ export interface AstroUserConfig { * * Enables security measures for an Astro website. * - * These features only exist for pages rendered on demand (SSR) using `server` mode or pages that opt out of prerendering in `hybrid` mode. + * These features only exist for pages rendered on demand (SSR) using `server` mode or pages that opt out of prerendering in `static` mode. * * ```js * // astro.config.mjs @@ -563,14 +562,14 @@ export interface AstroUserConfig { * @type {string} * @default `'./dist/client'` * @description - * Controls the output directory of your client-side CSS and JavaScript when `output: 'server'` or `output: 'hybrid'` only. + * Controls the output directory of your client-side CSS and JavaScript when building a website with server-rendered pages. * `outDir` controls where the code is built to. * * This value is relative to the `outDir`. * * ```js * { - * output: 'server', // or 'hybrid' + * output: 'server', * build: { * client: './client' * } diff --git a/packages/astro/test/ssr-prerender-get-static-paths.test.js b/packages/astro/test/ssr-prerender-get-static-paths.test.js index aa4532ca0b84..8ac65899d04f 100644 --- a/packages/astro/test/ssr-prerender-get-static-paths.test.js +++ b/packages/astro/test/ssr-prerender-get-static-paths.test.js @@ -137,7 +137,7 @@ describe('Prerender', () => { }); }); - describe('output: "hybrid"', () => { + describe('output: "static" with server output', () => { describe('getStaticPaths - build calls', () => { before(async () => { fixture = await loadFixture({ diff --git a/packages/integrations/node/src/index.ts b/packages/integrations/node/src/index.ts index f76747279162..52c4f69dd915 100644 --- a/packages/integrations/node/src/index.ts +++ b/packages/integrations/node/src/index.ts @@ -63,7 +63,7 @@ export default function createIntegration(userOptions: UserOptions): AstroIntegr }, }); }, - 'astro:config:done': ({ setAdapter, config, logger }) => { + 'astro:config:done': ({ setAdapter, config }) => { _options = { ...userOptions, client: config.build.client?.toString(), @@ -73,12 +73,6 @@ export default function createIntegration(userOptions: UserOptions): AstroIntegr assets: config.build.assets, }; setAdapter(getAdapter(_options)); - - if (config.output === 'static') { - logger.warn( - `\`output: "server"\` or \`output: "hybrid"\` is required to use this adapter.`, - ); - } }, }, }; diff --git a/packages/integrations/node/test/fixtures/trailing-slash/astro.config.mjs b/packages/integrations/node/test/fixtures/trailing-slash/astro.config.mjs index 7ee28f21340c..acf78132be06 100644 --- a/packages/integrations/node/test/fixtures/trailing-slash/astro.config.mjs +++ b/packages/integrations/node/test/fixtures/trailing-slash/astro.config.mjs @@ -2,7 +2,7 @@ import node from '@astrojs/node' export default { base: '/some-base', - output: 'hybrid', + output: 'static', trailingSlash: 'never', adapter: node({ mode: 'standalone' }) }; diff --git a/packages/integrations/vercel/test/edge-middleware.test.js b/packages/integrations/vercel/test/edge-middleware.test.js index 9ae583a119bf..c5ddf5f499f4 100644 --- a/packages/integrations/vercel/test/edge-middleware.test.js +++ b/packages/integrations/vercel/test/edge-middleware.test.js @@ -30,7 +30,8 @@ describe('Vercel edge middleware', () => { ); }); - it('edge sets Set-Cookie headers', async () => { + // TODO: This test fails because it tries to actually fetch `http://example.com/` which fails for me locally - erika, 2024-08-30 + it.skip('edge sets Set-Cookie headers', async () => { let entry = new URL( '../.vercel/output/functions/_middleware.func/middleware.mjs', build.config.outDir, diff --git a/packages/integrations/web-vitals/src/index.ts b/packages/integrations/web-vitals/src/index.ts index 84837e3edb93..c74ab32613ee 100644 --- a/packages/integrations/web-vitals/src/index.ts +++ b/packages/integrations/web-vitals/src/index.ts @@ -19,14 +19,6 @@ export default function webVitals({ deprecated }: { deprecated?: boolean } = {}) ); } - // if (config.output !== 'hybrid' && config.output !== 'server') { - // throw new AstroError( - // 'No SSR adapter found.', - // '`@astrojs/web-vitals` requires your site to be built with `hybrid` or `server` output.\n' + - // 'Please add an SSR adapter: https://docs.astro.build/en/guides/server-side-rendering/', - // ); - // } - // Middleware that adds a `` tag to each page. addMiddleware({ entrypoint: '@astrojs/web-vitals/middleware', order: 'post' }); // Endpoint that collects metrics and inserts them in Astro DB. diff --git a/packages/integrations/web-vitals/test/fixtures/basics/astro.config.mjs b/packages/integrations/web-vitals/test/fixtures/basics/astro.config.mjs index 42bfa6f6693f..4dae98ae0fc0 100644 --- a/packages/integrations/web-vitals/test/fixtures/basics/astro.config.mjs +++ b/packages/integrations/web-vitals/test/fixtures/basics/astro.config.mjs @@ -6,7 +6,7 @@ import { defineConfig } from 'astro/config'; // https://astro.build/config export default defineConfig({ integrations: [db(), webVitals()], - output: 'hybrid', + output: 'static', adapter: node({ mode: 'standalone' }), devToolbar: { enabled: false, From 485e305a3266373496b54f2364874fd0b80ad630 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 30 Aug 2024 15:26:34 +0200 Subject: [PATCH 15/27] fix: e2e tests --- packages/astro/e2e/custom-client-directives.test.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/astro/e2e/custom-client-directives.test.js b/packages/astro/e2e/custom-client-directives.test.js index 60470b4dd842..e0787109ef83 100644 --- a/packages/astro/e2e/custom-client-directives.test.js +++ b/packages/astro/e2e/custom-client-directives.test.js @@ -35,13 +35,18 @@ test.describe('Custom Client Directives - build static', () => { testClientDirectivesShared(); }); -test.describe('Custom Client Directives - build server zzz', () => { +test.describe('Custom Client Directives - build server', () => { let previewServer; test.beforeAll(async ({ astro }) => { await astro.build({ - adapter: testAdapter(), - output: 'server', + adapter: testAdapter({ + extendAdapter: { + adapterFeatures: { + forceServerOutput: false, + }, + }, + }), }); previewServer = await astro.preview(); }); From db0ead794a469321aea45003584f15aff3ce38bb Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:40:24 +0200 Subject: [PATCH 16/27] refactor: cleanup code --- packages/astro/src/config/index.ts | 4 +- packages/astro/src/core/build/index.ts | 4 +- packages/astro/src/core/create-vite.ts | 6 +- packages/astro/src/core/dev/container.ts | 3 +- packages/astro/src/core/preview/index.ts | 2 +- .../astro/src/core/routing/manifest/create.ts | 45 ++++-- packages/astro/src/core/sync/index.ts | 14 +- packages/astro/src/integrations/hooks.ts | 2 - .../src/vite-plugin-astro-server/plugin.ts | 4 +- .../astro/src/vite-plugin-scanner/index.ts | 52 ++----- .../astro/src/vite-plugin-scanner/scan.ts | 92 ------------ .../astro/test/units/routing/manifest.test.js | 20 +-- .../test/units/routing/route-matching.test.js | 2 +- .../vite-plugin-astro-server/request.test.js | 2 +- .../units/vite-plugin-scanner/scan.test.js | 137 ------------------ 15 files changed, 78 insertions(+), 311 deletions(-) delete mode 100644 packages/astro/src/vite-plugin-scanner/scan.ts delete mode 100644 packages/astro/test/units/vite-plugin-scanner/scan.test.js diff --git a/packages/astro/src/config/index.ts b/packages/astro/src/config/index.ts index b31105ec7adc..82f5d77c83ca 100644 --- a/packages/astro/src/config/index.ts +++ b/packages/astro/src/config/index.ts @@ -1,5 +1,6 @@ import type { UserConfig as ViteUserConfig } from 'vite'; import { Logger } from '../core/logger/core.js'; +import { createRouteManifest } from '../core/routing/index.js'; import type { AstroInlineConfig, AstroUserConfig } from '../types/public/config.js'; export function defineConfig(config: AstroUserConfig) { @@ -40,6 +41,7 @@ export function getViteConfig( const { astroConfig: config } = await resolveConfig(inlineAstroConfig, cmd); let settings = await createSettings(config, userViteConfig.root); settings = await runHookConfigSetup({ settings, command: cmd, logger }); + const manifest = await createRouteManifest({ settings }, logger); const viteConfig = await createVite( { mode, @@ -48,7 +50,7 @@ export function getViteConfig( astroContentListenPlugin({ settings, logger, fs }), ], }, - { settings, logger, mode, sync: false }, + { settings, logger, mode, sync: false, manifest }, ); await runHookConfigDone({ settings, logger }); return mergeConfig(viteConfig, userViteConfig); diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 56f74622bca6..77e3d2334fe5 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -118,7 +118,7 @@ class AstroBuilder { logger: logger, }); - this.manifest = createRouteManifest({ settings: this.settings }, this.logger); + this.manifest = await createRouteManifest({ settings: this.settings }, this.logger); if (this.settings.buildOutput === 'server') { injectImageEndpoint(this.settings, this.manifest, 'build'); @@ -143,6 +143,7 @@ class AstroBuilder { mode: 'build', command: 'build', sync: false, + manifest: this.manifest, }, ); await runHookConfigDone({ settings: this.settings, logger: logger }); @@ -152,6 +153,7 @@ class AstroBuilder { settings: this.settings, logger, fs, + manifest: this.manifest, }); return { viteConfig }; diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index bbac8d1c339a..76ca24714773 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -48,7 +48,7 @@ interface CreateViteOptions { command?: 'dev' | 'build'; fs?: typeof nodeFs; sync: boolean; - manifest?: ManifestData; + manifest: ManifestData; } const ALWAYS_NOEXTERNAL = [ @@ -132,7 +132,7 @@ export async function createVite( astroScriptsPlugin({ settings }), // The server plugin is for dev only and having it run during the build causes // the build to run very slow as the filewatcher is triggered often. - mode !== 'build' && vitePluginAstroServer({ settings, logger, fs, manifest: manifest! }), // The manifest is always defined in dev mode + mode !== 'build' && vitePluginAstroServer({ settings, logger, fs, manifest }), envVitePlugin({ settings, logger }), astroEnv({ settings, mode, sync }), markdownVitePlugin({ settings, logger }), @@ -141,7 +141,7 @@ export async function createVite( astroIntegrationsContainerPlugin({ settings, logger }), astroScriptsPageSSRPlugin({ settings }), astroHeadPlugin(), - astroScannerPlugin({ settings, logger }), + astroScannerPlugin({ settings, logger, manifest }), astroContentVirtualModPlugin({ fs, settings }), astroContentImportPlugin({ fs, settings, logger }), astroContentAssetPropagationPlugin({ mode, settings }), diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts index f6092eda97ca..d7f2c027427c 100644 --- a/packages/astro/src/core/dev/container.ts +++ b/packages/astro/src/core/dev/container.ts @@ -81,7 +81,7 @@ export async function createContainer({ .filter(Boolean) as string[]; // Create the route manifest already outside of Vite so that `runHookConfigDone` can use it to inform integrations of the build output - const manifest = createRouteManifest({ settings, fsMod: fs }, logger); + const manifest = await createRouteManifest({ settings, fsMod: fs }, logger); injectImageEndpoint(settings, manifest, 'dev'); @@ -104,6 +104,7 @@ export async function createContainer({ content: true, }, force: inlineConfig?.force, + manifest, }); const viteServer = await vite.createServer(viteConfig); diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index a1a58866a5eb..88338c606353 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -38,7 +38,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 - createRouteManifest({ settings: settings, cwd: inlineConfig.root }, logger); + await createRouteManifest({ settings: settings, cwd: inlineConfig.root }, logger); await runHookConfigDone({ settings: settings, logger: logger }); diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 60e6e014c629..77701bc40159 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -7,6 +7,7 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { bold } from 'kleur/colors'; 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'; @@ -472,10 +473,10 @@ function detectRouteCollision(a: RouteData, b: RouteData, _config: AstroConfig, } /** Create manifest of all static routes */ -export function createRouteManifest( +export async function createRouteManifest( params: CreateRouteManifestParams, logger: Logger, -): ManifestData { +): Promise { const { settings } = params; const { config } = settings; // Create a map of all routes so redirects can refer to any route @@ -499,21 +500,9 @@ export function createRouteManifest( settings.buildOutput = getPrerenderDefault(config) ? 'static' : 'server'; - // Scan all the routes to see if they're prerendered or not + // Check if for (const route of routes) { - if (route.type !== 'page' && route.type !== 'endpoint') continue; - const localFs = params.fsMod ?? nodeFs; - const content = localFs.readFileSync( - fileURLToPath(new URL(route.component, config.root)), - 'utf-8', - ); - - // Check if the route is pre-rendered or not - const match = /^export\s+const\s+prerender\s*=\s*(true|false);?/m.exec(content); - if (match) { - route.prerender = match[1] === 'true'; - if (!route.prerender) settings.buildOutput = 'server'; - } + await getRoutePrerenderOption(route, settings, params.fsMod, logger); } // Report route collisions @@ -719,6 +708,30 @@ export 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 = localFs.readFileSync( + 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 (!route.prerender) settings.buildOutput = 'server'; +} + export function resolveInjectedRoute(entrypoint: string, root: URL, cwd?: string) { let resolved; try { diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index 0634e16c6f7c..aae0a87ccbb4 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -12,7 +12,7 @@ import { syncAstroEnv } from '../../env/sync.js'; import { telemetry } from '../../events/index.js'; import { eventCliSession } from '../../events/session.js'; import { runHookConfigDone, runHookConfigSetup } from '../../integrations/hooks.js'; -import type { AstroSettings } from '../../types/astro.js'; +import type { AstroSettings, ManifestData } from '../../types/astro.js'; import type { AstroConfig, AstroInlineConfig } from '../../types/public/config.js'; import { getTimeStat } from '../build/util.js'; import { resolveConfig } from '../config/config.js'; @@ -45,6 +45,7 @@ export type SyncOptions = { // Must be skipped in dev content?: boolean; }; + manifest: ManifestData; }; export default async function sync( @@ -63,9 +64,9 @@ export default async function sync( settings, logger, }); - createRouteManifest({ settings, fsMod: fs }, logger); + const manifest = await createRouteManifest({ settings, fsMod: fs }, logger); await runHookConfigDone({ settings, logger }); - return await syncInternal({ settings, logger, fs, force: inlineConfig.force }); + return await syncInternal({ settings, logger, fs, force: inlineConfig.force, manifest }); } /** @@ -96,6 +97,7 @@ export async function syncInternal({ settings, skip, force, + manifest, }: SyncOptions): Promise { if (force) { await clearContentLayerCache({ astroConfig: settings.config, logger, fs }); @@ -105,7 +107,7 @@ export async function syncInternal({ try { if (!skip?.content) { - await syncContentCollections(settings, { fs, logger }); + await syncContentCollections(settings, { fs, logger, manifest }); settings.timer.start('Sync content layer'); let store: MutableDataStore | undefined; try { @@ -166,7 +168,7 @@ export async function syncInternal({ */ async function syncContentCollections( settings: AstroSettings, - { logger, fs }: Required>, + { logger, fs, manifest }: Required>, ): Promise { // Needed to load content config const tempViteServer = await createServer( @@ -177,7 +179,7 @@ async function syncContentCollections( ssr: { external: [] }, logLevel: 'silent', }, - { settings, logger, mode: 'build', command: 'build', fs, sync: true }, + { settings, logger, mode: 'build', command: 'build', fs, sync: true, manifest }, ), ); diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index 6f88415d5f20..f82ba39039d0 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -1,4 +1,3 @@ -import assert from 'node:assert'; import fsMod from 'node:fs'; import type { AddressInfo } from 'node:net'; import { fileURLToPath } from 'node:url'; @@ -296,7 +295,6 @@ export async function runHookConfigDone({ }) { for (const integration of settings.config.integrations) { if (integration?.hooks?.['astro:config:done']) { - assert(settings.buildOutput, 'buildOutput must be set before running astro:config:done'); await withTakingALongTimeMsg({ name: integration.name, hookName: 'astro:config:done', diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index c2ea559b5ea0..e8dec86765e2 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -49,12 +49,12 @@ export default function createVitePluginAstroServer({ const localStorage = new AsyncLocalStorage(); /** rebuild the route cache + manifest, as needed. */ - function rebuildManifest(needsManifestRebuild: boolean) { + async function rebuildManifest(needsManifestRebuild: boolean) { pipeline.clearRouteCache(); if (needsManifestRebuild) { manifestData = injectDefaultRoutes( devSSRManifest, - createRouteManifest({ settings, fsMod }, logger), + await createRouteManifest({ settings, fsMod }, logger), ); pipeline.setManifestData(manifestData); } diff --git a/packages/astro/src/vite-plugin-scanner/index.ts b/packages/astro/src/vite-plugin-scanner/index.ts index b70b4fc4860f..264b6f4dfc74 100644 --- a/packages/astro/src/vite-plugin-scanner/index.ts +++ b/packages/astro/src/vite-plugin-scanner/index.ts @@ -1,20 +1,17 @@ import { extname } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { bold } from 'kleur/colors'; import type { Plugin as VitePlugin } from 'vite'; import { normalizePath } from 'vite'; import type { Logger } from '../core/logger/core.js'; import { isEndpoint, isPage } from '../core/util.js'; import { rootRelativePath } from '../core/viteUtils.js'; -import { runHookRouteSetup } from '../integrations/hooks.js'; -import { getPrerenderDefault } from '../prerender/utils.js'; -import type { AstroSettings } from '../types/astro.js'; -import type { RouteOptions } from '../types/public/integrations.js'; -import type { PageOptions } from '../vite-plugin-astro/types.js'; -import { scan } from './scan.js'; +import type { AstroSettings, ManifestData } from '../types/astro.js'; export interface AstroPluginScannerOptions { settings: AstroSettings; logger: Logger; + manifest: ManifestData; } const KNOWN_FILE_EXTENSIONS = ['.astro', '.js', '.ts']; @@ -22,6 +19,7 @@ const KNOWN_FILE_EXTENSIONS = ['.astro', '.js', '.ts']; export default function astroScannerPlugin({ settings, logger, + manifest, }: AstroPluginScannerOptions): VitePlugin { return { name: 'astro:scanner', @@ -42,15 +40,19 @@ export default function astroScannerPlugin({ const fileIsPage = isPage(fileURL, settings); const fileIsEndpoint = isEndpoint(fileURL, settings); if (!(fileIsPage || fileIsEndpoint)) return; - const pageOptions = await getPageOptions(code, id, fileURL, settings, logger); - if (pageOptions.prerender === false) { - settings.buildOutput = 'server'; + const route = manifest.routes.find((r) => { + const filePath = new URL(`./${r.component}`, settings.config.root); + return fileURLToPath(filePath) === filename; + }); + + if (!route) { + return; } // `getStaticPaths` warning is just a string check, should be good enough for most cases if ( - !pageOptions.prerender && + !route.prerender && code.includes('getStaticPaths') && // this should only be valid for `.astro`, `.js` and `.ts` files KNOWN_FILE_EXTENSIONS.includes(extname(filename)) @@ -71,36 +73,12 @@ export default function astroScannerPlugin({ ...meta, astro: { ...(meta.astro ?? { hydratedComponents: [], clientOnlyComponents: [], scripts: [] }), - pageOptions, + pageOptions: { + prerender: route.prerender, + }, }, }, }; }, }; } - -async function getPageOptions( - code: string, - id: string, - fileURL: URL, - settings: AstroSettings, - logger: Logger, -): Promise { - // Run initial scan - const pageOptions = await scan(code, id, settings); - - // Run integration hooks to alter page options - const route: RouteOptions = { - component: rootRelativePath(settings.config.root, fileURL, false), - prerender: pageOptions.prerender, - }; - await runHookRouteSetup({ route, settings, logger }); - pageOptions.prerender = route.prerender; - - // Fallback if unset - if (typeof pageOptions.prerender === 'undefined') { - pageOptions.prerender = getPrerenderDefault(settings.config); - } - - return pageOptions; -} diff --git a/packages/astro/src/vite-plugin-scanner/scan.ts b/packages/astro/src/vite-plugin-scanner/scan.ts deleted file mode 100644 index c44af5c1df89..000000000000 --- a/packages/astro/src/vite-plugin-scanner/scan.ts +++ /dev/null @@ -1,92 +0,0 @@ -import type { AstroSettings } from '../types/astro.js'; -import type { PageOptions } from '../vite-plugin-astro/types.js'; - -import * as eslexer from 'es-module-lexer'; -import { AstroError, AstroErrorData } from '../core/errors/index.js'; - -const BOOLEAN_EXPORTS = new Set(['prerender']); - -// Quick scan to determine if code includes recognized export -// False positives are not a problem, so be forgiving! -function includesExport(code: string) { - for (const name of BOOLEAN_EXPORTS) { - if (code.includes(name)) return true; - } - return false; -} - -// Support quoted values to allow statically known `import.meta.env` variables to be used -function isQuoted(value: string) { - return (value[0] === '"' || value[0] === "'") && value[value.length - 1] === value[0]; -} - -function isTruthy(value: string) { - if (isQuoted(value)) { - value = value.slice(1, -1); - } - return value === 'true' || value === '1'; -} - -function isFalsy(value: string) { - if (isQuoted(value)) { - value = value.slice(1, -1); - } - return value === 'false' || value === '0'; -} - -let didInit = false; - -export async function scan( - code: string, - id: string, - settings?: AstroSettings, -): Promise { - 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; - // mark that a `prerender` export was found - if (BOOLEAN_EXPORTS.has(name)) { - // For a given export, check the value of the local declaration - // Basically extract the `const` from the statement `export const prerender = true` - const prefix = code - .slice(0, endOfLocalName) - .split('export') - .pop()! - .trim() - .replace('prerender', '') - .trim(); - // For a given export, check the value of the first non-whitespace token. - // Basically extract the `true` from the statement `export const prerender = true` - const suffix = code - .slice(endOfLocalName) - .trim() - .replace(/=/, '') - .trim() - .split(/[;\n\r]/)[0] - .trim(); - if (prefix !== 'const' || !(isTruthy(suffix) || isFalsy(suffix))) { - throw new AstroError({ - ...AstroErrorData.InvalidPrerenderExport, - message: AstroErrorData.InvalidPrerenderExport.message( - prefix, - suffix, - settings?.config.output === 'static', - ), - location: { file: id }, - }); - } else { - pageOptions[name as keyof PageOptions] = isTruthy(suffix); - } - } - } - - return pageOptions; -} diff --git a/packages/astro/test/units/routing/manifest.test.js b/packages/astro/test/units/routing/manifest.test.js index 9f6710a4bf9c..856679a71557 100644 --- a/packages/astro/test/units/routing/manifest.test.js +++ b/packages/astro/test/units/routing/manifest.test.js @@ -52,7 +52,7 @@ describe('routing - createRouteManifest', () => { base: '/search', trailingSlash: 'never', }); - const manifest = createRouteManifest({ + const manifest = await createRouteManifest({ cwd: fileURLToPath(root), settings, fsMod: fs, @@ -89,7 +89,7 @@ describe('routing - createRouteManifest', () => { }, ]; - const manifest = createRouteManifest({ + const manifest = await createRouteManifest({ cwd: fileURLToPath(root), settings, fsMod: fs, @@ -132,7 +132,7 @@ describe('routing - createRouteManifest', () => { trailingSlash: 'never', }); - const manifest = createRouteManifest({ + const manifest = await createRouteManifest({ cwd: fileURLToPath(root), settings, fsMod: fs, @@ -169,7 +169,7 @@ describe('routing - createRouteManifest', () => { trailingSlash: 'never', }); - const manifest = createRouteManifest({ + const manifest = await createRouteManifest({ cwd: fileURLToPath(root), settings, fsMod: fs, @@ -215,7 +215,7 @@ describe('routing - createRouteManifest', () => { trailingSlash: 'never', }); - const manifest = createRouteManifest({ + const manifest = await createRouteManifest({ cwd: fileURLToPath(root), settings, fsMod: fs, @@ -280,7 +280,7 @@ describe('routing - createRouteManifest', () => { }, ]; - const manifest = createRouteManifest({ + const manifest = await createRouteManifest({ cwd: fileURLToPath(root), settings, fsMod: fs, @@ -330,7 +330,7 @@ describe('routing - createRouteManifest', () => { }, }, }); - const manifest = createRouteManifest({ + const manifest = await createRouteManifest({ cwd: fileURLToPath(root), settings, fsMod: fs, @@ -387,7 +387,7 @@ describe('routing - createRouteManifest', () => { const { logger, logs } = getLogger(); - createRouteManifest(manifestOptions, logger); + await createRouteManifest(manifestOptions, logger); assert.deepEqual(logs, [ { @@ -430,7 +430,7 @@ describe('routing - createRouteManifest', () => { const { logger, logs } = getLogger(); - createRouteManifest(manifestOptions, logger); + await createRouteManifest(manifestOptions, logger); assert.deepEqual(logs, [ { @@ -477,7 +477,7 @@ describe('routing - createRouteManifest', () => { }, ]; - const manifest = createRouteManifest({ + const manifest = await createRouteManifest({ cwd: fileURLToPath(root), settings, fsMod: fs, diff --git a/packages/astro/test/units/routing/route-matching.test.js b/packages/astro/test/units/routing/route-matching.test.js index f8001c5723e9..26e540e6ec92 100644 --- a/packages/astro/test/units/routing/route-matching.test.js +++ b/packages/astro/test/units/routing/route-matching.test.js @@ -147,7 +147,7 @@ describe('Route matching', () => { const loader = createViteLoader(container.viteServer); const manifest = createDevelopmentManifest(container.settings); pipeline = DevPipeline.create(undefined, { loader, logger: defaultLogger, manifest, settings }); - manifestData = createRouteManifest( + manifestData = await createRouteManifest( { cwd: fileURLToPath(root), settings, diff --git a/packages/astro/test/units/vite-plugin-astro-server/request.test.js b/packages/astro/test/units/vite-plugin-astro-server/request.test.js index 26f446cb1d27..3ec1e52231ae 100644 --- a/packages/astro/test/units/vite-plugin-astro-server/request.test.js +++ b/packages/astro/test/units/vite-plugin-astro-server/request.test.js @@ -50,7 +50,7 @@ describe('vite-plugin-astro-server', () => { }, '/', ); - const manifestData = createRouteManifest( + const manifestData = await createRouteManifest( { fsMod: fs, settings: pipeline.settings, diff --git a/packages/astro/test/units/vite-plugin-scanner/scan.test.js b/packages/astro/test/units/vite-plugin-scanner/scan.test.js deleted file mode 100644 index 5dab2449d125..000000000000 --- a/packages/astro/test/units/vite-plugin-scanner/scan.test.js +++ /dev/null @@ -1,137 +0,0 @@ -import * as assert from 'node:assert/strict'; -import { describe, it } from 'node:test'; -import { scan } from '../../../dist/vite-plugin-scanner/scan.js'; - -describe('astro scan', () => { - it('should return empty object', async () => { - const result = await scan(`export {}`, '/src/components/index.astro'); - assert.equal(Object.keys(result).length, 0); - }); - - it('recognizes constant boolean literal (false)', async () => { - const result = await scan(`export const prerender = true;`, '/src/components/index.astro'); - assert.equal(result.prerender, true); - }); - - it('recognizes constant boolean literal (false)', async () => { - const result = await scan(`export const prerender = false;`, '/src/components/index.astro'); - assert.equal(result.prerender, false); - }); - - it("recognizes single quoted boolean ('true')", async () => { - const result = await scan(`export const prerender = 'true';`, '/src/components/index.astro'); - assert.equal(result.prerender, true); - }); - - it('recognizes double quoted boolean ("true")', async () => { - const result = await scan(`export const prerender = "true";`, '/src/components/index.astro'); - assert.equal(result.prerender, true); - }); - - it('recognizes double quoted boolean ("false")', async () => { - const result = await scan(`export const prerender = "false";`, '/src/components/index.astro'); - assert.equal(result.prerender, false); - }); - - it("recognizes single quoted boolean ('false')", async () => { - const result = await scan(`export const prerender = 'false';`, '/src/components/index.astro'); - assert.equal(result.prerender, false); - }); - - it('recognizes number (1)', async () => { - const result = await scan(`export const prerender = 1;`, '/src/components/index.astro'); - assert.equal(result.prerender, true); - }); - - it('recognizes number (0)', async () => { - const result = await scan(`export const prerender = 0;`, '/src/components/index.astro'); - assert.equal(result.prerender, false); - }); - - it('throws on let boolean literal', async () => { - try { - await scan(`export let prerender = true;`, '/src/components/index.astro'); - assert.equal(false).to.be.true; - } catch (e) { - assert.equal( - e.message.includes( - `A \`prerender\` export has been detected, but its value cannot be statically analyzed.`, - ), - true, - ); - } - }); - - it('throws on var boolean literal', async () => { - try { - await scan(`export var prerender = true;`, '/src/components/index.astro'); - assert.equal(false).to.be.true; - } catch (e) { - assert.equal( - e.message.includes( - `A \`prerender\` export has been detected, but its value cannot be statically analyzed.`, - ), - true, - ); - } - }); - - it('throws on unknown values I', async () => { - try { - await scan(`export const prerender = !!value;`, '/src/components/index.astro'); - assert.equal(false).to.be.true; - } catch (e) { - assert.equal( - e.message.includes( - `A \`prerender\` export has been detected, but its value cannot be statically analyzed.`, - ), - true, - ); - } - }); - - it('throws on unknown values II', async () => { - try { - await scan(`export const prerender = value;`, '/src/components/index.astro'); - assert.equal(false).to.be.true; - } catch (e) { - assert.equal( - e.message.includes( - `A \`prerender\` export has been detected, but its value cannot be statically analyzed.`, - ), - true, - ); - } - }); - - it('throws on unknown values III', async () => { - try { - await scan( - `export let prerender = undefined; prerender = true;`, - '/src/components/index.astro', - ); - assert.equal(false).to.be.true; - } catch (e) { - assert.equal( - e.message.includes( - `A \`prerender\` export has been detected, but its value cannot be statically analyzed.`, - ), - true, - ); - } - }); - - it('throws on unknown values IV', async () => { - try { - await scan(`let prerender = true; export { prerender }`, '/src/components/index.astro'); - assert.equal(false).to.be.true; - } catch (e) { - assert.equal( - e.message.includes( - `A \`prerender\` export has been detected, but its value cannot be statically analyzed.`, - ), - true, - ); - } - }); -}); From 6c1ef5562ebe481883708b36601a8a213c6d0598 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 30 Aug 2024 22:01:34 +0200 Subject: [PATCH 17/27] fix: more tests --- packages/astro/src/core/create-vite.ts | 2 +- packages/astro/src/vite-plugin-env/index.ts | 29 +------------------ .../fixtures/prerender/src/pages/two.astro | 2 +- .../src/pages/index.astro | 2 +- .../src/pages/index.astro | 2 +- 5 files changed, 5 insertions(+), 32 deletions(-) diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index 76ca24714773..cda60942a8f2 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -133,7 +133,7 @@ export async function createVite( // The server plugin is for dev only and having it run during the build causes // the build to run very slow as the filewatcher is triggered often. mode !== 'build' && vitePluginAstroServer({ settings, logger, fs, manifest }), - envVitePlugin({ settings, logger }), + envVitePlugin({ settings }), astroEnv({ settings, mode, sync }), markdownVitePlugin({ settings, logger }), htmlVitePlugin(), diff --git a/packages/astro/src/vite-plugin-env/index.ts b/packages/astro/src/vite-plugin-env/index.ts index 0408e645d0a8..f68012927c0b 100644 --- a/packages/astro/src/vite-plugin-env/index.ts +++ b/packages/astro/src/vite-plugin-env/index.ts @@ -1,16 +1,13 @@ import { fileURLToPath } from 'node:url'; import { transform } from 'esbuild'; -import { bold } from 'kleur/colors'; import MagicString from 'magic-string'; import type * as vite from 'vite'; import { loadEnv } from 'vite'; -import type { Logger } from '../core/logger/core.js'; import type { AstroSettings } from '../types/astro.js'; import type { AstroConfig } from '../types/public/config.js'; interface EnvPluginOptions { settings: AstroSettings; - logger: Logger; } // Match `import.meta.env` directly without trailing property access @@ -18,9 +15,6 @@ const importMetaEnvOnlyRe = /\bimport\.meta\.env\b(?!\.)/; // Match valid JS variable names (identifiers), which accepts most alphanumeric characters, // except that the first character cannot be a number. const isValidIdentifierRe = /^[_$a-zA-Z][\w$]*$/; -// Match `export const prerender = import.meta.env.*` since `vite=plugin-scanner` requires -// the `import.meta.env.*` to always be replaced. -const exportConstPrerenderRe = /\bexport\s+const\s+prerender\s*=\s*import\.meta\.env\.(.+?)\b/; function getPrivateEnv( viteConfig: vite.ResolvedConfig, @@ -120,7 +114,7 @@ async function replaceDefine( }; } -export default function envVitePlugin({ settings, logger }: EnvPluginOptions): vite.Plugin { +export default function envVitePlugin({ settings }: EnvPluginOptions): vite.Plugin { let privateEnv: Record; let defaultDefines: Record; let isDev: boolean; @@ -173,27 +167,6 @@ export default function envVitePlugin({ settings, logger }: EnvPluginOptions): v } s.prepend(devImportMetaEnvPrepend); - // EDGE CASE: We need to do a static replacement for `export const prerender` for `vite-plugin-scanner` - // TODO: Remove in Astro 5 - let exportConstPrerenderStr: string | undefined; - s.replace(exportConstPrerenderRe, (m, key) => { - if (privateEnv[key] != null) { - exportConstPrerenderStr = m; - return `export const prerender = ${privateEnv[key]}`; - } else { - return m; - } - }); - if (exportConstPrerenderStr) { - logger.warn( - 'router', - `Exporting dynamic values from prerender is deprecated. Please use an integration with the "astro:route:setup" hook ` + - `to update the route's \`prerender\` option instead. This allows for better treeshaking and bundling configuration ` + - `in the future. See https://docs.astro.build/en/reference/integrations-reference/#astroroutesetup for a migration example.` + - `\nFound \`${bold(exportConstPrerenderStr)}\` in ${bold(id)}.`, - ); - } - return { code: s.toString(), map: s.generateMap({ hires: 'boundary' }), diff --git a/packages/integrations/node/test/fixtures/prerender/src/pages/two.astro b/packages/integrations/node/test/fixtures/prerender/src/pages/two.astro index c0e5d07aa437..beb6e8d788bd 100644 --- a/packages/integrations/node/test/fixtures/prerender/src/pages/two.astro +++ b/packages/integrations/node/test/fixtures/prerender/src/pages/two.astro @@ -1,5 +1,5 @@ --- -export const prerender = import.meta.env.PRERENDER; +export const prerender = true; --- diff --git a/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro b/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro index b6b833e5358f..2a976957b1f3 100644 --- a/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro +++ b/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/index.astro @@ -1,5 +1,5 @@ --- -export const prerender = import.meta.env.PRERENDER; +export const prerender = true; --- diff --git a/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro b/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro index b6b833e5358f..2a976957b1f3 100644 --- a/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro +++ b/packages/integrations/vercel/test/fixtures/serverless-with-dynamic-routes/src/pages/index.astro @@ -1,5 +1,5 @@ --- -export const prerender = import.meta.env.PRERENDER; +export const prerender = true; --- From 5eab502af8381b7a0dafd2d531f92cb738d5c64a Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Sat, 31 Aug 2024 00:09:17 +0200 Subject: [PATCH 18/27] fix: windows --- packages/astro/src/vite-plugin-scanner/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/astro/src/vite-plugin-scanner/index.ts b/packages/astro/src/vite-plugin-scanner/index.ts index 264b6f4dfc74..b53decd2d6f2 100644 --- a/packages/astro/src/vite-plugin-scanner/index.ts +++ b/packages/astro/src/vite-plugin-scanner/index.ts @@ -43,7 +43,7 @@ export default function astroScannerPlugin({ const route = manifest.routes.find((r) => { const filePath = new URL(`./${r.component}`, settings.config.root); - return fileURLToPath(filePath) === filename; + return normalizePath(fileURLToPath(filePath)) === filename; }); if (!route) { From 3614071f3925463381afa20f34fa86a72c7a0a5f Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Tue, 3 Sep 2024 14:33:57 +0200 Subject: [PATCH 19/27] fix: apply feedback --- packages/astro/src/actions/integration.ts | 2 +- packages/astro/src/core/request.ts | 8 -------- packages/astro/src/core/routing/manifest/create.ts | 6 ++++-- packages/astro/src/integrations/hooks.ts | 4 +++- packages/astro/src/types/public/integrations.ts | 2 +- packages/db/src/core/integration/index.ts | 2 +- 6 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/astro/src/actions/integration.ts b/packages/astro/src/actions/integration.ts index 7d185410e5da..830420836a3d 100644 --- a/packages/astro/src/actions/integration.ts +++ b/packages/astro/src/actions/integration.ts @@ -30,7 +30,7 @@ export default function astroIntegrationActionsRouteHandler({ }); }, 'astro:config:done': async (params) => { - if (params.buildOutput() === 'static') { + if (params.buildOutput === 'static') { const error = new AstroError(ActionsWithoutServerOutputError); error.stack = undefined; throw error; diff --git a/packages/astro/src/core/request.ts b/packages/astro/src/core/request.ts index 93b552901791..d108c9d94658 100644 --- a/packages/astro/src/core/request.ts +++ b/packages/astro/src/core/request.ts @@ -35,7 +35,6 @@ const clientLocalsSymbol = Symbol.for('astro.locals'); * This is used by the static build to create fake requests for prerendering, and by the dev server to convert node requests into the standard request object. */ export function createRequest({ - base, url, headers, clientAddress, @@ -60,13 +59,6 @@ export function createRequest({ if (typeof url === 'string') url = new URL(url); - const imageEndpoint = prependForwardSlash(appendForwardSlash(base)) + '_image'; - - // HACK! astro:assets uses query params for the injected route in `dev` - if (staticLike && url.pathname !== imageEndpoint) { - url.search = ''; - } - const request = new Request(url, { method: method, headers: headersObj, diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 77701bc40159..24733b63216b 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -500,7 +500,7 @@ export async function createRouteManifest( settings.buildOutput = getPrerenderDefault(config) ? 'static' : 'server'; - // Check if + // Check the prerender option for each route for (const route of routes) { await getRoutePrerenderOption(route, settings, params.fsMod, logger); } @@ -721,10 +721,12 @@ async function getRoutePrerenderOption( 'utf-8', ); - // Check if the route is pre-rendered or not + // Check if the route is pre-rendered or not, if not explicitly set, default to the global setting const match = /^\s*export\s+const\s+prerender\s*=\s*(true|false);?/m.exec(content); if (match) { route.prerender = match[1] === 'true'; + } else { + route.prerender = getPrerenderDefault(settings.config); } await runHookRouteSetup({ route, settings, logger }); diff --git a/packages/astro/src/integrations/hooks.ts b/packages/astro/src/integrations/hooks.ts index f82ba39039d0..48935cba7e9e 100644 --- a/packages/astro/src/integrations/hooks.ts +++ b/packages/astro/src/integrations/hooks.ts @@ -355,7 +355,9 @@ export async function runHookConfigDone({ return new URL(normalizedFilename, settings.dotAstroDir); }, logger: getLogger(integration, logger), - buildOutput: () => settings.buildOutput!, // settings.buildOutput is always set at this point + get buildOutput() { + return settings.buildOutput!; // settings.buildOutput is always set at this point + }, }), logger, }); diff --git a/packages/astro/src/types/public/integrations.ts b/packages/astro/src/types/public/integrations.ts index 8ed61e88181c..822183a9ad5c 100644 --- a/packages/astro/src/types/public/integrations.ts +++ b/packages/astro/src/types/public/integrations.ts @@ -186,7 +186,7 @@ export interface BaseIntegrationHooks { setAdapter: (adapter: AstroAdapter) => void; injectTypes: (injectedType: InjectedType) => URL; logger: AstroIntegrationLogger; - buildOutput: () => 'static' | 'server'; + buildOutput: 'static' | 'server'; }) => void | Promise; 'astro:server:setup': (options: { server: ViteDevServer; diff --git a/packages/db/src/core/integration/index.ts b/packages/db/src/core/integration/index.ts index a642aaffe147..44c57cdf3c1e 100644 --- a/packages/db/src/core/integration/index.ts +++ b/packages/db/src/core/integration/index.ts @@ -107,7 +107,7 @@ function astroDBIntegration(): AstroIntegration { 'astro:config:done': async ({ config, injectTypes, buildOutput }) => { if (command === 'preview') return; - finalBuildOutput = buildOutput(); + finalBuildOutput = buildOutput; // TODO: refine where we load tables // @matthewp: may want to load tables by path at runtime From b6f4db90c0a6e20528c7a383043d91b892d6fe5b Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:45:57 +0200 Subject: [PATCH 20/27] perf: do in parallel --- packages/astro/src/core/routing/manifest/create.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 24733b63216b..8da5c31d8856 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -6,6 +6,7 @@ import { createRequire } from 'node:module'; import path from 'node:path'; 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'; @@ -501,9 +502,14 @@ export async function createRouteManifest( settings.buildOutput = getPrerenderDefault(config) ? 'static' : 'server'; // Check the prerender option for each route + const limit = pLimit(10); + let promises = []; for (const route of routes) { - await getRoutePrerenderOption(route, settings, params.fsMod, logger); + promises.push( + limit(async () => await getRoutePrerenderOption(route, settings, params.fsMod, logger)), + ); } + await Promise.all(promises); // Report route collisions for (const [index, higherRoute] of routes.entries()) { From 27cb9aca1ff8165199652e322e1689540c57ea70 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 4 Sep 2024 12:06:50 +0200 Subject: [PATCH 21/27] fix: tests --- packages/astro/src/core/request.ts | 5 ++++- packages/astro/src/core/routing/manifest/create.ts | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/astro/src/core/request.ts b/packages/astro/src/core/request.ts index d108c9d94658..9b2dc07ca0fe 100644 --- a/packages/astro/src/core/request.ts +++ b/packages/astro/src/core/request.ts @@ -1,6 +1,5 @@ import type { IncomingHttpHeaders } from 'node:http'; import type { Logger } from './logger/core.js'; -import { appendForwardSlash, prependForwardSlash } from './path.js'; type HeaderType = Headers | Record | IncomingHttpHeaders; type RequestBody = ArrayBuffer | Blob | ReadableStream | URLSearchParams | FormData; @@ -59,6 +58,10 @@ export function createRequest({ if (typeof url === 'string') url = new URL(url); + if (staticLike) { + url.search = ''; + } + const request = new Request(url, { method: method, headers: headersObj, diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 8da5c31d8856..f9744b727cf7 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -722,7 +722,7 @@ async function getRoutePrerenderOption( ) { if (route.type !== 'page' && route.type !== 'endpoint') return; const localFs = fsMod ?? nodeFs; - const content = localFs.readFileSync( + const content = await localFs.promises.readFile( fileURLToPath(new URL(route.component, settings.config.root)), 'utf-8', ); @@ -731,7 +731,9 @@ async function getRoutePrerenderOption( const match = /^\s*export\s+const\s+prerender\s*=\s*(true|false);?/m.exec(content); if (match) { route.prerender = match[1] === 'true'; - } else { + } + + if (route.prerender === undefined) { route.prerender = getPrerenderDefault(settings.config); } From 2c4faa754ce78b03a42dbb9be3546e6e75d93490 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:11:13 +0200 Subject: [PATCH 22/27] fix: tests, for real --- packages/astro/src/core/routing/manifest/create.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index f9744b727cf7..939efc8c04b4 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -727,13 +727,14 @@ async function getRoutePrerenderOption( 'utf-8', ); - // Check if the route is pre-rendered or not, if not explicitly set, default to the global setting + // 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'; } - if (route.prerender === undefined) { + // If not explicitly set, default to the global setting + if (typeof route.prerender === undefined) { route.prerender = getPrerenderDefault(settings.config); } From ca0b57098cb296d2d08c6b07193a179d30874ebf Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Wed, 4 Sep 2024 15:42:56 +0200 Subject: [PATCH 23/27] fix: make server islands tests server-rendered --- .../astro/e2e/fixtures/server-islands/src/pages/index.astro | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/astro/e2e/fixtures/server-islands/src/pages/index.astro b/packages/astro/e2e/fixtures/server-islands/src/pages/index.astro index 70a4fcabcb66..3caf452bc026 100644 --- a/packages/astro/e2e/fixtures/server-islands/src/pages/index.astro +++ b/packages/astro/e2e/fixtures/server-islands/src/pages/index.astro @@ -5,6 +5,8 @@ import HTMLError from '../components/HTMLError.astro'; import { generateLongText } from '../lorem'; const content = generateLongText(5); + +export const prerender = false; --- From c878836d4890b393810c60d2993bfe2b65a7df4b Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:03:54 +0200 Subject: [PATCH 24/27] fix: apply feedback --- packages/astro/src/core/routing/manifest/create.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/astro/src/core/routing/manifest/create.ts b/packages/astro/src/core/routing/manifest/create.ts index 939efc8c04b4..e22eb8de1189 100644 --- a/packages/astro/src/core/routing/manifest/create.ts +++ b/packages/astro/src/core/routing/manifest/create.ts @@ -733,13 +733,13 @@ async function getRoutePrerenderOption( 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); } - await runHookRouteSetup({ route, settings, logger }); - if (!route.prerender) settings.buildOutput = 'server'; } From 2cfe46d3fe864ede28cc145ad7cc465d05b0aea7 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:07:14 +0200 Subject: [PATCH 25/27] nit: remove unnecessary file --- .../serverless-prerender/src/pages/server-rendered.astro | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/server-rendered.astro diff --git a/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/server-rendered.astro b/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/server-rendered.astro deleted file mode 100644 index a4a2e0957735..000000000000 --- a/packages/integrations/vercel/test/fixtures/serverless-prerender/src/pages/server-rendered.astro +++ /dev/null @@ -1,7 +0,0 @@ ---- -export const prerender = false; ---- - -
-

Server Rendered

-
From e7bd25a27761768612bc53d493bd13fe4c758400 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:58:45 +0200 Subject: [PATCH 26/27] fix: test remove test that abuse prerender logic --- packages/astro/test/astro-global.test.js | 5 ----- .../astro/test/fixtures/astro-global/src/pages/about.astro | 4 +--- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/astro/test/astro-global.test.js b/packages/astro/test/astro-global.test.js index 2b400e87f7f8..c996de4364b3 100644 --- a/packages/astro/test/astro-global.test.js +++ b/packages/astro/test/astro-global.test.js @@ -66,11 +66,6 @@ describe('Astro Global', () => { let $ = cheerio.load(html); assert.match($('#prerender').text(), /Astro route prerender: true/); assert.match($('#prerender-middleware').text(), /Astro route prerender middleware: true/); - - html = await fixture.fetch('/blog/about', {}).then((res) => res.text()); - $ = cheerio.load(html); - assert.match($('#prerender').text(), /Astro route prerender: false/); - assert.match($('#prerender-middleware').text(), /Astro route prerender middleware: false/); }); }); diff --git a/packages/astro/test/fixtures/astro-global/src/pages/about.astro b/packages/astro/test/fixtures/astro-global/src/pages/about.astro index edafce0d2e47..c54179032875 100644 --- a/packages/astro/test/fixtures/astro-global/src/pages/about.astro +++ b/packages/astro/test/fixtures/astro-global/src/pages/about.astro @@ -1,7 +1,5 @@ --- -import Route from '../components/Route.astro' - -export const prerender = false +import Route from '../components/Route.astro'; --- From ef102484255c4ca98f37e75ae25e1e87609718f8 Mon Sep 17 00:00:00 2001 From: Princesseuh <3019731+Princesseuh@users.noreply.github.com> Date: Sat, 7 Sep 2024 00:20:37 +0200 Subject: [PATCH 27/27] fix: ensure image endpoint is there on dev reload --- packages/astro/src/assets/endpoint/config.ts | 24 +++++++++++++++++-- packages/astro/src/core/create-vite.ts | 7 ++++-- packages/astro/src/core/dev/container.ts | 19 +++++++++++---- .../astro/src/core/routing/dev-default.ts | 14 +++++++++++ .../src/vite-plugin-astro-server/plugin.ts | 17 ++++++------- pnpm-lock.yaml | 2 -- 6 files changed, 65 insertions(+), 18 deletions(-) create mode 100644 packages/astro/src/core/routing/dev-default.ts diff --git a/packages/astro/src/assets/endpoint/config.ts b/packages/astro/src/assets/endpoint/config.ts index 965b82812a9e..382645d6ee80 100644 --- a/packages/astro/src/assets/endpoint/config.ts +++ b/packages/astro/src/assets/endpoint/config.ts @@ -1,5 +1,6 @@ import { resolveInjectedRoute } from '../../core/routing/manifest/create.js'; import type { AstroSettings, ManifestData } from '../../types/astro.js'; +import type { RouteData } from '../../types/public/internal.js'; export function injectImageEndpoint( settings: AstroSettings, @@ -7,11 +8,30 @@ export function injectImageEndpoint( mode: 'dev' | 'build', cwd?: string, ) { + manifest.routes.push(getImageEndpointData(settings, mode, cwd)); +} + +export function ensureImageEndpointRoute( + settings: AstroSettings, + manifest: ManifestData, + mode: 'dev' | 'build', + cwd?: string, +) { + if (!manifest.routes.some((route) => route.route === '/_image')) { + manifest.routes.push(getImageEndpointData(settings, mode, cwd)); + } +} + +function getImageEndpointData( + settings: AstroSettings, + mode: 'dev' | 'build', + cwd?: string, +): RouteData { const endpointEntrypoint = settings.config.image.endpoint ?? (mode === 'dev' ? 'astro/assets/endpoint/node' : 'astro/assets/endpoint/generic'); - manifest.routes.push({ + return { type: 'endpoint', isIndex: false, route: '/_image', @@ -23,5 +43,5 @@ export function injectImageEndpoint( pathname: '/_image', prerender: false, fallbackRoutes: [], - }); + }; } diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts index cda60942a8f2..e8d9edfe3660 100644 --- a/packages/astro/src/core/create-vite.ts +++ b/packages/astro/src/core/create-vite.ts @@ -33,6 +33,7 @@ import astroScannerPlugin from '../vite-plugin-scanner/index.js'; import astroScriptsPlugin from '../vite-plugin-scripts/index.js'; import astroScriptsPageSSRPlugin from '../vite-plugin-scripts/page-ssr.js'; import { vitePluginSSRManifest } from '../vite-plugin-ssr-manifest/index.js'; +import type { SSRManifest } from './app/types.js'; import type { Logger } from './logger/core.js'; import { createViteLogger } from './logger/vite.js'; import { vitePluginMiddleware } from './middleware/vite-plugin.js'; @@ -49,6 +50,7 @@ interface CreateViteOptions { fs?: typeof nodeFs; sync: boolean; manifest: ManifestData; + ssrManifest?: SSRManifest; } const ALWAYS_NOEXTERNAL = [ @@ -76,7 +78,7 @@ const ONLY_DEV_EXTERNAL = [ /** Return a base vite config as a common starting point for all Vite commands. */ export async function createVite( commandConfig: vite.InlineConfig, - { settings, logger, mode, command, fs = nodeFs, sync, manifest }: CreateViteOptions, + { settings, logger, mode, command, fs = nodeFs, sync, manifest, ssrManifest }: CreateViteOptions, ): Promise { const astroPkgsConfig = await crawlFrameworkPkgs({ root: fileURLToPath(settings.config.root), @@ -132,7 +134,8 @@ export async function createVite( astroScriptsPlugin({ settings }), // The server plugin is for dev only and having it run during the build causes // the build to run very slow as the filewatcher is triggered often. - mode !== 'build' && vitePluginAstroServer({ settings, logger, fs, manifest }), + mode !== 'build' && + vitePluginAstroServer({ settings, logger, fs, manifest, ssrManifest: ssrManifest! }), // ssrManifest is only required in dev mode, where it gets created before a Vite instance is created, and get passed to this function envVitePlugin({ settings }), astroEnv({ settings, mode, sync }), markdownVitePlugin({ settings, logger }), diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts index d7f2c027427c..1fea20620b0a 100644 --- a/packages/astro/src/core/dev/container.ts +++ b/packages/astro/src/core/dev/container.ts @@ -4,7 +4,6 @@ import type { AstroSettings } from '../../types/astro.js'; import nodeFs from 'node:fs'; import * as vite from 'vite'; -import { injectImageEndpoint } from '../../assets/endpoint/config.js'; import { runHookConfigDone, runHookConfigSetup, @@ -12,9 +11,11 @@ import { runHookServerStart, } from '../../integrations/hooks.js'; import type { AstroInlineConfig } from '../../types/public/config.js'; +import { createDevelopmentManifest } from '../../vite-plugin-astro-server/plugin.js'; import { createVite } from '../create-vite.js'; import type { Logger } from '../logger/core.js'; 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'; @@ -81,9 +82,10 @@ export async function createContainer({ .filter(Boolean) as string[]; // Create the route manifest already outside of Vite so that `runHookConfigDone` can use it to inform integrations of the build output - const manifest = await createRouteManifest({ settings, fsMod: fs }, logger); + let manifest = await createRouteManifest({ settings, fsMod: fs }, logger); + const devSSRManifest = createDevelopmentManifest(settings); - injectImageEndpoint(settings, manifest, 'dev'); + manifest = injectDefaultDevRoutes(settings, devSSRManifest, manifest); const viteConfig = await createVite( { @@ -93,7 +95,16 @@ export async function createContainer({ include: rendererClientEntries, }, }, - { settings, logger, mode: 'dev', command: 'dev', fs, sync: false, manifest }, + { + settings, + logger, + mode: 'dev', + command: 'dev', + fs, + sync: false, + manifest, + ssrManifest: devSSRManifest, + }, ); await runHookConfigDone({ settings, logger }); diff --git a/packages/astro/src/core/routing/dev-default.ts b/packages/astro/src/core/routing/dev-default.ts new file mode 100644 index 000000000000..ac2ea1d3b03b --- /dev/null +++ b/packages/astro/src/core/routing/dev-default.ts @@ -0,0 +1,14 @@ +import { ensureImageEndpointRoute } from '../../assets/endpoint/config.js'; +import type { AstroSettings, ManifestData } from '../../types/astro.js'; +import type { SSRManifest } from '../app/types.js'; +import { injectDefaultRoutes } from './default.js'; + +export function injectDefaultDevRoutes( + settings: AstroSettings, + ssrManifest: SSRManifest, + routeManifest: ManifestData, +) { + ensureImageEndpointRoute(settings, routeManifest, 'dev'); + injectDefaultRoutes(ssrManifest, routeManifest); + return routeManifest; +} diff --git a/packages/astro/src/vite-plugin-astro-server/plugin.ts b/packages/astro/src/vite-plugin-astro-server/plugin.ts index e8dec86765e2..20020559e8f5 100644 --- a/packages/astro/src/vite-plugin-astro-server/plugin.ts +++ b/packages/astro/src/vite-plugin-astro-server/plugin.ts @@ -9,7 +9,7 @@ import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { patchOverlay } from '../core/errors/overlay.js'; import type { Logger } from '../core/logger/core.js'; import { createViteLoader } from '../core/module-loader/index.js'; -import { injectDefaultRoutes } from '../core/routing/default.js'; +import { injectDefaultDevRoutes } from '../core/routing/dev-default.js'; import { createRouteManifest } from '../core/routing/index.js'; import { toFallbackType, toRoutingStrategy } from '../i18n/utils.js'; import type { AstroSettings, ManifestData } from '../types/astro.js'; @@ -25,6 +25,7 @@ export interface AstroPluginOptions { logger: Logger; fs: typeof fs; manifest: ManifestData; + ssrManifest: SSRManifest; } export default function createVitePluginAstroServer({ @@ -32,14 +33,13 @@ export default function createVitePluginAstroServer({ logger, fs: fsMod, manifest: routeManifest, + ssrManifest: devSSRManifest, }: AstroPluginOptions): vite.Plugin { return { name: 'astro:server', configureServer(viteServer) { const loader = createViteLoader(viteServer); - const devSSRManifest = createDevelopmentManifest(settings); - let manifestData: ManifestData = injectDefaultRoutes(devSSRManifest, routeManifest); - const pipeline = DevPipeline.create(manifestData, { + const pipeline = DevPipeline.create(routeManifest, { loader, logger, manifest: devSSRManifest, @@ -52,11 +52,12 @@ export default function createVitePluginAstroServer({ async function rebuildManifest(needsManifestRebuild: boolean) { pipeline.clearRouteCache(); if (needsManifestRebuild) { - manifestData = injectDefaultRoutes( + routeManifest = injectDefaultDevRoutes( + settings, devSSRManifest, - await createRouteManifest({ settings, fsMod }, logger), + await createRouteManifest({ settings, fsMod }, logger), // TODO: Handle partial updates to the manifest ); - pipeline.setManifestData(manifestData); + pipeline.setManifestData(routeManifest); } } @@ -101,7 +102,7 @@ export default function createVitePluginAstroServer({ localStorage.run(request, () => { handleRequest({ pipeline, - manifestData, + manifestData: routeManifest, controller, incomingRequest: request, incomingResponse: response, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ecfeac33a404..dc6a790465d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8752,12 +8752,10 @@ packages: libsql@0.3.19: resolution: {integrity: sha512-Aj5cQ5uk/6fHdmeW0TiXK42FqUlwx7ytmMLPSaUQPin5HKKKuUPD62MAbN4OEweGBBI7q1BekoEN4gPUEL6MZA==} - cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] libsql@0.4.1: resolution: {integrity: sha512-qZlR9Yu1zMBeLChzkE/cKfoKV3Esp9cn9Vx5Zirn4AVhDWPcjYhKwbtJcMuHehgk3mH+fJr9qW+3vesBWbQpBg==} - cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lilconfig@2.1.0: