mirror of
https://github.com/withastro/astro.git
synced 2024-12-16 21:46:22 -05:00
Merge output: hybrid and output: static (#11824)
* feat: merge hybrid and static * fix: linting * fix: get a bunch of tests passing * fix: make forceServerOutput optional * fix: more tests passing * fix: http2 test * fix: CCC * fix: get unit tests passing * fix: lint * fix: vercel * fix: build * fix: build * fix: db tests * fix: get all normal tests passing * fix: e2e tests * refactor: cleanup code * fix: more tests * fix: windows * fix: apply feedback * perf: do in parallel * fix: tests * fix: tests, for real * fix: make server islands tests server-rendered * fix: apply feedback * nit: remove unnecessary file * fix: test remove test that abuse prerender logic * fix: ensure image endpoint is there on dev reload
This commit is contained in:
parent
5b4e3abbb1
commit
50ca656dba
75 changed files with 437 additions and 550 deletions
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
|
@ -15,7 +15,7 @@
|
||||||
"editor.defaultFormatter": "biomejs.biome"
|
"editor.defaultFormatter": "biomejs.biome"
|
||||||
},
|
},
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"quickFix.biome": true,
|
"quickFix.biome": "explicit",
|
||||||
"source.fixAll.biome": true
|
"source.fixAll.biome": "explicit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,13 @@ test.describe('Custom Client Directives - build server', () => {
|
||||||
|
|
||||||
test.beforeAll(async ({ astro }) => {
|
test.beforeAll(async ({ astro }) => {
|
||||||
await astro.build({
|
await astro.build({
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter({
|
||||||
|
extendAdapter: {
|
||||||
|
adapterFeatures: {
|
||||||
|
forceServerOutput: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
previewServer = await astro.preview();
|
previewServer = await astro.preview();
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,7 @@ import node from '@astrojs/node';
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://example.com',
|
site: 'https://example.com',
|
||||||
integrations: [db(), react()],
|
integrations: [db(), react()],
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: node({
|
adapter: node({
|
||||||
mode: 'standalone',
|
mode: 'standalone',
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -7,7 +7,7 @@ import node from '@astrojs/node';
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: 'https://example.com',
|
site: 'https://example.com',
|
||||||
integrations: [db(), react()],
|
integrations: [db(), react()],
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: node({
|
adapter: node({
|
||||||
mode: 'standalone',
|
mode: 'standalone',
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -6,7 +6,7 @@ import nodejs from '@astrojs/node';
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
base: '/base',
|
base: '/base',
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: nodejs({ mode: 'standalone' }),
|
adapter: nodejs({ mode: 'standalone' }),
|
||||||
integrations: [react(), mdx()],
|
integrations: [react(), mdx()],
|
||||||
trailingSlash: process.env.TRAILING_SLASH ?? 'always',
|
trailingSlash: process.env.TRAILING_SLASH ?? 'always',
|
||||||
|
|
|
@ -5,6 +5,8 @@ import HTMLError from '../components/HTMLError.astro';
|
||||||
import { generateLongText } from '../lorem';
|
import { generateLongText } from '../lorem';
|
||||||
|
|
||||||
const content = generateLongText(5);
|
const content = generateLongText(5);
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: nodejs({ mode: 'standalone' }),
|
adapter: nodejs({ mode: 'standalone' }),
|
||||||
integrations: [react(),vue(),svelte()],
|
integrations: [react(),vue(),svelte()],
|
||||||
redirects: {
|
redirects: {
|
||||||
|
|
|
@ -118,7 +118,8 @@
|
||||||
"test:e2e:chrome": "playwright test",
|
"test:e2e:chrome": "playwright test",
|
||||||
"test:e2e:firefox": "playwright test --config playwright.firefox.config.js",
|
"test:e2e:firefox": "playwright test --config playwright.firefox.config.js",
|
||||||
"test:types": "tsc --project tsconfig.tests.json",
|
"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": {
|
"dependencies": {
|
||||||
"@astrojs/compiler": "^2.10.3",
|
"@astrojs/compiler": "^2.10.3",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { ActionsWithoutServerOutputError } from '../core/errors/errors-data.js';
|
import { ActionsWithoutServerOutputError } from '../core/errors/errors-data.js';
|
||||||
import { AstroError } from '../core/errors/errors.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 { AstroSettings } from '../types/astro.js';
|
||||||
import type { AstroIntegration } from '../types/public/integrations.js';
|
import type { AstroIntegration } from '../types/public/integrations.js';
|
||||||
import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js';
|
import { ACTIONS_TYPES_FILE, VIRTUAL_MODULE_ID } from './consts.js';
|
||||||
|
@ -30,7 +30,7 @@ export default function astroIntegrationActionsRouteHandler({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'astro:config:done': async (params) => {
|
'astro:config:done': async (params) => {
|
||||||
if (!isServerLikeOutput(params.config)) {
|
if (params.buildOutput === 'static') {
|
||||||
const error = new AstroError(ActionsWithoutServerOutputError);
|
const error = new AstroError(ActionsWithoutServerOutputError);
|
||||||
error.stack = undefined;
|
error.stack = undefined;
|
||||||
throw error;
|
throw error;
|
||||||
|
|
|
@ -9,7 +9,6 @@ import { AstroError } from '../../core/errors/errors.js';
|
||||||
import { AstroErrorData } from '../../core/errors/index.js';
|
import { AstroErrorData } from '../../core/errors/index.js';
|
||||||
import type { Logger } from '../../core/logger/core.js';
|
import type { Logger } from '../../core/logger/core.js';
|
||||||
import { isRemotePath, removeLeadingForwardSlash } from '../../core/path.js';
|
import { isRemotePath, removeLeadingForwardSlash } from '../../core/path.js';
|
||||||
import { isServerLikeOutput } from '../../core/util.js';
|
|
||||||
import type { MapValue } from '../../type-utils.js';
|
import type { MapValue } from '../../type-utils.js';
|
||||||
import type { AstroConfig } from '../../types/public/config.js';
|
import type { AstroConfig } from '../../types/public/config.js';
|
||||||
import { getConfiguredImageService } from '../internal.js';
|
import { getConfiguredImageService } from '../internal.js';
|
||||||
|
@ -50,7 +49,7 @@ export async function prepareAssetsGenerationEnv(
|
||||||
pipeline: BuildPipeline,
|
pipeline: BuildPipeline,
|
||||||
totalCount: number,
|
totalCount: number,
|
||||||
): Promise<AssetEnv> {
|
): Promise<AssetEnv> {
|
||||||
const { config, logger } = pipeline;
|
const { config, logger, settings } = pipeline;
|
||||||
let useCache = true;
|
let useCache = true;
|
||||||
const assetsCacheDir = new URL('assets/', config.cacheDir);
|
const assetsCacheDir = new URL('assets/', config.cacheDir);
|
||||||
const count = { total: totalCount, current: 1 };
|
const count = { total: totalCount, current: 1 };
|
||||||
|
@ -66,8 +65,9 @@ export async function prepareAssetsGenerationEnv(
|
||||||
useCache = false;
|
useCache = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isServerOutput = settings.buildOutput === 'server';
|
||||||
let serverRoot: URL, clientRoot: URL;
|
let serverRoot: URL, clientRoot: URL;
|
||||||
if (isServerLikeOutput(config)) {
|
if (isServerOutput) {
|
||||||
serverRoot = config.build.server;
|
serverRoot = config.build.server;
|
||||||
clientRoot = config.build.client;
|
clientRoot = config.build.client;
|
||||||
} else {
|
} else {
|
||||||
|
@ -77,7 +77,7 @@ export async function prepareAssetsGenerationEnv(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
logger,
|
logger,
|
||||||
isSSR: isServerLikeOutput(config),
|
isSSR: isServerOutput,
|
||||||
count,
|
count,
|
||||||
useCache,
|
useCache,
|
||||||
assetsCacheDir,
|
assetsCacheDir,
|
||||||
|
|
|
@ -1,15 +1,47 @@
|
||||||
import type { AstroSettings } from '../../types/astro.js';
|
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, mode: 'dev' | 'build') {
|
export function injectImageEndpoint(
|
||||||
|
settings: AstroSettings,
|
||||||
|
manifest: ManifestData,
|
||||||
|
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 =
|
const endpointEntrypoint =
|
||||||
settings.config.image.endpoint ??
|
settings.config.image.endpoint ??
|
||||||
(mode === 'dev' ? 'astro/assets/endpoint/node' : 'astro/assets/endpoint/generic');
|
(mode === 'dev' ? 'astro/assets/endpoint/node' : 'astro/assets/endpoint/generic');
|
||||||
|
|
||||||
settings.injectedRoutes.push({
|
return {
|
||||||
pattern: '/_image',
|
type: 'endpoint',
|
||||||
entrypoint: endpointEntrypoint,
|
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,
|
prerender: false,
|
||||||
});
|
fallbackRoutes: [],
|
||||||
|
};
|
||||||
return settings;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ import {
|
||||||
removeBase,
|
removeBase,
|
||||||
removeQueryString,
|
removeQueryString,
|
||||||
} from '../core/path.js';
|
} from '../core/path.js';
|
||||||
import { isServerLikeOutput } from '../core/util.js';
|
|
||||||
import type { AstroPluginOptions, AstroSettings } from '../types/astro.js';
|
import type { AstroPluginOptions, AstroSettings } from '../types/astro.js';
|
||||||
import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from './consts.js';
|
import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from './consts.js';
|
||||||
import type { ImageTransform } from './types.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.
|
// so that it's tree-shaken away for all platforms that don't need it.
|
||||||
export const outDir = /* #__PURE__ */ new URL(${JSON.stringify(
|
export const outDir = /* #__PURE__ */ new URL(${JSON.stringify(
|
||||||
new URL(
|
new URL(
|
||||||
isServerLikeOutput(settings.config)
|
settings.buildOutput === 'server'
|
||||||
? settings.config.build.client
|
? settings.config.build.client
|
||||||
: settings.config.outDir,
|
: settings.config.outDir,
|
||||||
),
|
),
|
||||||
|
@ -222,7 +221,7 @@ export default function assets({
|
||||||
if (options?.ssr) {
|
if (options?.ssr) {
|
||||||
return `export default ${getProxyCode(
|
return `export default ${getProxyCode(
|
||||||
imageMetadata,
|
imageMetadata,
|
||||||
isServerLikeOutput(settings.config),
|
settings.buildOutput === 'server',
|
||||||
)}`;
|
)}`;
|
||||||
} else {
|
} else {
|
||||||
globalThis.astroAsset.referencedImages.add(imageMetadata.fsPath);
|
globalThis.astroAsset.referencedImages.add(imageMetadata.fsPath);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import type { UserConfig as ViteUserConfig } from 'vite';
|
import type { UserConfig as ViteUserConfig } from 'vite';
|
||||||
import { Logger } from '../core/logger/core.js';
|
import { Logger } from '../core/logger/core.js';
|
||||||
|
import { createRouteManifest } from '../core/routing/index.js';
|
||||||
import type { AstroInlineConfig, AstroUserConfig } from '../types/public/config.js';
|
import type { AstroInlineConfig, AstroUserConfig } from '../types/public/config.js';
|
||||||
|
|
||||||
export function defineConfig(config: AstroUserConfig) {
|
export function defineConfig(config: AstroUserConfig) {
|
||||||
|
@ -40,6 +41,7 @@ export function getViteConfig(
|
||||||
const { astroConfig: config } = await resolveConfig(inlineAstroConfig, cmd);
|
const { astroConfig: config } = await resolveConfig(inlineAstroConfig, cmd);
|
||||||
let settings = await createSettings(config, userViteConfig.root);
|
let settings = await createSettings(config, userViteConfig.root);
|
||||||
settings = await runHookConfigSetup({ settings, command: cmd, logger });
|
settings = await runHookConfigSetup({ settings, command: cmd, logger });
|
||||||
|
const manifest = await createRouteManifest({ settings }, logger);
|
||||||
const viteConfig = await createVite(
|
const viteConfig = await createVite(
|
||||||
{
|
{
|
||||||
mode,
|
mode,
|
||||||
|
@ -48,7 +50,7 @@ export function getViteConfig(
|
||||||
astroContentListenPlugin({ settings, logger, fs }),
|
astroContentListenPlugin({ settings, logger, fs }),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{ settings, logger, mode, sync: false },
|
{ settings, logger, mode, sync: false, manifest },
|
||||||
);
|
);
|
||||||
await runHookConfigDone({ settings, logger });
|
await runHookConfigDone({ settings, logger });
|
||||||
return mergeConfig(viteConfig, userViteConfig);
|
return mergeConfig(viteConfig, userViteConfig);
|
||||||
|
|
|
@ -8,7 +8,6 @@ import { getProxyCode } from '../assets/utils/proxy.js';
|
||||||
import { AstroError } from '../core/errors/errors.js';
|
import { AstroError } from '../core/errors/errors.js';
|
||||||
import { AstroErrorData } from '../core/errors/index.js';
|
import { AstroErrorData } from '../core/errors/index.js';
|
||||||
import type { Logger } from '../core/logger/core.js';
|
import type { Logger } from '../core/logger/core.js';
|
||||||
import { isServerLikeOutput } from '../core/util.js';
|
|
||||||
import type { AstroSettings } from '../types/astro.js';
|
import type { AstroSettings } from '../types/astro.js';
|
||||||
import type { AstroConfig } from '../types/public/config.js';
|
import type { AstroConfig } from '../types/public/config.js';
|
||||||
import type {
|
import type {
|
||||||
|
@ -115,7 +114,7 @@ export function astroContentImportPlugin({
|
||||||
const code = `
|
const code = `
|
||||||
export const id = ${JSON.stringify(id)};
|
export const id = ${JSON.stringify(id)};
|
||||||
export const collection = ${JSON.stringify(collection)};
|
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 = {
|
export const _internal = {
|
||||||
type: 'data',
|
type: 'data',
|
||||||
filePath: ${JSON.stringify(_internal.filePath)},
|
filePath: ${JSON.stringify(_internal.filePath)},
|
||||||
|
@ -140,7 +139,7 @@ export const _internal = {
|
||||||
export const collection = ${JSON.stringify(collection)};
|
export const collection = ${JSON.stringify(collection)};
|
||||||
export const slug = ${JSON.stringify(slug)};
|
export const slug = ${JSON.stringify(slug)};
|
||||||
export const body = ${JSON.stringify(body)};
|
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 = {
|
export const _internal = {
|
||||||
type: 'content',
|
type: 'content',
|
||||||
filePath: ${JSON.stringify(_internal.filePath)},
|
filePath: ${JSON.stringify(_internal.filePath)},
|
||||||
|
|
|
@ -8,7 +8,6 @@ import type { Plugin } from 'vite';
|
||||||
import { encodeName } from '../core/build/util.js';
|
import { encodeName } from '../core/build/util.js';
|
||||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||||
import { appendForwardSlash, removeFileExtension } from '../core/path.js';
|
import { appendForwardSlash, removeFileExtension } from '../core/path.js';
|
||||||
import { isServerLikeOutput } from '../core/util.js';
|
|
||||||
import { rootRelativePath } from '../core/viteUtils.js';
|
import { rootRelativePath } from '../core/viteUtils.js';
|
||||||
import type { AstroSettings } from '../types/astro.js';
|
import type { AstroSettings } from '../types/astro.js';
|
||||||
import type { AstroPluginMetadata } from '../vite-plugin-astro/index.js';
|
import type { AstroPluginMetadata } from '../vite-plugin-astro/index.js';
|
||||||
|
@ -53,7 +52,7 @@ export function astroContentVirtualModPlugin({
|
||||||
fs,
|
fs,
|
||||||
}: AstroContentVirtualModPluginParams): Plugin {
|
}: AstroContentVirtualModPluginParams): Plugin {
|
||||||
let IS_DEV = false;
|
let IS_DEV = false;
|
||||||
const IS_SERVER = isServerLikeOutput(settings.config);
|
const IS_SERVER = settings.buildOutput === 'server';
|
||||||
let dataStoreFile: URL;
|
let dataStoreFile: URL;
|
||||||
return {
|
return {
|
||||||
name: 'astro-content-virtual-mod-plugin',
|
name: 'astro-content-virtual-mod-plugin',
|
||||||
|
|
|
@ -1,26 +1,27 @@
|
||||||
import npath from 'node:path';
|
import npath from 'node:path';
|
||||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||||
import { appendForwardSlash } from '../../core/path.js';
|
import { appendForwardSlash } from '../../core/path.js';
|
||||||
|
import type { AstroSettings } from '../../types/astro.js';
|
||||||
import type { AstroConfig } from '../../types/public/config.js';
|
import type { AstroConfig } from '../../types/public/config.js';
|
||||||
import type { RouteData } from '../../types/public/internal.js';
|
import type { RouteData } from '../../types/public/internal.js';
|
||||||
|
|
||||||
const STATUS_CODE_PAGES = new Set(['/404', '/500']);
|
const STATUS_CODE_PAGES = new Set(['/404', '/500']);
|
||||||
const FALLBACK_OUT_DIR_NAME = './.astro/';
|
const FALLBACK_OUT_DIR_NAME = './.astro/';
|
||||||
|
|
||||||
function getOutRoot(astroConfig: AstroConfig): URL {
|
function getOutRoot(astroSettings: AstroSettings): URL {
|
||||||
if (astroConfig.output === 'static') {
|
if (astroSettings.buildOutput === 'static') {
|
||||||
return new URL('./', astroConfig.outDir);
|
return new URL('./', astroSettings.config.outDir);
|
||||||
} else {
|
} else {
|
||||||
return new URL('./', astroConfig.build.client);
|
return new URL('./', astroSettings.config.build.client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOutFolder(
|
export function getOutFolder(
|
||||||
astroConfig: AstroConfig,
|
astroSettings: AstroSettings,
|
||||||
pathname: string,
|
pathname: string,
|
||||||
routeData: RouteData,
|
routeData: RouteData,
|
||||||
): URL {
|
): URL {
|
||||||
const outRoot = getOutRoot(astroConfig);
|
const outRoot = getOutRoot(astroSettings);
|
||||||
const routeType = routeData.type;
|
const routeType = routeData.type;
|
||||||
|
|
||||||
// This is the root folder to write to.
|
// This is the root folder to write to.
|
||||||
|
@ -30,7 +31,7 @@ export function getOutFolder(
|
||||||
case 'fallback':
|
case 'fallback':
|
||||||
case 'page':
|
case 'page':
|
||||||
case 'redirect':
|
case 'redirect':
|
||||||
switch (astroConfig.build.format) {
|
switch (astroSettings.config.build.format) {
|
||||||
case 'directory': {
|
case 'directory': {
|
||||||
if (STATUS_CODE_PAGES.has(pathname)) {
|
if (STATUS_CODE_PAGES.has(pathname)) {
|
||||||
return new URL('.' + appendForwardSlash(npath.dirname(pathname)), outRoot);
|
return new URL('.' + appendForwardSlash(npath.dirname(pathname)), outRoot);
|
||||||
|
|
|
@ -35,7 +35,7 @@ import { callGetStaticPaths } from '../render/route-cache.js';
|
||||||
import { createRequest } from '../request.js';
|
import { createRequest } from '../request.js';
|
||||||
import { matchRoute } from '../routing/match.js';
|
import { matchRoute } from '../routing/match.js';
|
||||||
import { stringifyParams } from '../routing/params.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 { getOutFile, getOutFolder } from './common.js';
|
||||||
import { cssOrder, mergeInlineCss } from './internal.js';
|
import { cssOrder, mergeInlineCss } from './internal.js';
|
||||||
import { BuildPipeline } from './pipeline.js';
|
import { BuildPipeline } from './pipeline.js';
|
||||||
|
@ -49,12 +49,12 @@ import { getTimeStat, shouldAppendForwardSlash } from './util.js';
|
||||||
|
|
||||||
export async function generatePages(options: StaticBuildOptions, internals: BuildInternals) {
|
export async function generatePages(options: StaticBuildOptions, internals: BuildInternals) {
|
||||||
const generatePagesTimer = performance.now();
|
const generatePagesTimer = performance.now();
|
||||||
const ssr = isServerLikeOutput(options.settings.config);
|
const ssr = options.settings.buildOutput === 'server';
|
||||||
let manifest: SSRManifest;
|
let manifest: SSRManifest;
|
||||||
if (ssr) {
|
if (ssr) {
|
||||||
manifest = await BuildPipeline.retrieveManifest(options, internals);
|
manifest = await BuildPipeline.retrieveManifest(options, internals);
|
||||||
} else {
|
} else {
|
||||||
const baseDirectory = getOutputDirectory(options.settings.config);
|
const baseDirectory = getOutputDirectory(options.settings);
|
||||||
const renderersEntryUrl = new URL('renderers.mjs', baseDirectory);
|
const renderersEntryUrl = new URL('renderers.mjs', baseDirectory);
|
||||||
const renderers = await import(renderersEntryUrl.toString());
|
const renderers = await import(renderersEntryUrl.toString());
|
||||||
let middleware: MiddlewareHandler = (_, next) => next();
|
let middleware: MiddlewareHandler = (_, next) => next();
|
||||||
|
@ -138,7 +138,7 @@ export async function generatePages(options: StaticBuildOptions, internals: Buil
|
||||||
delete globalThis?.astroAsset?.addStaticImage;
|
delete globalThis?.astroAsset?.addStaticImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
await runHookBuildGenerated({ config, logger });
|
await runHookBuildGenerated({ settings: options.settings, logger });
|
||||||
}
|
}
|
||||||
|
|
||||||
const THRESHOLD_SLOW_RENDER_TIME_MS = 500;
|
const THRESHOLD_SLOW_RENDER_TIME_MS = 500;
|
||||||
|
@ -466,7 +466,7 @@ async function generatePath(
|
||||||
body = Buffer.from(await response.arrayBuffer());
|
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);
|
const outFile = getOutFile(config, outFolder, pathname, route);
|
||||||
route.distURL = outFile;
|
route.distURL = outFile;
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,14 @@ import { createNodeLogger } from '../config/logging.js';
|
||||||
import { createSettings } from '../config/settings.js';
|
import { createSettings } from '../config/settings.js';
|
||||||
import { createVite } from '../create-vite.js';
|
import { createVite } from '../create-vite.js';
|
||||||
import { createKey } from '../encryption.js';
|
import { createKey } from '../encryption.js';
|
||||||
|
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||||
import type { Logger } from '../logger/core.js';
|
import type { Logger } from '../logger/core.js';
|
||||||
import { levels, timerMessage } from '../logger/core.js';
|
import { levels, timerMessage } from '../logger/core.js';
|
||||||
import { apply as applyPolyfill } from '../polyfill.js';
|
import { apply as applyPolyfill } from '../polyfill.js';
|
||||||
import { createRouteManifest } from '../routing/index.js';
|
import { createRouteManifest } from '../routing/index.js';
|
||||||
import { getServerIslandRouteData } from '../server-islands/endpoint.js';
|
import { getServerIslandRouteData } from '../server-islands/endpoint.js';
|
||||||
import { clearContentLayerCache } from '../sync/index.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 { collectPagesData } from './page-data.js';
|
||||||
import { staticBuild, viteBuild } from './static-build.js';
|
import { staticBuild, viteBuild } from './static-build.js';
|
||||||
import type { StaticBuildOptions } from './types.js';
|
import type { StaticBuildOptions } from './types.js';
|
||||||
|
@ -118,11 +119,16 @@ class AstroBuilder {
|
||||||
logger: logger,
|
logger: logger,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isServerLikeOutput(this.settings.config)) {
|
this.manifest = await createRouteManifest({ settings: this.settings }, this.logger);
|
||||||
this.settings = injectImageEndpoint(this.settings, 'build');
|
|
||||||
|
if (this.settings.buildOutput === 'server') {
|
||||||
|
injectImageEndpoint(this.settings, this.manifest, '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(
|
const viteConfig = await createVite(
|
||||||
{
|
{
|
||||||
|
@ -138,6 +144,7 @@ class AstroBuilder {
|
||||||
mode: 'build',
|
mode: 'build',
|
||||||
command: 'build',
|
command: 'build',
|
||||||
sync: false,
|
sync: false,
|
||||||
|
manifest: this.manifest,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
await runHookConfigDone({ settings: this.settings, logger: logger });
|
await runHookConfigDone({ settings: this.settings, logger: logger });
|
||||||
|
@ -147,6 +154,7 @@ class AstroBuilder {
|
||||||
settings: this.settings,
|
settings: this.settings,
|
||||||
logger,
|
logger,
|
||||||
fs,
|
fs,
|
||||||
|
manifest: this.manifest,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { viteConfig };
|
return { viteConfig };
|
||||||
|
@ -212,7 +220,7 @@ class AstroBuilder {
|
||||||
|
|
||||||
// You're done! Time to clean up.
|
// You're done! Time to clean up.
|
||||||
await runHookBuildDone({
|
await runHookBuildDone({
|
||||||
config: this.settings.config,
|
settings: this.settings,
|
||||||
pages: pageNames,
|
pages: pageNames,
|
||||||
routes: Object.values(allPages)
|
routes: Object.values(allPages)
|
||||||
.flat()
|
.flat()
|
||||||
|
|
|
@ -41,7 +41,7 @@ export function collectPagesData(opts: CollectPagesDataOptions): CollectPagesDat
|
||||||
styles: [],
|
styles: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (settings.config.output === 'static') {
|
if (settings.buildOutput === 'static') {
|
||||||
const html = `${route.pathname}`.replace(/\/?$/, '/index.html');
|
const html = `${route.pathname}`.replace(/\/?$/, '/index.html');
|
||||||
debug(
|
debug(
|
||||||
'build',
|
'build',
|
||||||
|
|
|
@ -16,7 +16,6 @@ import { Pipeline } from '../render/index.js';
|
||||||
import { createAssetLink, createStylesheetElementSet } from '../render/ssr-element.js';
|
import { createAssetLink, createStylesheetElementSet } from '../render/ssr-element.js';
|
||||||
import { createDefaultRoutes } from '../routing/default.js';
|
import { createDefaultRoutes } from '../routing/default.js';
|
||||||
import { findRouteToRewrite } from '../routing/rewrite.js';
|
import { findRouteToRewrite } from '../routing/rewrite.js';
|
||||||
import { isServerLikeOutput } from '../util.js';
|
|
||||||
import { getOutDirWithinCwd } from './common.js';
|
import { getOutDirWithinCwd } from './common.js';
|
||||||
import { type BuildInternals, cssOrder, getPageData, mergeInlineCss } from './internal.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';
|
import { ASTRO_PAGE_MODULE_ID, ASTRO_PAGE_RESOLVED_MODULE_ID } from './plugins/plugin-pages.js';
|
||||||
|
@ -39,8 +38,7 @@ export class BuildPipeline extends Pipeline {
|
||||||
#routesByFilePath: WeakMap<RouteData, string> = new WeakMap<RouteData, string>();
|
#routesByFilePath: WeakMap<RouteData, string> = new WeakMap<RouteData, string>();
|
||||||
|
|
||||||
get outFolder() {
|
get outFolder() {
|
||||||
const ssr = isServerLikeOutput(this.settings.config);
|
return this.settings.buildOutput === 'server'
|
||||||
return ssr
|
|
||||||
? this.settings.config.build.server
|
? this.settings.config.build.server
|
||||||
: getOutDirWithinCwd(this.settings.config.outDir);
|
: getOutDirWithinCwd(this.settings.config.outDir);
|
||||||
}
|
}
|
||||||
|
@ -74,7 +72,7 @@ export class BuildPipeline extends Pipeline {
|
||||||
return assetLink;
|
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
|
// We can skip streaming in SSG for performance as writing as strings are faster
|
||||||
const streaming = serverLike;
|
const streaming = serverLike;
|
||||||
super(
|
super(
|
||||||
|
@ -113,8 +111,7 @@ export class BuildPipeline extends Pipeline {
|
||||||
staticBuildOptions: StaticBuildOptions,
|
staticBuildOptions: StaticBuildOptions,
|
||||||
internals: BuildInternals,
|
internals: BuildInternals,
|
||||||
): Promise<SSRManifest> {
|
): Promise<SSRManifest> {
|
||||||
const config = staticBuildOptions.settings.config;
|
const baseDirectory = getOutputDirectory(staticBuildOptions.settings);
|
||||||
const baseDirectory = getOutputDirectory(config);
|
|
||||||
const manifestEntryUrl = new URL(
|
const manifestEntryUrl = new URL(
|
||||||
`${internals.manifestFileName}?time=${Date.now()}`,
|
`${internals.manifestFileName}?time=${Date.now()}`,
|
||||||
baseDirectory,
|
baseDirectory,
|
||||||
|
|
|
@ -496,7 +496,7 @@ export function pluginContent(
|
||||||
targets: ['server'],
|
targets: ['server'],
|
||||||
hooks: {
|
hooks: {
|
||||||
async 'build:before'() {
|
async 'build:before'() {
|
||||||
if (!isContentCollectionsCacheEnabled(opts.settings.config)) {
|
if (!isContentCollectionsCacheEnabled(opts.settings)) {
|
||||||
return { vitePlugin: undefined };
|
return { vitePlugin: undefined };
|
||||||
}
|
}
|
||||||
const lookupMap = await generateLookupMap({ settings: opts.settings, fs: fsMod });
|
const lookupMap = await generateLookupMap({ settings: opts.settings, fs: fsMod });
|
||||||
|
@ -506,7 +506,7 @@ export function pluginContent(
|
||||||
},
|
},
|
||||||
|
|
||||||
async 'build:post'() {
|
async 'build:post'() {
|
||||||
if (!isContentCollectionsCacheEnabled(opts.settings.config)) {
|
if (!isContentCollectionsCacheEnabled(opts.settings)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Cache build output of chunks and assets
|
// Cache build output of chunks and assets
|
||||||
|
|
|
@ -176,7 +176,7 @@ function buildManifest(
|
||||||
if (!route.prerender) continue;
|
if (!route.prerender) continue;
|
||||||
if (!route.pathname) 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 outFile = getOutFile(opts.settings.config, outFolder, route.pathname, route);
|
||||||
const file = outFile.toString().replace(opts.settings.config.build.client.toString(), '');
|
const file = outFile.toString().replace(opts.settings.config.build.client.toString(), '');
|
||||||
routes.push({
|
routes.push({
|
||||||
|
|
|
@ -14,7 +14,7 @@ function vitePluginPages(opts: StaticBuildOptions, internals: BuildInternals): V
|
||||||
return {
|
return {
|
||||||
name: '@astro/plugin-build-pages',
|
name: '@astro/plugin-build-pages',
|
||||||
options(options) {
|
options(options) {
|
||||||
if (opts.settings.config.output === 'static') {
|
if (opts.settings.buildOutput === 'static') {
|
||||||
const inputs = new Set<string>();
|
const inputs = new Set<string>();
|
||||||
|
|
||||||
for (const pageData of Object.values(opts.allPages)) {
|
for (const pageData of Object.values(opts.allPages)) {
|
||||||
|
|
|
@ -90,7 +90,7 @@ export function pluginPrerender(
|
||||||
internals: BuildInternals,
|
internals: BuildInternals,
|
||||||
): AstroBuildPlugin {
|
): AstroBuildPlugin {
|
||||||
// Static output can skip prerender completely because we're already rendering all pages
|
// 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'] };
|
return { targets: ['server'] };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ import type { AstroSettings } from '../../../types/astro.js';
|
||||||
import type { AstroAdapter } from '../../../types/public/integrations.js';
|
import type { AstroAdapter } from '../../../types/public/integrations.js';
|
||||||
import { routeIsRedirect } from '../../redirects/index.js';
|
import { routeIsRedirect } from '../../redirects/index.js';
|
||||||
import { VIRTUAL_ISLAND_MAP_ID } from '../../server-islands/vite-plugin-server-islands.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 { addRollupInput } from '../add-rollup-input.js';
|
||||||
import type { BuildInternals } from '../internal.js';
|
import type { BuildInternals } from '../internal.js';
|
||||||
import type { AstroBuildPlugin } from '../plugin.js';
|
import type { AstroBuildPlugin } from '../plugin.js';
|
||||||
|
@ -131,11 +130,12 @@ export function pluginSSR(
|
||||||
options: StaticBuildOptions,
|
options: StaticBuildOptions,
|
||||||
internals: BuildInternals,
|
internals: BuildInternals,
|
||||||
): AstroBuildPlugin {
|
): AstroBuildPlugin {
|
||||||
const ssr = isServerLikeOutput(options.settings.config);
|
const ssr = options.settings.buildOutput === 'server';
|
||||||
return {
|
return {
|
||||||
targets: ['server'],
|
targets: ['server'],
|
||||||
hooks: {
|
hooks: {
|
||||||
'build:before': () => {
|
'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 adapter = options.settings.adapter!;
|
||||||
const ssrPlugin = ssr && vitePluginSSR(internals, adapter, options);
|
const ssrPlugin = ssr && vitePluginSSR(internals, adapter, options);
|
||||||
const vitePlugin = [vitePluginAdapter(adapter)];
|
const vitePlugin = [vitePluginAdapter(adapter)];
|
||||||
|
|
|
@ -18,12 +18,10 @@ import {
|
||||||
} from '../../core/build/internal.js';
|
} from '../../core/build/internal.js';
|
||||||
import { emptyDir, removeEmptyDirs } from '../../core/fs/index.js';
|
import { emptyDir, removeEmptyDirs } from '../../core/fs/index.js';
|
||||||
import { appendForwardSlash, prependForwardSlash, removeFileExtension } from '../../core/path.js';
|
import { appendForwardSlash, prependForwardSlash, removeFileExtension } from '../../core/path.js';
|
||||||
import { isModeServerWithNoAdapter, isServerLikeOutput } from '../../core/util.js';
|
|
||||||
import { runHookBuildSetup } from '../../integrations/hooks.js';
|
import { runHookBuildSetup } from '../../integrations/hooks.js';
|
||||||
import { getOutputDirectory } from '../../prerender/utils.js';
|
import { getOutputDirectory } from '../../prerender/utils.js';
|
||||||
import type { RouteData } from '../../types/public/internal.js';
|
import type { RouteData } from '../../types/public/internal.js';
|
||||||
import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.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 type { Logger } from '../logger/core.js';
|
||||||
import { routeIsRedirect } from '../redirects/index.js';
|
import { routeIsRedirect } from '../redirects/index.js';
|
||||||
import { getOutDirWithinCwd } from './common.js';
|
import { getOutDirWithinCwd } from './common.js';
|
||||||
|
@ -43,10 +41,6 @@ import { encodeName, getTimeStat, viteBuildReturnToRollupOutputs } from './util.
|
||||||
|
|
||||||
export async function viteBuild(opts: StaticBuildOptions) {
|
export async function viteBuild(opts: StaticBuildOptions) {
|
||||||
const { allPages, settings, logger } = opts;
|
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');
|
settings.timer.start('SSR build');
|
||||||
|
|
||||||
|
@ -144,15 +138,15 @@ export async function staticBuild(
|
||||||
contentFileNames?: string[],
|
contentFileNames?: string[],
|
||||||
) {
|
) {
|
||||||
const { settings } = opts;
|
const { settings } = opts;
|
||||||
switch (true) {
|
switch (settings.buildOutput) {
|
||||||
case settings.config.output === 'static': {
|
case 'static': {
|
||||||
settings.timer.start('Static generate');
|
settings.timer.start('Static generate');
|
||||||
await generatePages(opts, internals);
|
await generatePages(opts, internals);
|
||||||
await cleanServerOutput(opts, ssrOutputChunkNames, contentFileNames, internals);
|
await cleanServerOutput(opts, ssrOutputChunkNames, contentFileNames, internals);
|
||||||
settings.timer.end('Static generate');
|
settings.timer.end('Static generate');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case isServerLikeOutput(settings.config): {
|
case 'server': {
|
||||||
settings.timer.start('Server generate');
|
settings.timer.start('Server generate');
|
||||||
await generatePages(opts, internals);
|
await generatePages(opts, internals);
|
||||||
await cleanStaticOutput(opts, internals);
|
await cleanStaticOutput(opts, internals);
|
||||||
|
@ -161,7 +155,7 @@ export async function staticBuild(
|
||||||
settings.timer.end('Server generate');
|
settings.timer.end('Server generate');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
default:
|
default: // `settings.buildOutput` will always be one of the above, but TS doesn't know that
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -175,8 +169,8 @@ async function ssrBuild(
|
||||||
) {
|
) {
|
||||||
const buildID = Date.now().toString();
|
const buildID = Date.now().toString();
|
||||||
const { allPages, settings, viteConfig } = opts;
|
const { allPages, settings, viteConfig } = opts;
|
||||||
const ssr = isServerLikeOutput(settings.config);
|
const ssr = settings.buildOutput === 'server';
|
||||||
const out = getOutputDirectory(settings.config);
|
const out = getOutputDirectory(settings);
|
||||||
const routes = Object.values(allPages).flatMap((pageData) => pageData.route);
|
const routes = Object.values(allPages).flatMap((pageData) => pageData.route);
|
||||||
const isContentCache = !ssr && settings.config.experimental.contentCollectionCache;
|
const isContentCache = !ssr && settings.config.experimental.contentCollectionCache;
|
||||||
const { lastVitePlugins, vitePlugins } = await container.runBeforeHook('server', input);
|
const { lastVitePlugins, vitePlugins } = await container.runBeforeHook('server', input);
|
||||||
|
@ -306,7 +300,7 @@ async function clientBuild(
|
||||||
container: AstroBuildPluginContainer,
|
container: AstroBuildPluginContainer,
|
||||||
) {
|
) {
|
||||||
const { settings, viteConfig } = opts;
|
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);
|
const out = ssr ? settings.config.build.client : getOutDirWithinCwd(settings.config.outDir);
|
||||||
|
|
||||||
// Nothing to do if there is no client-side JS.
|
// Nothing to do if there is no client-side JS.
|
||||||
|
@ -370,7 +364,8 @@ async function runPostBuildHooks(
|
||||||
const config = container.options.settings.config;
|
const config = container.options.settings.config;
|
||||||
const build = container.options.settings.config.build;
|
const build = container.options.settings.config.build;
|
||||||
for (const [fileName, mutation] of mutations) {
|
for (const [fileName, mutation] of mutations) {
|
||||||
const root = isServerLikeOutput(config)
|
const root =
|
||||||
|
container.options.settings.buildOutput === 'server'
|
||||||
? mutation.targets.includes('server')
|
? mutation.targets.includes('server')
|
||||||
? build.server
|
? build.server
|
||||||
: build.client
|
: build.client
|
||||||
|
@ -386,7 +381,7 @@ async function runPostBuildHooks(
|
||||||
* Remove chunks that are used for prerendering only
|
* Remove chunks that are used for prerendering only
|
||||||
*/
|
*/
|
||||||
async function cleanStaticOutput(opts: StaticBuildOptions, internals: BuildInternals) {
|
async function cleanStaticOutput(opts: StaticBuildOptions, internals: BuildInternals) {
|
||||||
const ssr = isServerLikeOutput(opts.settings.config);
|
const ssr = opts.settings.buildOutput === 'server';
|
||||||
const out = ssr
|
const out = ssr
|
||||||
? opts.settings.config.build.server
|
? opts.settings.config.build.server
|
||||||
: getOutDirWithinCwd(opts.settings.config.outDir);
|
: getOutDirWithinCwd(opts.settings.config.outDir);
|
||||||
|
@ -478,7 +473,7 @@ export async function copyFiles(fromFolder: URL, toFolder: URL, includeDotfiles
|
||||||
async function ssrMoveAssets(opts: StaticBuildOptions) {
|
async function ssrMoveAssets(opts: StaticBuildOptions) {
|
||||||
opts.logger.info('build', 'Rearranging server assets...');
|
opts.logger.info('build', 'Rearranging server assets...');
|
||||||
const serverRoot =
|
const serverRoot =
|
||||||
opts.settings.config.output === 'static'
|
opts.settings.buildOutput === 'static'
|
||||||
? opts.settings.config.build.client
|
? opts.settings.config.build.client
|
||||||
: opts.settings.config.build.server;
|
: opts.settings.config.build.server;
|
||||||
const clientRoot = opts.settings.config.build.client;
|
const clientRoot = opts.settings.config.build.client;
|
||||||
|
|
|
@ -130,7 +130,7 @@ export const AstroConfigSchema = z.object({
|
||||||
.optional()
|
.optional()
|
||||||
.default(ASTRO_CONFIG_DEFAULTS.trailingSlash),
|
.default(ASTRO_CONFIG_DEFAULTS.trailingSlash),
|
||||||
output: z
|
output: z
|
||||||
.union([z.literal('static'), z.literal('server'), z.literal('hybrid')])
|
.union([z.literal('static'), z.literal('server')])
|
||||||
.optional()
|
.optional()
|
||||||
.default('static'),
|
.default('static'),
|
||||||
scopedStyleStrategy: z
|
scopedStyleStrategy: z
|
||||||
|
|
|
@ -111,6 +111,7 @@ export function createBaseSettings(config: AstroConfig): AstroSettings {
|
||||||
dotAstroDir,
|
dotAstroDir,
|
||||||
latestAstroVersion: undefined, // Will be set later if applicable when the dev server starts
|
latestAstroVersion: undefined, // Will be set later if applicable when the dev server starts
|
||||||
injectedTypes: [],
|
injectedTypes: [],
|
||||||
|
buildOutput: undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ import astroInternationalization from '../i18n/vite-plugin-i18n.js';
|
||||||
import astroPrefetch from '../prefetch/vite-plugin-prefetch.js';
|
import astroPrefetch from '../prefetch/vite-plugin-prefetch.js';
|
||||||
import astroDevToolbar from '../toolbar/vite-plugin-dev-toolbar.js';
|
import astroDevToolbar from '../toolbar/vite-plugin-dev-toolbar.js';
|
||||||
import astroTransitions from '../transitions/vite-plugin-transitions.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 astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.js';
|
||||||
import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js';
|
import { vitePluginAstroServer } from '../vite-plugin-astro-server/index.js';
|
||||||
import astroVitePlugin from '../vite-plugin-astro/index.js';
|
import astroVitePlugin from '../vite-plugin-astro/index.js';
|
||||||
|
@ -33,6 +33,7 @@ import astroScannerPlugin from '../vite-plugin-scanner/index.js';
|
||||||
import astroScriptsPlugin from '../vite-plugin-scripts/index.js';
|
import astroScriptsPlugin from '../vite-plugin-scripts/index.js';
|
||||||
import astroScriptsPageSSRPlugin from '../vite-plugin-scripts/page-ssr.js';
|
import astroScriptsPageSSRPlugin from '../vite-plugin-scripts/page-ssr.js';
|
||||||
import { vitePluginSSRManifest } from '../vite-plugin-ssr-manifest/index.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 type { Logger } from './logger/core.js';
|
||||||
import { createViteLogger } from './logger/vite.js';
|
import { createViteLogger } from './logger/vite.js';
|
||||||
import { vitePluginMiddleware } from './middleware/vite-plugin.js';
|
import { vitePluginMiddleware } from './middleware/vite-plugin.js';
|
||||||
|
@ -48,6 +49,8 @@ interface CreateViteOptions {
|
||||||
command?: 'dev' | 'build';
|
command?: 'dev' | 'build';
|
||||||
fs?: typeof nodeFs;
|
fs?: typeof nodeFs;
|
||||||
sync: boolean;
|
sync: boolean;
|
||||||
|
manifest: ManifestData;
|
||||||
|
ssrManifest?: SSRManifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALWAYS_NOEXTERNAL = [
|
const ALWAYS_NOEXTERNAL = [
|
||||||
|
@ -75,7 +78,7 @@ const ONLY_DEV_EXTERNAL = [
|
||||||
/** Return a base vite config as a common starting point for all Vite commands. */
|
/** Return a base vite config as a common starting point for all Vite commands. */
|
||||||
export async function createVite(
|
export async function createVite(
|
||||||
commandConfig: vite.InlineConfig,
|
commandConfig: vite.InlineConfig,
|
||||||
{ settings, logger, mode, command, fs = nodeFs, sync }: CreateViteOptions,
|
{ settings, logger, mode, command, fs = nodeFs, sync, manifest, ssrManifest }: CreateViteOptions,
|
||||||
): Promise<vite.InlineConfig> {
|
): Promise<vite.InlineConfig> {
|
||||||
const astroPkgsConfig = await crawlFrameworkPkgs({
|
const astroPkgsConfig = await crawlFrameworkPkgs({
|
||||||
root: fileURLToPath(settings.config.root),
|
root: fileURLToPath(settings.config.root),
|
||||||
|
@ -131,8 +134,9 @@ export async function createVite(
|
||||||
astroScriptsPlugin({ settings }),
|
astroScriptsPlugin({ settings }),
|
||||||
// The server plugin is for dev only and having it run during the build causes
|
// 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.
|
// the build to run very slow as the filewatcher is triggered often.
|
||||||
mode !== 'build' && vitePluginAstroServer({ settings, logger, fs }),
|
mode !== 'build' &&
|
||||||
envVitePlugin({ settings, logger }),
|
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 }),
|
astroEnv({ settings, mode, sync }),
|
||||||
markdownVitePlugin({ settings, logger }),
|
markdownVitePlugin({ settings, logger }),
|
||||||
htmlVitePlugin(),
|
htmlVitePlugin(),
|
||||||
|
@ -140,7 +144,7 @@ export async function createVite(
|
||||||
astroIntegrationsContainerPlugin({ settings, logger }),
|
astroIntegrationsContainerPlugin({ settings, logger }),
|
||||||
astroScriptsPageSSRPlugin({ settings }),
|
astroScriptsPageSSRPlugin({ settings }),
|
||||||
astroHeadPlugin(),
|
astroHeadPlugin(),
|
||||||
astroScannerPlugin({ settings, logger }),
|
astroScannerPlugin({ settings, logger, manifest }),
|
||||||
astroContentVirtualModPlugin({ fs, settings }),
|
astroContentVirtualModPlugin({ fs, settings }),
|
||||||
astroContentImportPlugin({ fs, settings, logger }),
|
astroContentImportPlugin({ fs, settings, logger }),
|
||||||
astroContentAssetPropagationPlugin({ mode, settings }),
|
astroContentAssetPropagationPlugin({ mode, settings }),
|
||||||
|
|
|
@ -4,7 +4,6 @@ import type { AstroSettings } from '../../types/astro.js';
|
||||||
|
|
||||||
import nodeFs from 'node:fs';
|
import nodeFs from 'node:fs';
|
||||||
import * as vite from 'vite';
|
import * as vite from 'vite';
|
||||||
import { injectImageEndpoint } from '../../assets/endpoint/config.js';
|
|
||||||
import {
|
import {
|
||||||
runHookConfigDone,
|
runHookConfigDone,
|
||||||
runHookConfigSetup,
|
runHookConfigSetup,
|
||||||
|
@ -12,9 +11,12 @@ import {
|
||||||
runHookServerStart,
|
runHookServerStart,
|
||||||
} from '../../integrations/hooks.js';
|
} from '../../integrations/hooks.js';
|
||||||
import type { AstroInlineConfig } from '../../types/public/config.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 { createVite } from '../create-vite.js';
|
||||||
import type { Logger } from '../logger/core.js';
|
import type { Logger } from '../logger/core.js';
|
||||||
import { apply as applyPolyfill } from '../polyfill.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';
|
import { syncInternal } from '../sync/index.js';
|
||||||
|
|
||||||
export interface Container {
|
export interface Container {
|
||||||
|
@ -52,8 +54,6 @@ export async function createContainer({
|
||||||
isRestart,
|
isRestart,
|
||||||
});
|
});
|
||||||
|
|
||||||
settings = injectImageEndpoint(settings, 'dev');
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
base,
|
base,
|
||||||
server: { host, headers, open: serverOpen },
|
server: { host, headers, open: serverOpen },
|
||||||
|
@ -81,6 +81,12 @@ export async function createContainer({
|
||||||
.map((r) => r.clientEntrypoint)
|
.map((r) => r.clientEntrypoint)
|
||||||
.filter(Boolean) as string[];
|
.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
|
||||||
|
let manifest = await createRouteManifest({ settings, fsMod: fs }, logger);
|
||||||
|
const devSSRManifest = createDevelopmentManifest(settings);
|
||||||
|
|
||||||
|
manifest = injectDefaultDevRoutes(settings, devSSRManifest, manifest);
|
||||||
|
|
||||||
const viteConfig = await createVite(
|
const viteConfig = await createVite(
|
||||||
{
|
{
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
|
@ -89,8 +95,18 @@ export async function createContainer({
|
||||||
include: rendererClientEntries,
|
include: rendererClientEntries,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ settings, logger, mode: 'dev', command: 'dev', fs, sync: false },
|
{
|
||||||
|
settings,
|
||||||
|
logger,
|
||||||
|
mode: 'dev',
|
||||||
|
command: 'dev',
|
||||||
|
fs,
|
||||||
|
sync: false,
|
||||||
|
manifest,
|
||||||
|
ssrManifest: devSSRManifest,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
await runHookConfigDone({ settings, logger });
|
await runHookConfigDone({ settings, logger });
|
||||||
await syncInternal({
|
await syncInternal({
|
||||||
settings,
|
settings,
|
||||||
|
@ -99,6 +115,7 @@ export async function createContainer({
|
||||||
content: true,
|
content: true,
|
||||||
},
|
},
|
||||||
force: inlineConfig?.force,
|
force: inlineConfig?.force,
|
||||||
|
manifest,
|
||||||
});
|
});
|
||||||
|
|
||||||
const viteServer = await vite.createServer(viteConfig);
|
const viteServer = await vite.createServer(viteConfig);
|
||||||
|
|
|
@ -74,6 +74,7 @@ export const PrerenderClientAddressNotAvailable = {
|
||||||
export const StaticClientAddressNotAvailable = {
|
export const StaticClientAddressNotAvailable = {
|
||||||
name: 'StaticClientAddressNotAvailable',
|
name: 'StaticClientAddressNotAvailable',
|
||||||
title: '`Astro.clientAddress` is not available in static mode.',
|
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:
|
message:
|
||||||
"`Astro.clientAddress` is only available when using `output: 'server'` or `output: 'hybrid'`. Update your Astro config if you need SSR features.",
|
"`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.',
|
hint: 'See https://docs.astro.build/en/guides/server-side-rendering/ for more information on how to enable SSR.',
|
||||||
|
@ -367,8 +368,7 @@ export const GetStaticPathsRequired = {
|
||||||
'`getStaticPaths()` function is required for dynamic routes. Make sure that you `export` a `getStaticPaths` function from your dynamic route.',
|
'`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.
|
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;\`.
|
If you meant for this route to be server-rendered, set \`export const prerender = false;\` in the page.`,
|
||||||
See https://docs.astro.build/en/guides/server-side-rendering/ for more information on non-static rendering.`,
|
|
||||||
} satisfies ErrorData;
|
} satisfies ErrorData;
|
||||||
/**
|
/**
|
||||||
* @docs
|
* @docs
|
||||||
|
@ -393,7 +393,7 @@ export const ReservedSlotName = {
|
||||||
export const NoAdapterInstalled = {
|
export const NoAdapterInstalled = {
|
||||||
name: 'NoAdapterInstalled',
|
name: 'NoAdapterInstalled',
|
||||||
title: 'Cannot use Server-side Rendering without an adapter.',
|
title: 'Cannot use Server-side Rendering without an adapter.',
|
||||||
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.',
|
hint: 'See https://docs.astro.build/en/guides/server-side-rendering/ for more information.',
|
||||||
} satisfies ErrorData;
|
} satisfies ErrorData;
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -126,7 +126,7 @@ export function vitePluginMiddlewareBuild(
|
||||||
writeBundle(_, bundle) {
|
writeBundle(_, bundle) {
|
||||||
for (const [chunkName, chunk] of Object.entries(bundle)) {
|
for (const [chunkName, chunk] of Object.entries(bundle)) {
|
||||||
if (chunk.type !== 'asset' && chunk.fileName === 'middleware.mjs') {
|
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);
|
internals.middlewareEntryPoint = new URL(chunkName, outputDirectory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { resolveConfig } from '../config/config.js';
|
||||||
import { createNodeLogger } from '../config/logging.js';
|
import { createNodeLogger } from '../config/logging.js';
|
||||||
import { createSettings } from '../config/settings.js';
|
import { createSettings } from '../config/settings.js';
|
||||||
import { apply as applyPolyfills } from '../polyfill.js';
|
import { apply as applyPolyfills } from '../polyfill.js';
|
||||||
|
import { createRouteManifest } from '../routing/index.js';
|
||||||
import { ensureProcessNodeEnv } from '../util.js';
|
import { ensureProcessNodeEnv } from '../util.js';
|
||||||
import createStaticPreviewServer from './static-preview-server.js';
|
import createStaticPreviewServer from './static-preview-server.js';
|
||||||
import { getResolvedHostForHttpServer } from './util.js';
|
import { getResolvedHostForHttpServer } from './util.js';
|
||||||
|
@ -35,9 +36,13 @@ export default async function preview(inlineConfig: AstroInlineConfig): Promise<
|
||||||
command: 'preview',
|
command: 'preview',
|
||||||
logger: logger,
|
logger: logger,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Create a route manifest so we can know if the build output is a static site or not
|
||||||
|
await createRouteManifest({ settings: settings, cwd: inlineConfig.root }, logger);
|
||||||
|
|
||||||
await runHookConfigDone({ settings: settings, logger: logger });
|
await runHookConfigDone({ settings: settings, logger: logger });
|
||||||
|
|
||||||
if (settings.config.output === 'static') {
|
if (settings.buildOutput === 'static') {
|
||||||
if (!fs.existsSync(settings.config.outDir)) {
|
if (!fs.existsSync(settings.config.outDir)) {
|
||||||
const outDirPath = fileURLToPath(settings.config.outDir);
|
const outDirPath = fileURLToPath(settings.config.outDir);
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -47,9 +52,11 @@ export default async function preview(inlineConfig: AstroInlineConfig): Promise<
|
||||||
const server = await createStaticPreviewServer(settings, logger);
|
const server = await createStaticPreviewServer(settings, logger);
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.adapter) {
|
if (!settings.adapter) {
|
||||||
throw new Error(`[preview] No adapter found.`);
|
throw new Error(`[preview] No adapter found.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settings.adapter.previewEntrypoint) {
|
if (!settings.adapter.previewEntrypoint) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`[preview] The ${settings.adapter.name} adapter does not support the preview command.`,
|
`[preview] The ${settings.adapter.name} adapter does not support the preview command.`,
|
||||||
|
|
|
@ -148,7 +148,12 @@ export class RenderContext {
|
||||||
|
|
||||||
switch (this.routeData.type) {
|
switch (this.routeData.type) {
|
||||||
case 'endpoint': {
|
case 'endpoint': {
|
||||||
response = await renderEndpoint(componentInstance as any, ctx, serverLike, logger);
|
response = await renderEndpoint(
|
||||||
|
componentInstance as any,
|
||||||
|
ctx,
|
||||||
|
this.routeData.prerender,
|
||||||
|
logger,
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'redirect':
|
case 'redirect':
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import type { IncomingHttpHeaders } from 'node:http';
|
import type { IncomingHttpHeaders } from 'node:http';
|
||||||
import type { Logger } from './logger/core.js';
|
import type { Logger } from './logger/core.js';
|
||||||
import { appendForwardSlash, prependForwardSlash } from './path.js';
|
|
||||||
|
|
||||||
type HeaderType = Headers | Record<string, any> | IncomingHttpHeaders;
|
type HeaderType = Headers | Record<string, any> | IncomingHttpHeaders;
|
||||||
type RequestBody = ArrayBuffer | Blob | ReadableStream | URLSearchParams | FormData;
|
type RequestBody = ArrayBuffer | Blob | ReadableStream | URLSearchParams | FormData;
|
||||||
|
@ -35,7 +34,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.
|
* 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({
|
export function createRequest({
|
||||||
base,
|
|
||||||
url,
|
url,
|
||||||
headers,
|
headers,
|
||||||
clientAddress,
|
clientAddress,
|
||||||
|
@ -60,10 +58,7 @@ export function createRequest({
|
||||||
|
|
||||||
if (typeof url === 'string') url = new URL(url);
|
if (typeof url === 'string') url = new URL(url);
|
||||||
|
|
||||||
const imageEndpoint = prependForwardSlash(appendForwardSlash(base)) + '_image';
|
if (staticLike) {
|
||||||
|
|
||||||
// HACK! astro:assets uses query params for the injected route in `dev`
|
|
||||||
if (staticLike && url.pathname !== imageEndpoint) {
|
|
||||||
url.search = '';
|
url.search = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
14
packages/astro/src/core/routing/dev-default.ts
Normal file
14
packages/astro/src/core/routing/dev-default.ts
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -6,7 +6,9 @@ import { createRequire } from 'node:module';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { bold } from 'kleur/colors';
|
import { bold } from 'kleur/colors';
|
||||||
|
import pLimit from 'p-limit';
|
||||||
import { toRoutingStrategy } from '../../../i18n/utils.js';
|
import { toRoutingStrategy } from '../../../i18n/utils.js';
|
||||||
|
import { runHookRouteSetup } from '../../../integrations/hooks.js';
|
||||||
import { getPrerenderDefault } from '../../../prerender/utils.js';
|
import { getPrerenderDefault } from '../../../prerender/utils.js';
|
||||||
import type { AstroConfig } from '../../../types/public/config.js';
|
import type { AstroConfig } from '../../../types/public/config.js';
|
||||||
import type { RouteData, RoutePart } from '../../../types/public/internal.js';
|
import type { RouteData, RoutePart } from '../../../types/public/internal.js';
|
||||||
|
@ -278,13 +280,7 @@ function createInjectedRoutes({ settings, cwd }: CreateRouteManifestParams): Rou
|
||||||
|
|
||||||
for (const injectedRoute of settings.injectedRoutes) {
|
for (const injectedRoute of settings.injectedRoutes) {
|
||||||
const { pattern: name, entrypoint, prerender: prerenderInjected } = injectedRoute;
|
const { pattern: name, entrypoint, prerender: prerenderInjected } = injectedRoute;
|
||||||
let resolved: string;
|
const { resolved, component } = resolveInjectedRoute(entrypoint, config.root, cwd);
|
||||||
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 segments = removeLeadingForwardSlash(name)
|
const segments = removeLeadingForwardSlash(name)
|
||||||
.split(path.posix.sep)
|
.split(path.posix.sep)
|
||||||
|
@ -478,10 +474,10 @@ function detectRouteCollision(a: RouteData, b: RouteData, _config: AstroConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create manifest of all static routes */
|
/** Create manifest of all static routes */
|
||||||
export function createRouteManifest(
|
export async function createRouteManifest(
|
||||||
params: CreateRouteManifestParams,
|
params: CreateRouteManifestParams,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): ManifestData {
|
): Promise<ManifestData> {
|
||||||
const { settings } = params;
|
const { settings } = params;
|
||||||
const { config } = settings;
|
const { config } = settings;
|
||||||
// Create a map of all routes so redirects can refer to any route
|
// Create a map of all routes so redirects can refer to any route
|
||||||
|
@ -503,6 +499,18 @@ export function createRouteManifest(
|
||||||
...[...fileBasedRoutes, ...injectedRoutes, ...redirectRoutes].sort(routeComparator),
|
...[...fileBasedRoutes, ...injectedRoutes, ...redirectRoutes].sort(routeComparator),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
settings.buildOutput = getPrerenderDefault(config) ? 'static' : 'server';
|
||||||
|
|
||||||
|
// Check the prerender option for each route
|
||||||
|
const limit = pLimit(10);
|
||||||
|
let promises = [];
|
||||||
|
for (const route of routes) {
|
||||||
|
promises.push(
|
||||||
|
limit(async () => await getRoutePrerenderOption(route, settings, params.fsMod, logger)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await Promise.all(promises);
|
||||||
|
|
||||||
// Report route collisions
|
// Report route collisions
|
||||||
for (const [index, higherRoute] of routes.entries()) {
|
for (const [index, higherRoute] of routes.entries()) {
|
||||||
for (const lowerRoute of routes.slice(index + 1)) {
|
for (const lowerRoute of routes.slice(index + 1)) {
|
||||||
|
@ -706,6 +714,49 @@ 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 = await localFs.promises.readFile(
|
||||||
|
fileURLToPath(new URL(route.component, settings.config.root)),
|
||||||
|
'utf-8',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the route is pre-rendered or not
|
||||||
|
const match = /^\s*export\s+const\s+prerender\s*=\s*(true|false);?/m.exec(content);
|
||||||
|
if (match) {
|
||||||
|
route.prerender = match[1] === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
await runHookRouteSetup({ route, settings, logger });
|
||||||
|
|
||||||
|
// If not explicitly set, default to the global setting
|
||||||
|
if (typeof route.prerender === undefined) {
|
||||||
|
route.prerender = getPrerenderDefault(settings.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!route.prerender) settings.buildOutput = 'server';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveInjectedRoute(entrypoint: string, root: URL, cwd?: string) {
|
||||||
|
let resolved;
|
||||||
|
try {
|
||||||
|
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 joinSegments(segments: RoutePart[][]): string {
|
function joinSegments(segments: RoutePart[][]): string {
|
||||||
const arr = segments.map((segment) => {
|
const arr = segments.map((segment) => {
|
||||||
return segment.map((rp) => (rp.dynamic ? `[${rp.content}]` : rp.content)).join('');
|
return segment.map((rp) => (rp.dynamic ? `[${rp.content}]` : rp.content)).join('');
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { syncAstroEnv } from '../../env/sync.js';
|
||||||
import { telemetry } from '../../events/index.js';
|
import { telemetry } from '../../events/index.js';
|
||||||
import { eventCliSession } from '../../events/session.js';
|
import { eventCliSession } from '../../events/session.js';
|
||||||
import { runHookConfigDone, runHookConfigSetup } from '../../integrations/hooks.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 { AstroInlineConfig } from '../../types/public/config.js';
|
import type { AstroInlineConfig } from '../../types/public/config.js';
|
||||||
import { getTimeStat } from '../build/util.js';
|
import { getTimeStat } from '../build/util.js';
|
||||||
import { resolveConfig } from '../config/config.js';
|
import { resolveConfig } from '../config/config.js';
|
||||||
|
@ -31,6 +31,7 @@ import {
|
||||||
} from '../errors/index.js';
|
} from '../errors/index.js';
|
||||||
import type { Logger } from '../logger/core.js';
|
import type { Logger } from '../logger/core.js';
|
||||||
import { formatErrorMessage } from '../messages.js';
|
import { formatErrorMessage } from '../messages.js';
|
||||||
|
import { createRouteManifest } from '../routing/index.js';
|
||||||
import { ensureProcessNodeEnv } from '../util.js';
|
import { ensureProcessNodeEnv } from '../util.js';
|
||||||
|
|
||||||
export type SyncOptions = {
|
export type SyncOptions = {
|
||||||
|
@ -45,6 +46,7 @@ export type SyncOptions = {
|
||||||
// Must be skipped in dev
|
// Must be skipped in dev
|
||||||
content?: boolean;
|
content?: boolean;
|
||||||
};
|
};
|
||||||
|
manifest: ManifestData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function sync(
|
export default async function sync(
|
||||||
|
@ -63,8 +65,9 @@ export default async function sync(
|
||||||
settings,
|
settings,
|
||||||
logger,
|
logger,
|
||||||
});
|
});
|
||||||
|
const manifest = await createRouteManifest({ settings, fsMod: fs }, logger);
|
||||||
await runHookConfigDone({ settings, logger });
|
await runHookConfigDone({ settings, logger });
|
||||||
return await syncInternal({ settings, logger, fs, force: inlineConfig.force });
|
return await syncInternal({ settings, logger, fs, force: inlineConfig.force, manifest });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,6 +98,7 @@ export async function syncInternal({
|
||||||
settings,
|
settings,
|
||||||
skip,
|
skip,
|
||||||
force,
|
force,
|
||||||
|
manifest,
|
||||||
}: SyncOptions): Promise<void> {
|
}: SyncOptions): Promise<void> {
|
||||||
if (force) {
|
if (force) {
|
||||||
await clearContentLayerCache({ settings, logger, fs });
|
await clearContentLayerCache({ settings, logger, fs });
|
||||||
|
@ -104,7 +108,7 @@ export async function syncInternal({
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!skip?.content) {
|
if (!skip?.content) {
|
||||||
await syncContentCollections(settings, { fs, logger });
|
await syncContentCollections(settings, { fs, logger, manifest });
|
||||||
settings.timer.start('Sync content layer');
|
settings.timer.start('Sync content layer');
|
||||||
let store: MutableDataStore | undefined;
|
let store: MutableDataStore | undefined;
|
||||||
try {
|
try {
|
||||||
|
@ -192,7 +196,7 @@ function writeInjectedTypes(settings: AstroSettings, fs: typeof fsMod) {
|
||||||
*/
|
*/
|
||||||
async function syncContentCollections(
|
async function syncContentCollections(
|
||||||
settings: AstroSettings,
|
settings: AstroSettings,
|
||||||
{ logger, fs }: Required<Pick<SyncOptions, 'logger' | 'fs'>>,
|
{ logger, fs, manifest }: Required<Pick<SyncOptions, 'logger' | 'fs' | 'manifest'>>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Needed to load content config
|
// Needed to load content config
|
||||||
const tempViteServer = await createServer(
|
const tempViteServer = await createServer(
|
||||||
|
@ -203,7 +207,7 @@ async function syncContentCollections(
|
||||||
ssr: { external: [] },
|
ssr: { external: [] },
|
||||||
logLevel: 'silent',
|
logLevel: 'silent',
|
||||||
},
|
},
|
||||||
{ settings, logger, mode: 'build', command: 'build', fs, sync: true },
|
{ settings, logger, mode: 'build', command: 'build', fs, sync: true, manifest },
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -158,19 +158,11 @@ export function isEndpoint(file: URL, settings: AstroSettings): boolean {
|
||||||
return !endsWithPageExt(file, settings) && !file.toString().includes('?astro');
|
return !endsWithPageExt(file, settings) && !file.toString().includes('?astro');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isServerLikeOutput(config: AstroConfig) {
|
export function isContentCollectionsCacheEnabled(settings: AstroSettings): boolean {
|
||||||
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 {
|
|
||||||
return (
|
return (
|
||||||
config.experimental.contentCollectionCache &&
|
settings.config.experimental.contentCollectionCache &&
|
||||||
// contentCollectionsCache is an SSG only feature
|
// contentCollectionsCache is an SSG only feature
|
||||||
!isServerLikeOutput(config)
|
settings.buildOutput !== 'server'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { Logger } from '../core/logger/core.js';
|
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 { AstroConfig } from '../types/public/config.js';
|
||||||
import type {
|
import type {
|
||||||
AdapterSupportsKind,
|
AdapterSupportsKind,
|
||||||
|
@ -31,7 +32,7 @@ type ValidationResult = {
|
||||||
export function validateSupportedFeatures(
|
export function validateSupportedFeatures(
|
||||||
adapterName: string,
|
adapterName: string,
|
||||||
featureMap: AstroAdapterFeatureMap,
|
featureMap: AstroAdapterFeatureMap,
|
||||||
config: AstroConfig,
|
settings: AstroSettings,
|
||||||
adapterFeatures: AstroAdapterFeatures | undefined,
|
adapterFeatures: AstroAdapterFeatures | undefined,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): ValidationResult {
|
): ValidationResult {
|
||||||
|
@ -50,7 +51,7 @@ export function validateSupportedFeatures(
|
||||||
adapterName,
|
adapterName,
|
||||||
logger,
|
logger,
|
||||||
'staticOutput',
|
'staticOutput',
|
||||||
() => config?.output === 'static',
|
() => settings.buildOutput === 'static',
|
||||||
);
|
);
|
||||||
|
|
||||||
validationResult.hybridOutput = validateSupportKind(
|
validationResult.hybridOutput = validateSupportKind(
|
||||||
|
@ -58,7 +59,7 @@ export function validateSupportedFeatures(
|
||||||
adapterName,
|
adapterName,
|
||||||
logger,
|
logger,
|
||||||
'hybridOutput',
|
'hybridOutput',
|
||||||
() => config?.output === 'hybrid',
|
() => settings.config.output == 'static' && settings.buildOutput === 'server',
|
||||||
);
|
);
|
||||||
|
|
||||||
validationResult.serverOutput = validateSupportKind(
|
validationResult.serverOutput = validateSupportKind(
|
||||||
|
@ -66,18 +67,18 @@ export function validateSupportedFeatures(
|
||||||
adapterName,
|
adapterName,
|
||||||
logger,
|
logger,
|
||||||
'serverOutput',
|
'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(
|
validationResult.i18nDomains = validateSupportKind(
|
||||||
i18nDomains,
|
i18nDomains,
|
||||||
adapterName,
|
adapterName,
|
||||||
logger,
|
logger,
|
||||||
'i18nDomains',
|
'i18nDomains',
|
||||||
() => {
|
() => {
|
||||||
return config?.output === 'server' && !config?.site;
|
return settings.config?.output === 'server' && !settings.config?.site;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -87,7 +88,7 @@ export function validateSupportedFeatures(
|
||||||
adapterName,
|
adapterName,
|
||||||
logger,
|
logger,
|
||||||
'astro:env getSecret',
|
'astro:env getSecret',
|
||||||
() => Object.keys(config?.env?.schema ?? {}).length !== 0,
|
() => Object.keys(settings.config?.env?.schema ?? {}).length !== 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
return validationResult;
|
return validationResult;
|
||||||
|
|
|
@ -13,7 +13,6 @@ import type { PageBuildData } from '../core/build/types.js';
|
||||||
import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js';
|
import { buildClientDirectiveEntrypoint } from '../core/client-directive/index.js';
|
||||||
import { mergeConfig } from '../core/config/index.js';
|
import { mergeConfig } from '../core/config/index.js';
|
||||||
import type { AstroIntegrationLogger, Logger } from '../core/logger/core.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 { AstroSettings } from '../types/astro.js';
|
||||||
import type { AstroConfig } from '../types/public/config.js';
|
import type { AstroConfig } from '../types/public/config.js';
|
||||||
import type {
|
import type {
|
||||||
|
@ -133,9 +132,9 @@ export async function runHookConfigSetup({
|
||||||
isRestart?: boolean;
|
isRestart?: boolean;
|
||||||
fs?: typeof fsMod;
|
fs?: typeof fsMod;
|
||||||
}): Promise<AstroSettings> {
|
}): Promise<AstroSettings> {
|
||||||
// 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) {
|
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)) {
|
if (await isActionsFilePresent(fs, settings.config.srcDir)) {
|
||||||
settings.config.integrations.push(astroIntegrationActionsRouteHandler({ settings }));
|
settings.config.integrations.push(astroIntegrationActionsRouteHandler({ settings }));
|
||||||
|
@ -314,6 +313,11 @@ export async function runHookConfigDone({
|
||||||
`Integration "${integration.name}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.`,
|
`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) {
|
if (!adapter.supportedAstroFeatures) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`The adapter ${adapter.name} doesn't provide a feature map. It is required in Astro 4.0.`,
|
`The adapter ${adapter.name} doesn't provide a feature map. It is required in Astro 4.0.`,
|
||||||
|
@ -322,7 +326,7 @@ export async function runHookConfigDone({
|
||||||
const validationResult = validateSupportedFeatures(
|
const validationResult = validateSupportedFeatures(
|
||||||
adapter.name,
|
adapter.name,
|
||||||
adapter.supportedAstroFeatures,
|
adapter.supportedAstroFeatures,
|
||||||
settings.config,
|
settings,
|
||||||
// SAFETY: we checked before if it's not present, and we throw an error
|
// SAFETY: we checked before if it's not present, and we throw an error
|
||||||
adapter.adapterFeatures,
|
adapter.adapterFeatures,
|
||||||
logger,
|
logger,
|
||||||
|
@ -358,6 +362,9 @@ export async function runHookConfigDone({
|
||||||
return new URL(normalizedFilename, settings.dotAstroDir);
|
return new URL(normalizedFilename, settings.dotAstroDir);
|
||||||
},
|
},
|
||||||
logger: getLogger(integration, logger),
|
logger: getLogger(integration, logger),
|
||||||
|
get buildOutput() {
|
||||||
|
return settings.buildOutput!; // settings.buildOutput is always set at this point
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
logger,
|
logger,
|
||||||
});
|
});
|
||||||
|
@ -544,15 +551,16 @@ export async function runHookBuildSsr({
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runHookBuildGenerated({
|
export async function runHookBuildGenerated({
|
||||||
config,
|
settings,
|
||||||
logger,
|
logger,
|
||||||
}: {
|
}: {
|
||||||
config: AstroConfig;
|
settings: AstroSettings;
|
||||||
logger: Logger;
|
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']) {
|
if (integration?.hooks?.['astro:build:generated']) {
|
||||||
await withTakingALongTimeMsg({
|
await withTakingALongTimeMsg({
|
||||||
name: integration.name,
|
name: integration.name,
|
||||||
|
@ -568,7 +576,7 @@ export async function runHookBuildGenerated({
|
||||||
}
|
}
|
||||||
|
|
||||||
type RunHookBuildDone = {
|
type RunHookBuildDone = {
|
||||||
config: AstroConfig;
|
settings: AstroSettings;
|
||||||
pages: string[];
|
pages: string[];
|
||||||
routes: RouteData[];
|
routes: RouteData[];
|
||||||
logging: Logger;
|
logging: Logger;
|
||||||
|
@ -576,16 +584,17 @@ type RunHookBuildDone = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function runHookBuildDone({
|
export async function runHookBuildDone({
|
||||||
config,
|
settings,
|
||||||
pages,
|
pages,
|
||||||
routes,
|
routes,
|
||||||
logging,
|
logging,
|
||||||
cacheManifest,
|
cacheManifest,
|
||||||
}: RunHookBuildDone) {
|
}: 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 });
|
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']) {
|
if (integration?.hooks?.['astro:build:done']) {
|
||||||
const logger = getLogger(integration, logging);
|
const logger = getLogger(integration, logging);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { getOutDirWithinCwd } from '../core/build/common.js';
|
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';
|
import type { AstroConfig } from '../types/public/config.js';
|
||||||
|
|
||||||
export function getPrerenderDefault(config: AstroConfig) {
|
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
|
* Returns the correct output directory of the SSR build based on the configuration
|
||||||
*/
|
*/
|
||||||
export function getOutputDirectory(config: AstroConfig): URL {
|
export function getOutputDirectory(settings: AstroSettings): URL {
|
||||||
const ssr = isServerLikeOutput(config);
|
if (settings.buildOutput === 'server') {
|
||||||
if (ssr) {
|
return settings.config.build.server;
|
||||||
return config.build.server;
|
|
||||||
} else {
|
} else {
|
||||||
return getOutDirWithinCwd(config.outDir);
|
return getOutDirWithinCwd(settings.config.outDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ export async function renderEndpoint(
|
||||||
[method: string]: APIRoute;
|
[method: string]: APIRoute;
|
||||||
},
|
},
|
||||||
context: APIContext,
|
context: APIContext,
|
||||||
ssr: boolean,
|
isPrerendered: boolean,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
) {
|
) {
|
||||||
const { request, url } = context;
|
const { request, url } = context;
|
||||||
|
@ -20,12 +20,12 @@ export async function renderEndpoint(
|
||||||
const method = request.method.toUpperCase();
|
const method = request.method.toUpperCase();
|
||||||
// use the exact match on `method`, fallback to ALL
|
// use the exact match on `method`, fallback to ALL
|
||||||
const handler = mod[method] ?? mod['ALL'];
|
const handler = mod[method] ?? mod['ALL'];
|
||||||
if (!ssr && ssr === false && method !== 'GET') {
|
if (isPrerendered && method !== 'GET') {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
'router',
|
'router',
|
||||||
`${url.pathname} ${bold(
|
`${url.pathname} ${bold(
|
||||||
method,
|
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) {
|
if (handler === undefined) {
|
||||||
|
|
|
@ -67,6 +67,11 @@ export interface AstroSettings {
|
||||||
serverIslandMap: NonNullable<SSRManifest['serverIslandMap']>;
|
serverIslandMap: NonNullable<SSRManifest['serverIslandMap']>;
|
||||||
serverIslandNameMap: NonNullable<SSRManifest['serverIslandNameMap']>;
|
serverIslandNameMap: NonNullable<SSRManifest['serverIslandNameMap']>;
|
||||||
injectedTypes: Array<InjectedType>;
|
injectedTypes: Array<InjectedType>;
|
||||||
|
/**
|
||||||
|
* 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.) */
|
/** Generic interface for a component (Astro, Svelte, React, etc.) */
|
||||||
|
|
|
@ -240,16 +240,15 @@ export interface AstroUserConfig {
|
||||||
/**
|
/**
|
||||||
* @docs
|
* @docs
|
||||||
* @name output
|
* @name output
|
||||||
* @type {('static' | 'server' | 'hybrid')}
|
* @type {('static' | 'server')}
|
||||||
* @default `'static'`
|
* @default `'static'`
|
||||||
* @see adapter
|
* @see adapter
|
||||||
* @description
|
* @description
|
||||||
*
|
*
|
||||||
* Specifies the output target for builds.
|
* Specifies the output target for builds.
|
||||||
*
|
*
|
||||||
* - `'static'` - Building a static site to be deployed to any static host.
|
* - `'static'` - Prerender all your pages by default, outputting a completely static site if none of your pages opt out of prerendering.
|
||||||
* - `'server'` - Building an app to be deployed to a host supporting SSR (server-side rendering).
|
* - `'server'` - Use server-side rendering (SSR) for all pages by default, always outputting a server-rendered site.
|
||||||
* - `'hybrid'` - Building a static site with a few server-side rendered pages.
|
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
* import { defineConfig } from 'astro/config';
|
* import { defineConfig } from 'astro/config';
|
||||||
|
@ -259,7 +258,7 @@ export interface AstroUserConfig {
|
||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
output?: 'static' | 'server' | 'hybrid';
|
output?: 'static' | 'server';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @docs
|
* @docs
|
||||||
|
@ -450,7 +449,7 @@ export interface AstroUserConfig {
|
||||||
*
|
*
|
||||||
* Enables security measures for an Astro website.
|
* 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
|
* ```js
|
||||||
* // astro.config.mjs
|
* // astro.config.mjs
|
||||||
|
@ -563,14 +562,14 @@ export interface AstroUserConfig {
|
||||||
* @type {string}
|
* @type {string}
|
||||||
* @default `'./dist/client'`
|
* @default `'./dist/client'`
|
||||||
* @description
|
* @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.
|
* `outDir` controls where the code is built to.
|
||||||
*
|
*
|
||||||
* This value is relative to the `outDir`.
|
* This value is relative to the `outDir`.
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
* {
|
* {
|
||||||
* output: 'server', // or 'hybrid'
|
* output: 'server',
|
||||||
* build: {
|
* build: {
|
||||||
* client: './client'
|
* client: './client'
|
||||||
* }
|
* }
|
||||||
|
|
|
@ -67,6 +67,10 @@ export interface AstroAdapterFeatures {
|
||||||
* Creates an edge function that will communiate with the Astro middleware
|
* Creates an edge function that will communiate with the Astro middleware
|
||||||
*/
|
*/
|
||||||
edgeMiddleware: boolean;
|
edgeMiddleware: boolean;
|
||||||
|
/**
|
||||||
|
* Force Astro to output a server output, even if all the pages are prerendered
|
||||||
|
*/
|
||||||
|
forceServerOutput?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AstroAdapter {
|
export interface AstroAdapter {
|
||||||
|
@ -183,6 +187,7 @@ export interface BaseIntegrationHooks {
|
||||||
setAdapter: (adapter: AstroAdapter) => void;
|
setAdapter: (adapter: AstroAdapter) => void;
|
||||||
injectTypes: (injectedType: InjectedType) => URL;
|
injectTypes: (injectedType: InjectedType) => URL;
|
||||||
logger: AstroIntegrationLogger;
|
logger: AstroIntegrationLogger;
|
||||||
|
buildOutput: 'static' | 'server';
|
||||||
}) => void | Promise<void>;
|
}) => void | Promise<void>;
|
||||||
'astro:server:setup': (options: {
|
'astro:server:setup': (options: {
|
||||||
server: ViteDevServer;
|
server: ViteDevServer;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import type { ModuleLoader } from '../core/module-loader/index.js';
|
||||||
import { Pipeline, loadRenderer } from '../core/render/index.js';
|
import { Pipeline, loadRenderer } from '../core/render/index.js';
|
||||||
import { createDefaultRoutes } from '../core/routing/default.js';
|
import { createDefaultRoutes } from '../core/routing/default.js';
|
||||||
import { findRouteToRewrite } from '../core/routing/rewrite.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 { resolveIdToUrl } from '../core/viteUtils.js';
|
||||||
import type { AstroSettings, ComponentInstance, ManifestData } from '../types/astro.js';
|
import type { AstroSettings, ComponentInstance, ManifestData } from '../types/astro.js';
|
||||||
import type { RewritePayload } from '../types/public/common.js';
|
import type { RewritePayload } from '../types/public/common.js';
|
||||||
|
@ -47,7 +47,7 @@ export class DevPipeline extends Pipeline {
|
||||||
) {
|
) {
|
||||||
const mode = 'development';
|
const mode = 'development';
|
||||||
const resolve = createResolve(loader, config.root);
|
const resolve = createResolve(loader, config.root);
|
||||||
const serverLike = isServerLikeOutput(config);
|
const serverLike = settings.buildOutput === 'server';
|
||||||
const streaming = true;
|
const streaming = true;
|
||||||
super(logger, manifest, mode, [], resolve, serverLike, streaming);
|
super(logger, manifest, mode, [], resolve, serverLike, streaming);
|
||||||
manifest.serverIslandMap = settings.serverIslandMap;
|
manifest.serverIslandMap = settings.serverIslandMap;
|
||||||
|
@ -219,7 +219,7 @@ export class DevPipeline extends Pipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
rewriteKnownRoute(route: string, sourceRoute: RouteData): ComponentInstance {
|
rewriteKnownRoute(route: string, sourceRoute: RouteData): ComponentInstance {
|
||||||
if (isServerLikeOutput(this.config) && sourceRoute.prerender) {
|
if (this.serverLike && sourceRoute.prerender) {
|
||||||
for (let def of this.defaultRoutes) {
|
for (let def of this.defaultRoutes) {
|
||||||
if (route === def.route) {
|
if (route === def.route) {
|
||||||
return def.instance;
|
return def.instance;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||||
import { patchOverlay } from '../core/errors/overlay.js';
|
import { patchOverlay } from '../core/errors/overlay.js';
|
||||||
import type { Logger } from '../core/logger/core.js';
|
import type { Logger } from '../core/logger/core.js';
|
||||||
import { createViteLoader } from '../core/module-loader/index.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 { createRouteManifest } from '../core/routing/index.js';
|
||||||
import { toFallbackType, toRoutingStrategy } from '../i18n/utils.js';
|
import { toFallbackType, toRoutingStrategy } from '../i18n/utils.js';
|
||||||
import type { AstroSettings, ManifestData } from '../types/astro.js';
|
import type { AstroSettings, ManifestData } from '../types/astro.js';
|
||||||
|
@ -24,32 +24,40 @@ export interface AstroPluginOptions {
|
||||||
settings: AstroSettings;
|
settings: AstroSettings;
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
fs: typeof fs;
|
fs: typeof fs;
|
||||||
|
manifest: ManifestData;
|
||||||
|
ssrManifest: SSRManifest;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function createVitePluginAstroServer({
|
export default function createVitePluginAstroServer({
|
||||||
settings,
|
settings,
|
||||||
logger,
|
logger,
|
||||||
fs: fsMod,
|
fs: fsMod,
|
||||||
|
manifest: routeManifest,
|
||||||
|
ssrManifest: devSSRManifest,
|
||||||
}: AstroPluginOptions): vite.Plugin {
|
}: AstroPluginOptions): vite.Plugin {
|
||||||
return {
|
return {
|
||||||
name: 'astro:server',
|
name: 'astro:server',
|
||||||
configureServer(viteServer) {
|
configureServer(viteServer) {
|
||||||
const loader = createViteLoader(viteServer);
|
const loader = createViteLoader(viteServer);
|
||||||
const manifest = createDevelopmentManifest(settings);
|
const pipeline = DevPipeline.create(routeManifest, {
|
||||||
let manifestData: ManifestData = injectDefaultRoutes(
|
loader,
|
||||||
manifest,
|
logger,
|
||||||
createRouteManifest({ settings, fsMod }, logger),
|
manifest: devSSRManifest,
|
||||||
);
|
settings,
|
||||||
const pipeline = DevPipeline.create(manifestData, { loader, logger, manifest, settings });
|
});
|
||||||
const controller = createController({ loader });
|
const controller = createController({ loader });
|
||||||
const localStorage = new AsyncLocalStorage();
|
const localStorage = new AsyncLocalStorage();
|
||||||
|
|
||||||
/** rebuild the route cache + manifest, as needed. */
|
/** rebuild the route cache + manifest, as needed. */
|
||||||
function rebuildManifest(needsManifestRebuild: boolean) {
|
async function rebuildManifest(needsManifestRebuild: boolean) {
|
||||||
pipeline.clearRouteCache();
|
pipeline.clearRouteCache();
|
||||||
if (needsManifestRebuild) {
|
if (needsManifestRebuild) {
|
||||||
manifestData = injectDefaultRoutes(manifest, createRouteManifest({ settings }, logger));
|
routeManifest = injectDefaultDevRoutes(
|
||||||
pipeline.setManifestData(manifestData);
|
settings,
|
||||||
|
devSSRManifest,
|
||||||
|
await createRouteManifest({ settings, fsMod }, logger), // TODO: Handle partial updates to the manifest
|
||||||
|
);
|
||||||
|
pipeline.setManifestData(routeManifest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +102,7 @@ export default function createVitePluginAstroServer({
|
||||||
localStorage.run(request, () => {
|
localStorage.run(request, () => {
|
||||||
handleRequest({
|
handleRequest({
|
||||||
pipeline,
|
pipeline,
|
||||||
manifestData,
|
manifestData: routeManifest,
|
||||||
controller,
|
controller,
|
||||||
incomingRequest: request,
|
incomingRequest: request,
|
||||||
incomingResponse: response,
|
incomingResponse: response,
|
||||||
|
|
|
@ -175,7 +175,7 @@ export async function handleRoute({
|
||||||
body,
|
body,
|
||||||
logger,
|
logger,
|
||||||
clientAddress: incomingRequest.socket.remoteAddress,
|
clientAddress: incomingRequest.socket.remoteAddress,
|
||||||
staticLike: config.output === 'static' || route.prerender,
|
staticLike: route.prerender,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set user specified headers to response object.
|
// Set user specified headers to response object.
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { transform } from 'esbuild';
|
import { transform } from 'esbuild';
|
||||||
import { bold } from 'kleur/colors';
|
|
||||||
import MagicString from 'magic-string';
|
import MagicString from 'magic-string';
|
||||||
import type * as vite from 'vite';
|
import type * as vite from 'vite';
|
||||||
import { loadEnv } from 'vite';
|
import { loadEnv } from 'vite';
|
||||||
import type { Logger } from '../core/logger/core.js';
|
|
||||||
import type { AstroSettings } from '../types/astro.js';
|
import type { AstroSettings } from '../types/astro.js';
|
||||||
import type { AstroConfig } from '../types/public/config.js';
|
import type { AstroConfig } from '../types/public/config.js';
|
||||||
|
|
||||||
interface EnvPluginOptions {
|
interface EnvPluginOptions {
|
||||||
settings: AstroSettings;
|
settings: AstroSettings;
|
||||||
logger: Logger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match `import.meta.env` directly without trailing property access
|
// 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,
|
// Match valid JS variable names (identifiers), which accepts most alphanumeric characters,
|
||||||
// except that the first character cannot be a number.
|
// except that the first character cannot be a number.
|
||||||
const isValidIdentifierRe = /^[_$a-zA-Z][\w$]*$/;
|
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(
|
function getPrivateEnv(
|
||||||
viteConfig: vite.ResolvedConfig,
|
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<string, string>;
|
let privateEnv: Record<string, string>;
|
||||||
let defaultDefines: Record<string, string>;
|
let defaultDefines: Record<string, string>;
|
||||||
let isDev: boolean;
|
let isDev: boolean;
|
||||||
|
@ -173,27 +167,6 @@ export default function envVitePlugin({ settings, logger }: EnvPluginOptions): v
|
||||||
}
|
}
|
||||||
s.prepend(devImportMetaEnvPrepend);
|
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 {
|
return {
|
||||||
code: s.toString(),
|
code: s.toString(),
|
||||||
map: s.generateMap({ hires: 'boundary' }),
|
map: s.generateMap({ hires: 'boundary' }),
|
||||||
|
|
|
@ -1,20 +1,17 @@
|
||||||
import { extname } from 'node:path';
|
import { extname } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
import { bold } from 'kleur/colors';
|
import { bold } from 'kleur/colors';
|
||||||
import type { Plugin as VitePlugin } from 'vite';
|
import type { Plugin as VitePlugin } from 'vite';
|
||||||
import { normalizePath } 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, isServerLikeOutput } from '../core/util.js';
|
import { isEndpoint, isPage } from '../core/util.js';
|
||||||
import { rootRelativePath } from '../core/viteUtils.js';
|
import { rootRelativePath } from '../core/viteUtils.js';
|
||||||
import { runHookRouteSetup } from '../integrations/hooks.js';
|
import type { AstroSettings, ManifestData } from '../types/astro.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';
|
|
||||||
|
|
||||||
export interface AstroPluginScannerOptions {
|
export interface AstroPluginScannerOptions {
|
||||||
settings: AstroSettings;
|
settings: AstroSettings;
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
|
manifest: ManifestData;
|
||||||
}
|
}
|
||||||
|
|
||||||
const KNOWN_FILE_EXTENSIONS = ['.astro', '.js', '.ts'];
|
const KNOWN_FILE_EXTENSIONS = ['.astro', '.js', '.ts'];
|
||||||
|
@ -22,6 +19,7 @@ const KNOWN_FILE_EXTENSIONS = ['.astro', '.js', '.ts'];
|
||||||
export default function astroScannerPlugin({
|
export default function astroScannerPlugin({
|
||||||
settings,
|
settings,
|
||||||
logger,
|
logger,
|
||||||
|
manifest,
|
||||||
}: AstroPluginScannerOptions): VitePlugin {
|
}: AstroPluginScannerOptions): VitePlugin {
|
||||||
return {
|
return {
|
||||||
name: 'astro:scanner',
|
name: 'astro:scanner',
|
||||||
|
@ -42,12 +40,19 @@ export default function astroScannerPlugin({
|
||||||
const fileIsPage = isPage(fileURL, settings);
|
const fileIsPage = isPage(fileURL, settings);
|
||||||
const fileIsEndpoint = isEndpoint(fileURL, settings);
|
const fileIsEndpoint = isEndpoint(fileURL, settings);
|
||||||
if (!(fileIsPage || fileIsEndpoint)) return;
|
if (!(fileIsPage || fileIsEndpoint)) return;
|
||||||
const pageOptions = await getPageOptions(code, id, fileURL, settings, logger);
|
|
||||||
|
const route = manifest.routes.find((r) => {
|
||||||
|
const filePath = new URL(`./${r.component}`, settings.config.root);
|
||||||
|
return normalizePath(fileURLToPath(filePath)) === filename;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!route) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// `getStaticPaths` warning is just a string check, should be good enough for most cases
|
// `getStaticPaths` warning is just a string check, should be good enough for most cases
|
||||||
if (
|
if (
|
||||||
!pageOptions.prerender &&
|
!route.prerender &&
|
||||||
isServerLikeOutput(settings.config) &&
|
|
||||||
code.includes('getStaticPaths') &&
|
code.includes('getStaticPaths') &&
|
||||||
// this should only be valid for `.astro`, `.js` and `.ts` files
|
// this should only be valid for `.astro`, `.js` and `.ts` files
|
||||||
KNOWN_FILE_EXTENSIONS.includes(extname(filename))
|
KNOWN_FILE_EXTENSIONS.includes(extname(filename))
|
||||||
|
@ -68,36 +73,12 @@ export default function astroScannerPlugin({
|
||||||
...meta,
|
...meta,
|
||||||
astro: {
|
astro: {
|
||||||
...(meta.astro ?? { hydratedComponents: [], clientOnlyComponents: [], scripts: [] }),
|
...(meta.astro ?? { hydratedComponents: [], clientOnlyComponents: [], scripts: [] }),
|
||||||
pageOptions,
|
pageOptions: {
|
||||||
|
prerender: route.prerender,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPageOptions(
|
|
||||||
code: string,
|
|
||||||
id: string,
|
|
||||||
fileURL: URL,
|
|
||||||
settings: AstroSettings,
|
|
||||||
logger: Logger,
|
|
||||||
): Promise<PageOptions> {
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -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<PageOptions> {
|
|
||||||
if (!includesExport(code)) return {};
|
|
||||||
if (!didInit) {
|
|
||||||
await eslexer.init;
|
|
||||||
didInit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [, exports] = eslexer.parse(code, id);
|
|
||||||
|
|
||||||
let pageOptions: PageOptions = {};
|
|
||||||
for (const _export of exports) {
|
|
||||||
const { n: name, le: endOfLocalName } = _export;
|
|
||||||
// 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 === 'hybrid',
|
|
||||||
),
|
|
||||||
location: { file: id },
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
pageOptions[name as keyof PageOptions] = isTruthy(suffix);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return pageOptions;
|
|
||||||
}
|
|
|
@ -66,11 +66,6 @@ describe('Astro Global', () => {
|
||||||
let $ = cheerio.load(html);
|
let $ = cheerio.load(html);
|
||||||
assert.match($('#prerender').text(), /Astro route prerender: true/);
|
assert.match($('#prerender').text(), /Astro route prerender: true/);
|
||||||
assert.match($('#prerender-middleware').text(), /Astro route prerender middleware: 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/);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ describe('build assets (server)', () => {
|
||||||
fixture = await loadFixture({
|
fixture = await loadFixture({
|
||||||
root: './fixtures/build-assets/',
|
root: './fixtures/build-assets/',
|
||||||
integrations: [preact()],
|
integrations: [preact()],
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter({ extendAdapter: { adapterFeatures: { forceServerOutput: false } } }),
|
||||||
// test suite was authored when inlineStylesheets defaulted to never
|
// test suite was authored when inlineStylesheets defaulted to never
|
||||||
build: { inlineStylesheets: 'never' },
|
build: { inlineStylesheets: 'never' },
|
||||||
});
|
});
|
||||||
|
@ -148,7 +148,11 @@ describe('build assets (server)', () => {
|
||||||
assets: 'custom-assets',
|
assets: 'custom-assets',
|
||||||
inlineStylesheets: 'never',
|
inlineStylesheets: 'never',
|
||||||
},
|
},
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter({extendAdapter: {
|
||||||
|
adapterFeatures: {
|
||||||
|
forceServerOutput: false,
|
||||||
|
}
|
||||||
|
}}),
|
||||||
});
|
});
|
||||||
await fixture.build();
|
await fixture.build();
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { defineConfig } from "astro/config";
|
||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
output: "hybrid",
|
output: "static",
|
||||||
vite: {
|
vite: {
|
||||||
server: {
|
server: {
|
||||||
https: {
|
https: {
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
---
|
---
|
||||||
import Route from '../components/Route.astro'
|
import Route from '../components/Route.astro';
|
||||||
|
|
||||||
export const prerender = false
|
|
||||||
---
|
---
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
|
|
@ -2,7 +2,7 @@ import svelte from '@astrojs/svelte';
|
||||||
import { defineConfig } from 'astro/config';
|
import { defineConfig } from 'astro/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
integrations: [
|
integrations: [
|
||||||
svelte()
|
svelte()
|
||||||
],
|
],
|
||||||
|
@ -10,4 +10,3 @@ export default defineConfig({
|
||||||
serverIslands: true,
|
serverIslands: true,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ import AsyncEach from '../components/AsyncEach.astro';
|
||||||
import Header from '../components/Header.astro';
|
import Header from '../components/Header.astro';
|
||||||
import { wait } from '../wait';
|
import { wait } from '../wait';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
async function * list() {
|
async function * list() {
|
||||||
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
for(const num of nums) {
|
for(const num of nums) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
---
|
---
|
||||||
import ReactComp from '../components/react.tsx';
|
import ReactComp from '../components/react.tsx';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
|
|
||||||
const foo = { bar: null } as any;
|
const foo = { bar: null } as any;
|
||||||
---
|
---
|
||||||
<ReactComp foo={foo} />
|
<ReactComp foo={foo} />
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
---
|
---
|
||||||
import BareComponent from '../components/BareComponent.astro';
|
import BareComponent from '../components/BareComponent.astro';
|
||||||
import Wait from '../components/Wait.astro';
|
import Wait from '../components/Wait.astro';
|
||||||
|
|
||||||
|
export const prerender = false;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
|
|
|
@ -1763,7 +1763,7 @@ describe('[SSR] i18n routing', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
fixture = await loadFixture({
|
fixture = await loadFixture({
|
||||||
root: './fixtures/i18n-routing-prefix-always/',
|
root: './fixtures/i18n-routing-prefix-always/',
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter(),
|
||||||
});
|
});
|
||||||
await fixture.build();
|
await fixture.build();
|
||||||
|
|
|
@ -137,7 +137,7 @@ describe('Prerender', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('output: "hybrid"', () => {
|
describe('output: "static" with server output', () => {
|
||||||
describe('getStaticPaths - build calls', () => {
|
describe('getStaticPaths - build calls', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
fixture = await loadFixture({
|
fixture = await loadFixture({
|
||||||
|
@ -145,7 +145,7 @@ describe('Prerender', () => {
|
||||||
site: 'https://mysite.dev/',
|
site: 'https://mysite.dev/',
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter(),
|
||||||
base: '/blog',
|
base: '/blog',
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [vitePluginRemovePrerenderExport()],
|
plugins: [vitePluginRemovePrerenderExport()],
|
||||||
},
|
},
|
||||||
|
|
|
@ -113,6 +113,9 @@ export default function ({
|
||||||
assets: 'stable',
|
assets: 'stable',
|
||||||
i18nDomains: 'stable',
|
i18nDomains: 'stable',
|
||||||
},
|
},
|
||||||
|
adapterFeatures: {
|
||||||
|
forceServerOutput: true,
|
||||||
|
},
|
||||||
...extendAdapter,
|
...extendAdapter,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,7 @@ describe('Projects with a underscore in the folder name', () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
fixture = await loadFixture({
|
fixture = await loadFixture({
|
||||||
root: './fixtures/_underscore in folder name/',
|
root: './fixtures/_underscore in folder name/',
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter(),
|
||||||
});
|
});
|
||||||
await fixture.build();
|
await fixture.build();
|
||||||
|
|
|
@ -137,7 +137,7 @@ describe('Astro feature map', function () {
|
||||||
hybridOutput: 'stable',
|
hybridOutput: 'stable',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
output: 'hybrid',
|
config: { output: 'static' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -150,7 +150,8 @@ describe('Astro feature map', function () {
|
||||||
'test',
|
'test',
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
output: 'hybrid',
|
buildOutput: 'server',
|
||||||
|
config: { output: 'static' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -163,7 +164,8 @@ describe('Astro feature map', function () {
|
||||||
'test',
|
'test',
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
output: 'hybrid',
|
buildOutput: 'server',
|
||||||
|
config: { output: 'static' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -177,7 +179,7 @@ describe('Astro feature map', function () {
|
||||||
'test',
|
'test',
|
||||||
{ staticOutput: 'stable' },
|
{ staticOutput: 'stable' },
|
||||||
{
|
{
|
||||||
output: 'static',
|
config: { output: 'static' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -190,7 +192,8 @@ describe('Astro feature map', function () {
|
||||||
'test',
|
'test',
|
||||||
{ staticOutput: 'unsupported' },
|
{ staticOutput: 'unsupported' },
|
||||||
{
|
{
|
||||||
output: 'static',
|
buildOutput: 'static',
|
||||||
|
config: { output: 'static' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -204,7 +207,7 @@ describe('Astro feature map', function () {
|
||||||
'test',
|
'test',
|
||||||
{ hybridOutput: 'stable' },
|
{ hybridOutput: 'stable' },
|
||||||
{
|
{
|
||||||
output: 'hybrid',
|
config: { output: 'static' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -219,7 +222,8 @@ describe('Astro feature map', function () {
|
||||||
hybridOutput: 'unsupported',
|
hybridOutput: 'unsupported',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
output: 'hybrid',
|
buildOutput: 'server',
|
||||||
|
config: { output: 'static' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -233,7 +237,7 @@ describe('Astro feature map', function () {
|
||||||
'test',
|
'test',
|
||||||
{ serverOutput: 'stable' },
|
{ serverOutput: 'stable' },
|
||||||
{
|
{
|
||||||
output: 'server',
|
config: { output: 'server' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -248,7 +252,7 @@ describe('Astro feature map', function () {
|
||||||
serverOutput: 'unsupported',
|
serverOutput: 'unsupported',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
output: 'server',
|
config: { output: 'server' },
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
|
@ -268,12 +272,14 @@ describe('Astro feature map', function () {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
config: {
|
||||||
image: {
|
image: {
|
||||||
service: {
|
service: {
|
||||||
entrypoint: 'astro/assets/services/sharp',
|
entrypoint: 'astro/assets/services/sharp',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
);
|
);
|
||||||
|
@ -290,12 +296,14 @@ describe('Astro feature map', function () {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
config: {
|
||||||
image: {
|
image: {
|
||||||
service: {
|
service: {
|
||||||
entrypoint: 'astro/assets/services/sharp',
|
entrypoint: 'astro/assets/services/sharp',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
{},
|
{},
|
||||||
defaultLogger,
|
defaultLogger,
|
||||||
);
|
);
|
||||||
|
|
|
@ -52,7 +52,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
base: '/search',
|
base: '/search',
|
||||||
trailingSlash: 'never',
|
trailingSlash: 'never',
|
||||||
});
|
});
|
||||||
const manifest = createRouteManifest({
|
const manifest = await createRouteManifest({
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
|
@ -67,6 +67,8 @@ describe('routing - createRouteManifest', () => {
|
||||||
{
|
{
|
||||||
'/src/pages/[contact].astro': `<h1>test</h1>`,
|
'/src/pages/[contact].astro': `<h1>test</h1>`,
|
||||||
'/src/pages/[contact].ts': `<h1>test</h1>`,
|
'/src/pages/[contact].ts': `<h1>test</h1>`,
|
||||||
|
'/src/entrypoint.astro': `<h1>test</h1>`,
|
||||||
|
'/src/entrypoint.ts': `<h1>test</h1>`,
|
||||||
},
|
},
|
||||||
root,
|
root,
|
||||||
);
|
);
|
||||||
|
@ -79,15 +81,15 @@ describe('routing - createRouteManifest', () => {
|
||||||
settings.injectedRoutes = [
|
settings.injectedRoutes = [
|
||||||
{
|
{
|
||||||
pattern: '/about',
|
pattern: '/about',
|
||||||
entrypoint: '@lib/legacy/static.astro',
|
entrypoint: 'src/entrypoint.astro',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: '/api',
|
pattern: '/api',
|
||||||
entrypoint: '@lib/legacy/static.ts',
|
entrypoint: 'src/entrypoint.ts',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const manifest = createRouteManifest({
|
const manifest = await createRouteManifest({
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
|
@ -130,7 +132,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
trailingSlash: 'never',
|
trailingSlash: 'never',
|
||||||
});
|
});
|
||||||
|
|
||||||
const manifest = createRouteManifest({
|
const manifest = await createRouteManifest({
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
|
@ -167,7 +169,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
trailingSlash: 'never',
|
trailingSlash: 'never',
|
||||||
});
|
});
|
||||||
|
|
||||||
const manifest = createRouteManifest({
|
const manifest = await createRouteManifest({
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
|
@ -213,7 +215,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
trailingSlash: 'never',
|
trailingSlash: 'never',
|
||||||
});
|
});
|
||||||
|
|
||||||
const manifest = createRouteManifest({
|
const manifest = await createRouteManifest({
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
|
@ -255,6 +257,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
{
|
{
|
||||||
'/src/pages/index.astro': `<h1>test</h1>`,
|
'/src/pages/index.astro': `<h1>test</h1>`,
|
||||||
'/src/pages/blog/[...slug].astro': `<h1>test</h1>`,
|
'/src/pages/blog/[...slug].astro': `<h1>test</h1>`,
|
||||||
|
'/src/entrypoint.astro': `<h1>test</h1>`,
|
||||||
},
|
},
|
||||||
root,
|
root,
|
||||||
);
|
);
|
||||||
|
@ -268,16 +271,16 @@ describe('routing - createRouteManifest', () => {
|
||||||
settings.injectedRoutes = [
|
settings.injectedRoutes = [
|
||||||
{
|
{
|
||||||
pattern: '/contributing',
|
pattern: '/contributing',
|
||||||
entrypoint: '@lib/legacy/static.astro',
|
entrypoint: 'src/entrypoint.astro',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
pattern: '/[...slug]',
|
pattern: '/[...slug]',
|
||||||
entrypoint: '@lib/legacy/dynamic.astro',
|
entrypoint: 'src/entrypoint.astro',
|
||||||
priority: 'normal',
|
priority: 'normal',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const manifest = createRouteManifest({
|
const manifest = await createRouteManifest({
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
|
@ -327,7 +330,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const manifest = createRouteManifest({
|
const manifest = await createRouteManifest({
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
|
@ -357,6 +360,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
const fs = createFs(
|
const fs = createFs(
|
||||||
{
|
{
|
||||||
'/src/pages/contributing.astro': `<h1>test</h1>`,
|
'/src/pages/contributing.astro': `<h1>test</h1>`,
|
||||||
|
'/src/entrypoint.astro': `<h1>test</h1>`,
|
||||||
},
|
},
|
||||||
root,
|
root,
|
||||||
);
|
);
|
||||||
|
@ -371,7 +375,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
settings.injectedRoutes = [
|
settings.injectedRoutes = [
|
||||||
{
|
{
|
||||||
pattern: '/contributing',
|
pattern: '/contributing',
|
||||||
entrypoint: '@lib/legacy/static.astro',
|
entrypoint: 'src/entrypoint.astro',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -383,14 +387,14 @@ describe('routing - createRouteManifest', () => {
|
||||||
|
|
||||||
const { logger, logs } = getLogger();
|
const { logger, logs } = getLogger();
|
||||||
|
|
||||||
createRouteManifest(manifestOptions, logger);
|
await createRouteManifest(manifestOptions, logger);
|
||||||
|
|
||||||
assert.deepEqual(logs, [
|
assert.deepEqual(logs, [
|
||||||
{
|
{
|
||||||
label: 'router',
|
label: 'router',
|
||||||
level: 'warn',
|
level: 'warn',
|
||||||
message:
|
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,
|
newLine: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -426,7 +430,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
|
|
||||||
const { logger, logs } = getLogger();
|
const { logger, logs } = getLogger();
|
||||||
|
|
||||||
createRouteManifest(manifestOptions, logger);
|
await createRouteManifest(manifestOptions, logger);
|
||||||
|
|
||||||
assert.deepEqual(logs, [
|
assert.deepEqual(logs, [
|
||||||
{
|
{
|
||||||
|
@ -450,6 +454,7 @@ describe('routing - createRouteManifest', () => {
|
||||||
{
|
{
|
||||||
'/src/pages/a-[b].astro': `<h1>test</h1>`,
|
'/src/pages/a-[b].astro': `<h1>test</h1>`,
|
||||||
'/src/pages/blog/a-[b].233.ts': ``,
|
'/src/pages/blog/a-[b].233.ts': ``,
|
||||||
|
'/src/entrypoint.astro': `<h1>test</h1>`,
|
||||||
},
|
},
|
||||||
root,
|
root,
|
||||||
);
|
);
|
||||||
|
@ -467,12 +472,12 @@ describe('routing - createRouteManifest', () => {
|
||||||
settings.injectedRoutes = [
|
settings.injectedRoutes = [
|
||||||
{
|
{
|
||||||
pattern: '/[c]-d',
|
pattern: '/[c]-d',
|
||||||
entrypoint: '@lib/legacy/dynamic.astro',
|
entrypoint: 'src/entrypoint.astro',
|
||||||
priority: 'normal',
|
priority: 'normal',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const manifest = createRouteManifest({
|
const manifest = await createRouteManifest({
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
|
|
|
@ -135,7 +135,7 @@ describe('Route matching', () => {
|
||||||
settings = await createBasicSettings({
|
settings = await createBasicSettings({
|
||||||
root: fileURLToPath(root),
|
root: fileURLToPath(root),
|
||||||
trailingSlash: 'never',
|
trailingSlash: 'never',
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter(),
|
||||||
});
|
});
|
||||||
container = await createContainer({
|
container = await createContainer({
|
||||||
|
@ -147,7 +147,7 @@ describe('Route matching', () => {
|
||||||
const loader = createViteLoader(container.viteServer);
|
const loader = createViteLoader(container.viteServer);
|
||||||
const manifest = createDevelopmentManifest(container.settings);
|
const manifest = createDevelopmentManifest(container.settings);
|
||||||
pipeline = DevPipeline.create(undefined, { loader, logger: defaultLogger, manifest, settings });
|
pipeline = DevPipeline.create(undefined, { loader, logger: defaultLogger, manifest, settings });
|
||||||
manifestData = createRouteManifest(
|
manifestData = await createRouteManifest(
|
||||||
{
|
{
|
||||||
cwd: fileURLToPath(root),
|
cwd: fileURLToPath(root),
|
||||||
settings,
|
settings,
|
||||||
|
|
|
@ -38,7 +38,7 @@ describe('Route sanitization', () => {
|
||||||
settings = await createBasicSettings({
|
settings = await createBasicSettings({
|
||||||
root: fileURLToPath(root),
|
root: fileURLToPath(root),
|
||||||
trailingSlash: 'never',
|
trailingSlash: 'never',
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter(),
|
||||||
});
|
});
|
||||||
container = await createContainer({
|
container = await createContainer({
|
||||||
|
|
|
@ -50,7 +50,7 @@ describe('vite-plugin-astro-server', () => {
|
||||||
},
|
},
|
||||||
'/',
|
'/',
|
||||||
);
|
);
|
||||||
const manifestData = createRouteManifest(
|
const manifestData = await createRouteManifest(
|
||||||
{
|
{
|
||||||
fsMod: fs,
|
fsMod: fs,
|
||||||
settings: pipeline.settings,
|
settings: pipeline.settings,
|
||||||
|
|
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -4,7 +4,7 @@ import { dirname } from 'node:path';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import type { ManagedAppToken } from '@astrojs/studio';
|
import type { ManagedAppToken } from '@astrojs/studio';
|
||||||
import { LibsqlError } from '@libsql/client';
|
import { LibsqlError } from '@libsql/client';
|
||||||
import type { AstroConfig, AstroIntegration } from 'astro';
|
import type { AstroIntegration } from 'astro';
|
||||||
import { blue, yellow } from 'kleur/colors';
|
import { blue, yellow } from 'kleur/colors';
|
||||||
import {
|
import {
|
||||||
type HMRPayload,
|
type HMRPayload,
|
||||||
|
@ -59,14 +59,13 @@ function astroDBIntegration(): AstroIntegration {
|
||||||
};
|
};
|
||||||
|
|
||||||
let command: 'dev' | 'build' | 'preview' | 'sync';
|
let command: 'dev' | 'build' | 'preview' | 'sync';
|
||||||
let output: AstroConfig['output'] = 'server';
|
let finalBuildOutput: string;
|
||||||
return {
|
return {
|
||||||
name: 'astro:db',
|
name: 'astro:db',
|
||||||
hooks: {
|
hooks: {
|
||||||
'astro:config:setup': async ({ updateConfig, config, command: _command, logger }) => {
|
'astro:config:setup': async ({ updateConfig, config, command: _command, logger }) => {
|
||||||
command = _command;
|
command = _command;
|
||||||
root = config.root;
|
root = config.root;
|
||||||
output = config.output;
|
|
||||||
|
|
||||||
if (command === 'preview') return;
|
if (command === 'preview') return;
|
||||||
|
|
||||||
|
@ -105,9 +104,11 @@ function astroDBIntegration(): AstroIntegration {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
'astro:config:done': async ({ config, injectTypes }) => {
|
'astro:config:done': async ({ config, injectTypes, buildOutput }) => {
|
||||||
if (command === 'preview') return;
|
if (command === 'preview') return;
|
||||||
|
|
||||||
|
finalBuildOutput = buildOutput;
|
||||||
|
|
||||||
// TODO: refine where we load tables
|
// TODO: refine where we load tables
|
||||||
// @matthewp: may want to load tables by path at runtime
|
// @matthewp: may want to load tables by path at runtime
|
||||||
const { dbConfig, dependencies, integrationSeedPaths } = await resolveDbConfig(config);
|
const { dbConfig, dependencies, integrationSeedPaths } = await resolveDbConfig(config);
|
||||||
|
@ -159,11 +160,7 @@ function astroDBIntegration(): AstroIntegration {
|
||||||
}, 100);
|
}, 100);
|
||||||
},
|
},
|
||||||
'astro:build:start': async ({ logger }) => {
|
'astro:build:start': async ({ logger }) => {
|
||||||
if (
|
if (!connectToRemote && !databaseFileEnvDefined() && finalBuildOutput === 'server') {
|
||||||
!connectToRemote &&
|
|
||||||
!databaseFileEnvDefined() &&
|
|
||||||
(output === 'server' || output === 'hybrid')
|
|
||||||
) {
|
|
||||||
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 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 =
|
const hint =
|
||||||
'Learn more connecting to Studio: https://docs.astro.build/en/guides/astro-db/#connect-to-astro-studio';
|
'Learn more connecting to Studio: https://docs.astro.build/en/guides/astro-db/#connect-to-astro-studio';
|
||||||
|
|
|
@ -71,7 +71,7 @@ describe('astro:db local database', () => {
|
||||||
it('should throw during the build for hybrid output', async () => {
|
it('should throw during the build for hybrid output', async () => {
|
||||||
let fixture2 = await loadFixture({
|
let fixture2 = await loadFixture({
|
||||||
root: new URL('./fixtures/local-prod/', import.meta.url),
|
root: new URL('./fixtures/local-prod/', import.meta.url),
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: testAdapter(),
|
adapter: testAdapter(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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 `<meta>` tag to each page.
|
// Middleware that adds a `<meta>` tag to each page.
|
||||||
addMiddleware({ entrypoint: '@astrojs/web-vitals/middleware', order: 'post' });
|
addMiddleware({ entrypoint: '@astrojs/web-vitals/middleware', order: 'post' });
|
||||||
// Endpoint that collects metrics and inserts them in Astro DB.
|
// Endpoint that collects metrics and inserts them in Astro DB.
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { defineConfig } from 'astro/config';
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
integrations: [db(), webVitals()],
|
integrations: [db(), webVitals()],
|
||||||
output: 'hybrid',
|
output: 'static',
|
||||||
adapter: node({ mode: 'standalone' }),
|
adapter: node({ mode: 'standalone' }),
|
||||||
devToolbar: {
|
devToolbar: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|
|
@ -8752,12 +8752,10 @@ packages:
|
||||||
|
|
||||||
libsql@0.3.19:
|
libsql@0.3.19:
|
||||||
resolution: {integrity: sha512-Aj5cQ5uk/6fHdmeW0TiXK42FqUlwx7ytmMLPSaUQPin5HKKKuUPD62MAbN4OEweGBBI7q1BekoEN4gPUEL6MZA==}
|
resolution: {integrity: sha512-Aj5cQ5uk/6fHdmeW0TiXK42FqUlwx7ytmMLPSaUQPin5HKKKuUPD62MAbN4OEweGBBI7q1BekoEN4gPUEL6MZA==}
|
||||||
cpu: [x64, arm64, wasm32]
|
|
||||||
os: [darwin, linux, win32]
|
os: [darwin, linux, win32]
|
||||||
|
|
||||||
libsql@0.4.1:
|
libsql@0.4.1:
|
||||||
resolution: {integrity: sha512-qZlR9Yu1zMBeLChzkE/cKfoKV3Esp9cn9Vx5Zirn4AVhDWPcjYhKwbtJcMuHehgk3mH+fJr9qW+3vesBWbQpBg==}
|
resolution: {integrity: sha512-qZlR9Yu1zMBeLChzkE/cKfoKV3Esp9cn9Vx5Zirn4AVhDWPcjYhKwbtJcMuHehgk3mH+fJr9qW+3vesBWbQpBg==}
|
||||||
cpu: [x64, arm64, wasm32]
|
|
||||||
os: [darwin, linux, win32]
|
os: [darwin, linux, win32]
|
||||||
|
|
||||||
lilconfig@2.1.0:
|
lilconfig@2.1.0:
|
||||||
|
|
Loading…
Reference in a new issue