Skip to content

Commit 6fd161d

Browse files
Add the output option (#4015)
* Start of work on astroConfig.mode === 'server' * Add tests and more * adapter -> deploy in some places * Add fallback for `adapter` config * Update more tests * Update image tests * Fix clientAddress test * Updates based on PR review * Add a changeset * Update integrations tests + readme * Oops * Remove old option * Rename `mode` to `output` * Update Node adapter test * Update test * fred pass * fred pass * fred pass * fix test Co-authored-by: Fred K. Schott <fkschott@gmail.com>
1 parent 8859655 commit 6fd161d

67 files changed

Lines changed: 365 additions & 251 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/famous-coins-destroy.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
'astro': minor
3+
'@astrojs/cloudflare': minor
4+
'@astrojs/deno': minor
5+
'@astrojs/image': minor
6+
'@astrojs/netlify': minor
7+
'@astrojs/node': minor
8+
'@astrojs/sitemap': minor
9+
'@astrojs/vercel': minor
10+
---
11+
12+
New `output` configuration option
13+
14+
This change introduces a new "output target" configuration option (`output`). Setting the output target lets you decide the format of your final build, either:
15+
16+
* `"static"` (default): A static site. Your final build will be a collection of static assets (HTML, CSS, JS) that you can deploy to any static site host.
17+
* `"server"`: A dynamic server application. Your final build will be an application that will run in a hosted server environment, generating HTML dynamically for different requests.
18+
19+
If `output` is omitted from your config, the default value `"static"` will be used.
20+
21+
When using the `"server"` output target, you must also include a runtime adapter via the `adapter` configuration. An adapter will *adapt* your final build to run on the deployed platform of your choice (Netlify, Vercel, Node.js, Deno, etc).
22+
23+
To migrate: No action is required for most users. If you currently define an `adapter`, you will need to also add `output: 'server'` to your config file to make it explicit that you are building a server. Here is an example of what that change would look like for someone deploying to Netlify:
24+
25+
```diff
26+
import { defineConfig } from 'astro/config';
27+
import netlify from '@astrojs/netlify/functions';
28+
29+
export default defineConfig({
30+
adapter: netlify(),
31+
+ output: 'server',
32+
});
33+
```

examples/ssr/astro.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import node from '@astrojs/node';
44

55
// https://astro.build/config
66
export default defineConfig({
7+
output: 'server',
78
adapter: node(),
89
integrations: [svelte()],
910
});

packages/astro/src/@types/astro.ts

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ export interface BuildConfig {
8484
client: URL;
8585
server: URL;
8686
serverEntry: string;
87-
staticMode: boolean | undefined;
8887
}
8988

9089
/**
@@ -424,6 +423,50 @@ export interface AstroUserConfig {
424423
*/
425424
trailingSlash?: 'always' | 'never' | 'ignore';
426425

426+
/**
427+
* @docs
428+
* @name adapter
429+
* @typeraw {AstroIntegration}
430+
* @see output
431+
* @description
432+
*
433+
* Deploy to your favorite server, serverless, or edge host with build adapters. Import one of our first-party adapters for [Netlify](https://docs.astro.build/en/guides/deploy/netlify/#adapter-for-ssredge), [Vercel](https://docs.astro.build/en/guides/deploy/vercel/#adapter-for-ssr), and more to engage Astro SSR.
434+
*
435+
* [See our Server-side Rendering guide](https://docs.astro.build/en/guides/server-side-rendering/) for more on SSR, and [our deployment guides](https://docs.astro.build/en/guides/deploy/) for a complete list of hosts.
436+
*
437+
* ```js
438+
* import netlify from '@astrojs/netlify/functions';
439+
* {
440+
* // Example: Build for Netlify serverless deployment
441+
* adapter: netlify(),
442+
* }
443+
* ```
444+
*/
445+
adapter?: AstroIntegration;
446+
447+
/**
448+
* @docs
449+
* @name output
450+
* @type {('static' | 'server')}
451+
* @default `'static'`
452+
* @see adapter
453+
* @description
454+
*
455+
* Specifies the output target for builds.
456+
*
457+
* - 'static' - Building a static site to be deploy to any static host.
458+
* - 'server' - Building an app to be deployed to a host supporting SSR (server-side rendering).
459+
*
460+
* ```js
461+
* import { defineConfig } from 'astro/config';
462+
*
463+
* export default defineConfig({
464+
* output: 'static'
465+
* })
466+
* ```
467+
*/
468+
output?: 'static' | 'server';
469+
427470
/**
428471
* @docs
429472
* @kind heading
@@ -606,26 +649,6 @@ export interface AstroUserConfig {
606649
rehypePlugins?: RehypePlugins;
607650
};
608651

609-
/**
610-
* @docs
611-
* @kind heading
612-
* @name Adapter
613-
* @description
614-
*
615-
* Deploy to your favorite server, serverless, or edge host with build adapters. Import one of our first-party adapters for [Netlify](https://docs.astro.build/en/guides/deploy/netlify/#adapter-for-ssredge), [Vercel](https://docs.astro.build/en/guides/deploy/vercel/#adapter-for-ssr), and more to engage Astro SSR.
616-
*
617-
* [See our Server-side Rendering guide](https://docs.astro.build/en/guides/server-side-rendering/) for more on SSR, and [our deployment guides](https://docs.astro.build/en/guides/deploy/) for a complete list of hosts.
618-
*
619-
* ```js
620-
* import netlify from '@astrojs/netlify/functions';
621-
* {
622-
* // Example: Build for Netlify serverless deployment
623-
* adapter: netlify(),
624-
* }
625-
* ```
626-
*/
627-
adapter?: AstroIntegration;
628-
629652
/**
630653
* @docs
631654
* @kind heading
@@ -747,7 +770,7 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
747770
// This is a more detailed type than zod validation gives us.
748771
// TypeScript still confirms zod validation matches this type.
749772
integrations: AstroIntegration[];
750-
adapter?: AstroIntegration;
773+
751774
// Private:
752775
// We have a need to pass context based on configured state,
753776
// that is different from the user-exposed configuration.

packages/astro/src/adapter-ssg/index.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

packages/astro/src/cli/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
132132
}
133133
}
134134

135-
let { astroConfig, userConfig, userConfigPath } = await openConfig({ cwd: root, flags, cmd });
135+
let { astroConfig, userConfig, userConfigPath } = await openConfig({ cwd: root, flags, cmd, logging });
136136
telemetry.record(event.eventCliSession(cmd, userConfig, flags));
137137

138138
// Common CLI Commands:
@@ -154,7 +154,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
154154
watcher.on('add', async function restartServerOnNewConfigFile(addedFile: string) {
155155
// if there was not a config before, attempt to resolve
156156
if (!userConfigPath && addedFile.includes('astro.config')) {
157-
const addedConfig = await openConfig({ cwd: root, flags, cmd });
157+
const addedConfig = await openConfig({ cwd: root, flags, cmd, logging });
158158
if (addedConfig.userConfigPath) {
159159
info(logging, 'astro', 'Astro config detected. Restarting server...');
160160
astroConfig = addedConfig.astroConfig;

packages/astro/src/core/add/index.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,21 @@ export default async function add(names: string[], { cwd, flags, logging, teleme
6666
['--yes', 'Accept all prompts.'],
6767
['--help', 'Show this help message.'],
6868
],
69-
'Example: Add a UI Framework': [
69+
'Recommended: UI Frameworks': [
7070
['react', 'astro add react'],
7171
['preact', 'astro add preact'],
7272
['vue', 'astro add vue'],
7373
['svelte', 'astro add svelte'],
7474
['solid-js', 'astro add solid-js'],
7575
['lit', 'astro add lit'],
7676
],
77-
'Example: Add an Integration': [
77+
'Recommended: Hosting': [
78+
['netlify', 'astro add netlify'],
79+
['vercel', 'astro add vercel'],
80+
['cloudflare', 'astro add cloudflare'],
81+
['deno', 'astro add deno'],
82+
],
83+
'Recommended: Integrations': [
7884
['tailwind', 'astro add tailwind'],
7985
['partytown', 'astro add partytown'],
8086
['sitemap', 'astro add sitemap'],
@@ -85,9 +91,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme
8591
['deno', 'astro add deno'],
8692
],
8793
},
88-
description: `Check out the full integration catalog: ${cyan(
89-
'https://astro.build/integrations'
90-
)}`,
94+
description: `For more integrations, check out: ${cyan('https://astro.build/integrations')}`,
9195
});
9296
return;
9397
}

packages/astro/src/core/build/generate.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { debug, info } from '../logger/core.js';
1818
import { render } from '../render/core.js';
1919
import { createLinkStylesheetElementSet, createModuleScriptsSet } from '../render/ssr-element.js';
2020
import { createRequest } from '../request.js';
21-
import { getOutputFilename, isBuildingToSSR } from '../util.js';
21+
import { getOutputFilename } from '../util.js';
2222
import { getOutFile, getOutFolder } from './common.js';
2323
import { eachPageData, getPageDataByComponent } from './internal.js';
2424
import type { PageBuildData, SingleFileBuiltModule, StaticBuildOptions } from './types';
@@ -97,7 +97,7 @@ export async function generatePages(
9797
const timer = performance.now();
9898
info(opts.logging, null, `\n${bgGreen(black(' generating static routes '))}`);
9999

100-
const ssr = isBuildingToSSR(opts.astroConfig);
100+
const ssr = opts.astroConfig.output === 'server';
101101
const serverEntry = opts.buildConfig.serverEntry;
102102
const outFolder = ssr ? opts.buildConfig.server : opts.astroConfig.outDir;
103103
const ssrEntryURL = new URL('./' + serverEntry + `?time=${Date.now()}`, outFolder);
@@ -207,7 +207,7 @@ async function generatePath(
207207
}
208208
}
209209

210-
const ssr = isBuildingToSSR(opts.astroConfig);
210+
const ssr = opts.astroConfig.output === 'server';
211211
const url = new URL(opts.astroConfig.base + removeLeadingForwardSlash(pathname), origin);
212212
const options: RenderOptions = {
213213
adapterName: undefined,

packages/astro/src/core/build/index.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import type { AstroTelemetry } from '@astrojs/telemetry';
2-
import type { AstroConfig, BuildConfig, ManifestData, RuntimeMode } from '../../@types/astro';
2+
import type {
3+
AstroAdapter,
4+
AstroConfig,
5+
BuildConfig,
6+
ManifestData,
7+
RuntimeMode,
8+
} from '../../@types/astro';
39
import type { LogOptions } from '../logger/core';
410

511
import fs from 'fs';
@@ -18,7 +24,7 @@ import { debug, info, levels, timerMessage } from '../logger/core.js';
1824
import { apply as applyPolyfill } from '../polyfill.js';
1925
import { RouteCache } from '../render/route-cache.js';
2026
import { createRouteManifest } from '../routing/index.js';
21-
import { createSafeError, isBuildingToSSR } from '../util.js';
27+
import { createSafeError } from '../util.js';
2228
import { collectPagesData } from './page-data.js';
2329
import { staticBuild } from './static-build.js';
2430
import { getTimeStat } from './util.js';
@@ -98,11 +104,14 @@ class AstroBuilder {
98104
client: new URL('./client/', this.config.outDir),
99105
server: new URL('./server/', this.config.outDir),
100106
serverEntry: 'entry.mjs',
101-
staticMode: undefined,
102107
};
103108
await runHookBuildStart({ config: this.config, buildConfig });
104109

105-
info(this.logging, 'build', 'Collecting build information...');
110+
info(this.logging, 'build', `output target: ${colors.green(this.config.output)}`);
111+
if (this.config._ctx.adapter) {
112+
info(this.logging, 'build', `deploy adapter: ${colors.green(this.config._ctx.adapter.name)}`);
113+
}
114+
info(this.logging, 'build', 'Collecting build info...');
106115
this.timer.loadStart = performance.now();
107116
const { assets, allPages } = await collectPagesData({
108117
astroConfig: this.config,
@@ -111,7 +120,7 @@ class AstroBuilder {
111120
origin,
112121
routeCache: this.routeCache,
113122
viteServer,
114-
ssr: isBuildingToSSR(this.config),
123+
ssr: this.config.output === 'server',
115124
});
116125

117126
debug('build', timerMessage('All pages loaded', this.timer.loadStart));
@@ -168,12 +177,11 @@ class AstroBuilder {
168177
});
169178

170179
if (this.logging.level && levels[this.logging.level] <= levels['info']) {
171-
const buildMode = isBuildingToSSR(this.config) ? 'ssr' : 'static';
172180
await this.printStats({
173181
logging: this.logging,
174182
timeStart: this.timer.init,
175183
pageCount: pageNames.length,
176-
buildMode,
184+
buildMode: this.config.output,
177185
});
178186
}
179187
}
@@ -198,7 +206,7 @@ class AstroBuilder {
198206
logging: LogOptions;
199207
timeStart: number;
200208
pageCount: number;
201-
buildMode: 'static' | 'ssr';
209+
buildMode: 'static' | 'server';
202210
}) {
203211
const total = getTimeStat(timeStart, performance.now());
204212

packages/astro/src/core/build/page-data.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { debug } from '../logger/core.js';
1010
import { removeTrailingForwardSlash } from '../path.js';
1111
import { callGetStaticPaths, RouteCache, RouteCacheEntry } from '../render/route-cache.js';
1212
import { matchRoute } from '../routing/match.js';
13-
import { isBuildingToSSR } from '../util.js';
1413

1514
export interface CollectPagesDataOptions {
1615
astroConfig: AstroConfig;
@@ -36,9 +35,6 @@ export async function collectPagesData(
3635
const assets: Record<string, string> = {};
3736
const allPages: AllPagesData = {};
3837
const builtPaths = new Set<string>();
39-
40-
const buildMode = isBuildingToSSR(astroConfig) ? 'ssr' : 'static';
41-
4238
const dataCollectionLogTimeout = setInterval(() => {
4339
info(opts.logging, 'build', 'The data collection step may take longer for larger projects...');
4440
clearInterval(dataCollectionLogTimeout);
@@ -72,7 +68,7 @@ export async function collectPagesData(
7268
};
7369

7470
clearInterval(routeCollectionLogTimeout);
75-
if (buildMode === 'static') {
71+
if (astroConfig.output === 'static') {
7672
const html = `${route.pathname}`.replace(/\/?$/, '/index.html');
7773
debug(
7874
'build',

0 commit comments

Comments
 (0)