Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Fred K. Schott
54c5900a24 wip 2022-06-25 23:26:00 -07:00
Fred K. Schott
ee5b66e298 wip 2022-06-25 22:22:13 -07:00
30 changed files with 242 additions and 15114 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Update "astro add" output to remove confusing multi-select prompt.

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Update the help output to improve formatting

View file

@ -1,4 +1,8 @@
import { defineConfig } from 'astro/config'; import { defineConfig } from 'astro/config';
import netlify from "@astrojs/netlify";
// https://astro.build/config // https://astro.build/config
export default defineConfig({}); export default defineConfig({
mode: "static",
});

View file

@ -41,6 +41,7 @@
"./package.json": "./package.json", "./package.json": "./package.json",
"./runtime/*": "./dist/runtime/*", "./runtime/*": "./dist/runtime/*",
"./server/*": "./dist/runtime/server/*", "./server/*": "./dist/runtime/server/*",
"./adapter-node/server.js": "./dist/adapter-node/server.js",
"./vite-plugin-astro": "./dist/vite-plugin-astro/index.js", "./vite-plugin-astro": "./dist/vite-plugin-astro/index.js",
"./vite-plugin-astro/*": "./dist/vite-plugin-astro/*", "./vite-plugin-astro/*": "./dist/vite-plugin-astro/*",
"./vite-plugin-astro-postprocess": "./dist/vite-plugin-astro-postprocess/index.js", "./vite-plugin-astro-postprocess": "./dist/vite-plugin-astro-postprocess/index.js",

View file

@ -78,7 +78,6 @@ export interface BuildConfig {
client: URL; client: URL;
server: URL; server: URL;
serverEntry: string; serverEntry: string;
staticMode: boolean | undefined;
} }
/** /**
@ -589,10 +588,15 @@ export interface AstroUserConfig {
* @type {AstroIntegration} * @type {AstroIntegration}
* @default `undefined` * @default `undefined`
* @description * @description
* @deprecated
* Add an adapter to build for SSR (server-side rendering). An adapter makes it easy to connect a deployed Astro app to a hosting provider or runtime environment. * Add an adapter to build for SSR (server-side rendering). An adapter makes it easy to connect a deployed Astro app to a hosting provider or runtime environment.
*/ */
adapter?: AstroIntegration; adapter?: AstroIntegration;
// TODO: Document
mode?: 'static' | 'server';
/** /**
* @docs * @docs
* @kind heading * @kind heading
@ -720,7 +724,7 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
// This is a more detailed type than zod validation gives us. // This is a more detailed type than zod validation gives us.
// TypeScript still confirms zod validation matches this type. // TypeScript still confirms zod validation matches this type.
integrations: AstroIntegration[]; integrations: AstroIntegration[];
adapter?: AstroIntegration;
// Private: // Private:
// We have a need to pass context based on configured state, // We have a need to pass context based on configured state,
// that is different from the user-exposed configuration. // that is different from the user-exposed configuration.

View file

@ -0,0 +1,9 @@
import { AstroAdapter } from "../@types/astro";
export function getGenericNodeAdapter(): AstroAdapter {
return {
name: 'node',
serverEntrypoint: 'astro/adapter-node/server.js',
exports: ['handler'],
};
}

View file

@ -0,0 +1,44 @@
import { polyfill } from '@astrojs/webapi';
import type { IncomingMessage, ServerResponse } from 'http';
import type { Readable } from 'stream';
import { SSRManifest } from '../@types/astro';
import { NodeApp } from '../core/app/node';
polyfill(globalThis, {
exclude: 'window document',
});
export function createExports(manifest: SSRManifest) {
const app = new NodeApp(manifest);
return {
async handler(req: IncomingMessage, res: ServerResponse, next?: (err?: unknown) => void) {
const route = app.match(req);
if (route) {
try {
const response = await app.render(req);
await writeWebResponse(res, response);
} catch (err: unknown) {
if (next) {
next(err);
} else {
throw err;
}
}
} else if (next) {
return next();
}
},
};
}
async function writeWebResponse(res: ServerResponse, webResponse: Response) {
const { status, headers, body } = webResponse;
res.writeHead(status, Object.fromEntries(headers.entries()));
if (body) {
for await (const chunk of body as unknown as Readable) {
res.write(chunk);
}
}
res.end();
}

View file

@ -1,23 +0,0 @@
import type { AstroAdapter, AstroIntegration } from '../@types/astro';
export function getAdapter(): AstroAdapter {
return {
name: '@astrojs/ssg',
// This one has no server entrypoint and is mostly just an integration
//serverEntrypoint: '@astrojs/ssg/server.js',
};
}
export default function createIntegration(): AstroIntegration {
return {
name: '@astrojs/ssg',
hooks: {
'astro:config:done': ({ setAdapter }) => {
setAdapter(getAdapter());
},
'astro:build:start': ({ buildConfig }) => {
buildConfig.staticMode = true;
},
},
};
}

View file

@ -37,26 +37,27 @@ type CLICommand =
function printAstroHelp() { function printAstroHelp() {
printHelp({ printHelp({
commandName: 'astro', commandName: 'astro',
usage: '[command] [...flags]',
headline: 'Futuristic web development tool.', headline: 'Futuristic web development tool.',
commands: [ tables: {
['add', 'Add an integration to your configuration.'], Commands: [
['docs', "Launch Astro's Doc site directly from the terminal. "], ['add', 'Add an integration.'],
['dev', 'Run Astro in development mode.'], ['build', 'Build your project and write it to disk.'],
['build', 'Build a pre-compiled production-ready site.'], ['check', 'Check your project for errors.'],
['preview', 'Preview your build locally before deploying.'], ['dev', 'Start the development server.'],
['check', 'Check your project for errors.'], ['docs', "Open documentation in your web browser."],
['telemetry', 'Enable/disable anonymous data collection.'], ['preview', 'Preview your build locally.'],
['--version', 'Show the version number and exit.'], ['telemetry', 'Configure telemetry settings.'],
['--help', 'Show this help message.'], ],
], 'Global Flags': [
flags: [ ['--config <path>', 'Specify your config file.'],
['--host [optional IP]', 'Expose server on network'], ['--root <path>', 'Specify your project root folder.'],
['--config <path>', 'Specify the path to the Astro config file.'], ['--verbose', 'Enable verbose logging.'],
['--root <path>', 'Specify the path to the project root folder.'], ['--silent', 'Disable all logging.'],
['--drafts', 'Include markdown draft pages in the build.'], ['--version', 'Show the version number and exit.'],
['--verbose', 'Enable verbose logging'], ['--help', 'Show this help message.'],
['--silent', 'Disable logging'], ],
], },
}); });
} }

View file

@ -15,12 +15,14 @@ export async function update(subcommand: string, { flags, telemetry }: Telemetry
if (flags.help || !isValid) { if (flags.help || !isValid) {
msg.printHelp({ msg.printHelp({
commandName: 'astro telemetry', commandName: 'astro telemetry',
usage: '<enable|disable|reset>', usage: '[command]',
commands: [ tables: {
['enable', 'Enable anonymous data collection.'], Commands: [
['disable', 'Disable anonymous data collection.'], ['enable', 'Enable anonymous data collection.'],
['reset', 'Reset anonymous data collection settings.'], ['disable', 'Disable anonymous data collection.'],
], ['reset', 'Reset anonymous data collection settings.'],
],
},
}); });
return; return;
} }

View file

@ -1,27 +0,0 @@
export const FIRST_PARTY_FRAMEWORKS = [
{ value: 'react', title: 'React' },
{ value: 'preact', title: 'Preact' },
{ value: 'vue', title: 'Vue' },
{ value: 'svelte', title: 'Svelte' },
{ value: 'solid-js', title: 'Solid' },
{ value: 'lit', title: 'Lit' },
];
export const FIRST_PARTY_ADDONS = [
{ value: 'tailwind', title: 'Tailwind' },
{ value: 'turbolinks', title: 'Turbolinks' },
{ value: 'partytown', title: 'Partytown' },
{ value: 'sitemap', title: 'Sitemap' },
];
export const ALIASES = new Map([
['solid', 'solid-js'],
['tailwindcss', 'tailwind'],
]);
export const CONFIG_STUB = `import { defineConfig } from 'astro/config';\n\nexport default defineConfig({});`;
export const TAILWIND_CONFIG_STUB = `/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}\n`;

View file

@ -11,14 +11,13 @@ import prompts from 'prompts';
import { fileURLToPath, pathToFileURL } from 'url'; import { fileURLToPath, pathToFileURL } from 'url';
import type yargs from 'yargs-parser'; import type yargs from 'yargs-parser';
import { resolveConfigURL } from '../config.js'; import { resolveConfigURL } from '../config.js';
import { debug, error, info, LogOptions } from '../logger/core.js'; import { debug, info, LogOptions } from '../logger/core.js';
import * as msg from '../messages.js'; import * as msg from '../messages.js';
import { printHelp } from '../messages.js'; import { printHelp } from '../messages.js';
import { appendForwardSlash } from '../path.js'; import { appendForwardSlash } from '../path.js';
import { apply as applyPolyfill } from '../polyfill.js'; import { apply as applyPolyfill } from '../polyfill.js';
import { parseNpmName } from '../util.js'; import { parseNpmName } from '../util.js';
import { generate, parse, t, visit } from './babel.js'; import { generate, parse, t, visit } from './babel.js';
import * as CONSTS from './consts.js';
import { ensureImport } from './imports.js'; import { ensureImport } from './imports.js';
import { wrapDefaultExport } from './wrapper.js'; import { wrapDefaultExport } from './wrapper.js';
@ -34,71 +33,77 @@ export interface IntegrationInfo {
packageName: string; packageName: string;
dependencies: [name: string, version: string][]; dependencies: [name: string, version: string][];
} }
const ALIASES = new Map([
['solid', 'solid-js'],
['tailwindcss', 'tailwind'],
]);
const ASTRO_CONFIG_STUB = `import { defineConfig } from 'astro/config';\n\ndefault defineConfig({});`;
const TAILWIND_CONFIG_STUB = `/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{astro,html,js,jsx,md,svelte,ts,tsx,vue}'],
theme: {
extend: {},
},
plugins: [],
}\n`;
export default async function add(names: string[], { cwd, flags, logging, telemetry }: AddOptions) { export default async function add(names: string[], { cwd, flags, logging, telemetry }: AddOptions) {
if (flags.help) { if (flags.help || names.length === 0) {
printHelp({ printHelp({
commandName: 'astro add', commandName: 'astro add',
usage: '[FLAGS] [INTEGRATIONS...]', usage: '[integration]',
flags: [ tables: {
['--yes', 'Add the integration without user interaction.'], Flags: [
['--help', 'Show this help message.'], ['--yes', 'Accept all prompts.'],
], ['--help', 'Show this help message.'],
],
'Recommended: UI Frameworks': [
['react', 'astro add react'],
['preact', 'astro add preact'],
['vue', 'astro add vue'],
['svelte', 'astro add svelte'],
['solid-js', 'astro add solid-js'],
['lit', 'astro add lit'],
],
'Recommended: Hosting': [
['netlify', 'astro add netlify'],
['vercel', 'astro add vercel'],
['cloudflare', 'astro add cloudflare'],
['deno', 'astro add deno'],
],
'Recommended: Integrations': [
['tailwind', 'astro add tailwind'],
['partytown', 'astro add partytown'],
['sitemap', 'astro add sitemap'],
],
},
description: `For more integrations, check out: ${cyan('https://astro.build/integrations')}`,
}); });
return; return;
} }
let configURL: URL | undefined; let configURL: URL | undefined;
const root = pathToFileURL(cwd ? path.resolve(cwd) : process.cwd()); const root = pathToFileURL(cwd ? path.resolve(cwd) : process.cwd());
// TODO: improve error handling for invalid configs
configURL = await resolveConfigURL({ cwd, flags }); configURL = await resolveConfigURL({ cwd, flags });
if (configURL?.pathname.endsWith('package.json')) {
throw new Error(
`Unable to use astro add with package.json#astro configuration! Try migrating to \`astro.config.mjs\` and try again.`
);
}
applyPolyfill(); applyPolyfill();
// If no integrations were given, prompt the user for some popular ones.
if (names.length === 0) {
const response = await prompts([
{
type: 'multiselect',
name: 'frameworks',
message: 'What frameworks would you like to enable?',
instructions: '\n Space to select. Return to submit',
choices: CONSTS.FIRST_PARTY_FRAMEWORKS,
},
{
type: 'multiselect',
name: 'addons',
message: 'What additional integrations would you like to enable?',
instructions: '\n Space to select. Return to submit',
choices: CONSTS.FIRST_PARTY_ADDONS,
},
]);
names = [...(response.frameworks ?? []), ...(response.addons ?? [])];
}
// If still empty after prompting, exit gracefully.
if (names.length === 0) {
error(logging, null, `No integrations specified.`);
return;
}
// Some packages might have a common alias! We normalize those here.
names = names.map((name) => (CONSTS.ALIASES.has(name) ? CONSTS.ALIASES.get(name)! : name));
if (configURL) { if (configURL) {
debug('add', `Found config at ${configURL}`); debug('add', `Found config at ${configURL}`);
} else { } else {
info(logging, 'add', `Unable to locate a config file, generating one for you.`); info(logging, 'add', `Unable to locate a config file, generating one for you.`);
configURL = new URL('./astro.config.mjs', appendForwardSlash(root.href)); configURL = new URL('./astro.config.mjs', appendForwardSlash(root.href));
await fs.writeFile(fileURLToPath(configURL), CONSTS.CONFIG_STUB, { encoding: 'utf-8' }); await fs.writeFile(fileURLToPath(configURL), ASTRO_CONFIG_STUB, { encoding: 'utf-8' });
} }
const integrations = await validateIntegrations(names); // TODO: improve error handling for invalid configs
if (configURL?.pathname.endsWith('package.json')) {
throw new Error(
`Unable to use "astro add" with package.json configuration. Try migrating to \`astro.config.mjs\` and try again.`
);
}
// Some packages might have a common alias! We normalize those here.
const integrationNames = names.map((name) => (ALIASES.has(name) ? ALIASES.get(name)! : name));
const integrations = await validateIntegrations(integrationNames);
let ast: t.File | null = null; let ast: t.File | null = null;
try { try {
@ -194,7 +199,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme
if (await askToContinue({ flags })) { if (await askToContinue({ flags })) {
await fs.writeFile( await fs.writeFile(
fileURLToPath(new URL('./tailwind.config.cjs', configURL)), fileURLToPath(new URL('./tailwind.config.cjs', configURL)),
CONSTS.TAILWIND_CONFIG_STUB, TAILWIND_CONFIG_STUB,
{ encoding: 'utf-8' } { encoding: 'utf-8' }
); );
debug('add', `Generated default ./tailwind.config.cjs file`); debug('add', `Generated default ./tailwind.config.cjs file`);

View file

@ -18,7 +18,7 @@ import { debug, info } from '../logger/core.js';
import { render } from '../render/core.js'; import { render } from '../render/core.js';
import { createLinkStylesheetElementSet, createModuleScriptsSet } from '../render/ssr-element.js'; import { createLinkStylesheetElementSet, createModuleScriptsSet } from '../render/ssr-element.js';
import { createRequest } from '../request.js'; import { createRequest } from '../request.js';
import { getOutputFilename, isBuildingToSSR } from '../util.js'; import { getOutputFilename } from '../util.js';
import { getOutFile, getOutFolder } from './common.js'; import { getOutFile, getOutFolder } from './common.js';
import { eachPageData, getPageDataByComponent } from './internal.js'; import { eachPageData, getPageDataByComponent } from './internal.js';
import type { PageBuildData, SingleFileBuiltModule, StaticBuildOptions } from './types'; import type { PageBuildData, SingleFileBuiltModule, StaticBuildOptions } from './types';
@ -97,7 +97,7 @@ export async function generatePages(
const timer = performance.now(); const timer = performance.now();
info(opts.logging, null, `\n${bgGreen(black(' generating static routes '))}`); info(opts.logging, null, `\n${bgGreen(black(' generating static routes '))}`);
const ssr = isBuildingToSSR(opts.astroConfig); const ssr = opts.astroConfig.mode === 'server';
const serverEntry = opts.buildConfig.serverEntry; const serverEntry = opts.buildConfig.serverEntry;
const outFolder = ssr ? opts.buildConfig.server : opts.astroConfig.outDir; const outFolder = ssr ? opts.buildConfig.server : opts.astroConfig.outDir;
const ssrEntryURL = new URL('./' + serverEntry + `?time=${Date.now()}`, outFolder); const ssrEntryURL = new URL('./' + serverEntry + `?time=${Date.now()}`, outFolder);
@ -207,7 +207,7 @@ async function generatePath(
} }
} }
const ssr = isBuildingToSSR(opts.astroConfig); const ssr = opts.astroConfig.mode === 'server';
const url = new URL(opts.astroConfig.base + removeLeadingForwardSlash(pathname), origin); const url = new URL(opts.astroConfig.base + removeLeadingForwardSlash(pathname), origin);
const options: RenderOptions = { const options: RenderOptions = {
links, links,

View file

@ -1,5 +1,5 @@
import type { AstroTelemetry } from '@astrojs/telemetry'; import type { AstroTelemetry } from '@astrojs/telemetry';
import type { AstroConfig, BuildConfig, ManifestData } from '../../@types/astro'; import type { AstroAdapter, AstroConfig, BuildConfig, ManifestData } from '../../@types/astro';
import type { LogOptions } from '../logger/core'; import type { LogOptions } from '../logger/core';
import fs from 'fs'; import fs from 'fs';
@ -14,11 +14,11 @@ import {
} from '../../integrations/index.js'; } from '../../integrations/index.js';
import { createVite, ViteConfigWithSSR } from '../create-vite.js'; import { createVite, ViteConfigWithSSR } from '../create-vite.js';
import { fixViteErrorMessage } from '../errors.js'; import { fixViteErrorMessage } from '../errors.js';
import { debug, info, levels, timerMessage, warnIfUsingExperimentalSSR } from '../logger/core.js'; import { debug, info, levels, timerMessage } from '../logger/core.js';
import { apply as applyPolyfill } from '../polyfill.js'; import { apply as applyPolyfill } from '../polyfill.js';
import { RouteCache } from '../render/route-cache.js'; import { RouteCache } from '../render/route-cache.js';
import { createRouteManifest } from '../routing/index.js'; import { createRouteManifest } from '../routing/index.js';
import { createSafeError, isBuildingToSSR } from '../util.js'; import { createSafeError } from '../util.js';
import { collectPagesData } from './page-data.js'; import { collectPagesData } from './page-data.js';
import { staticBuild } from './static-build.js'; import { staticBuild } from './static-build.js';
import { getTimeStat } from './util.js'; import { getTimeStat } from './util.js';
@ -79,7 +79,6 @@ class AstroBuilder {
{ astroConfig: this.config, logging, mode: 'build' } { astroConfig: this.config, logging, mode: 'build' }
); );
await runHookConfigDone({ config: this.config }); await runHookConfigDone({ config: this.config });
warnIfUsingExperimentalSSR(logging, this.config);
const viteServer = await vite.createServer(viteConfig); const viteServer = await vite.createServer(viteConfig);
debug('build', timerMessage('Vite started', this.timer.viteStart)); debug('build', timerMessage('Vite started', this.timer.viteStart));
return { viteConfig, viteServer }; return { viteConfig, viteServer };
@ -98,11 +97,21 @@ class AstroBuilder {
client: new URL('./client/', this.config.outDir), client: new URL('./client/', this.config.outDir),
server: new URL('./server/', this.config.outDir), server: new URL('./server/', this.config.outDir),
serverEntry: 'entry.mjs', serverEntry: 'entry.mjs',
staticMode: undefined,
}; };
await runHookBuildStart({ config: this.config, buildConfig }); await runHookBuildStart({ config: this.config, buildConfig });
info(this.logging, 'build', 'Collecting build information...'); const getPrettyDeployTarget = (adapter: AstroAdapter | undefined) => {
if (adapter?.name === 'node') {
return 'node.js (generic)';
}
if (adapter?.name) {
return adapter?.name;
}
return 'unknown';
}
info(this.logging, 'build', `build target: ${colors.green(this.config.mode)}`);
info(this.logging, 'build', `deploy target: ${colors.green(getPrettyDeployTarget(this.config._ctx.adapter))}`);
info(this.logging, 'build', 'Collecting build info...');
this.timer.loadStart = performance.now(); this.timer.loadStart = performance.now();
const { assets, allPages } = await collectPagesData({ const { assets, allPages } = await collectPagesData({
astroConfig: this.config, astroConfig: this.config,
@ -111,7 +120,7 @@ class AstroBuilder {
origin, origin,
routeCache: this.routeCache, routeCache: this.routeCache,
viteServer, viteServer,
ssr: isBuildingToSSR(this.config), ssr: this.config.mode === 'server',
}); });
debug('build', timerMessage('All pages loaded', this.timer.loadStart)); debug('build', timerMessage('All pages loaded', this.timer.loadStart));
@ -161,12 +170,11 @@ class AstroBuilder {
}); });
if (this.logging.level && levels[this.logging.level] <= levels['info']) { if (this.logging.level && levels[this.logging.level] <= levels['info']) {
const buildMode = isBuildingToSSR(this.config) ? 'ssr' : 'static';
await this.printStats({ await this.printStats({
logging: this.logging, logging: this.logging,
timeStart: this.timer.init, timeStart: this.timer.init,
pageCount: pageNames.length, pageCount: pageNames.length,
buildMode, buildMode: this.config.mode,
}); });
} }
} }
@ -191,7 +199,7 @@ class AstroBuilder {
logging: LogOptions; logging: LogOptions;
timeStart: number; timeStart: number;
pageCount: number; pageCount: number;
buildMode: 'static' | 'ssr'; buildMode: 'static' | 'server';
}) { }) {
const total = getTimeStat(timeStart, performance.now()); const total = getTimeStat(timeStart, performance.now());

View file

@ -10,7 +10,6 @@ import { debug } from '../logger/core.js';
import { removeTrailingForwardSlash } from '../path.js'; import { removeTrailingForwardSlash } from '../path.js';
import { callGetStaticPaths, RouteCache, RouteCacheEntry } from '../render/route-cache.js'; import { callGetStaticPaths, RouteCache, RouteCacheEntry } from '../render/route-cache.js';
import { matchRoute } from '../routing/match.js'; import { matchRoute } from '../routing/match.js';
import { isBuildingToSSR } from '../util.js';
export interface CollectPagesDataOptions { export interface CollectPagesDataOptions {
astroConfig: AstroConfig; astroConfig: AstroConfig;
@ -36,9 +35,6 @@ export async function collectPagesData(
const assets: Record<string, string> = {}; const assets: Record<string, string> = {};
const allPages: AllPagesData = {}; const allPages: AllPagesData = {};
const builtPaths = new Set<string>(); const builtPaths = new Set<string>();
const buildMode = isBuildingToSSR(astroConfig) ? 'ssr' : 'static';
const dataCollectionLogTimeout = setInterval(() => { const dataCollectionLogTimeout = setInterval(() => {
info(opts.logging, 'build', 'The data collection step may take longer for larger projects...'); info(opts.logging, 'build', 'The data collection step may take longer for larger projects...');
clearInterval(dataCollectionLogTimeout); clearInterval(dataCollectionLogTimeout);
@ -72,7 +68,7 @@ export async function collectPagesData(
}; };
clearInterval(routeCollectionLogTimeout); clearInterval(routeCollectionLogTimeout);
if (buildMode === 'static') { if (astroConfig.mode === 'static') {
const html = `${route.pathname}`.replace(/\/?$/, '/index.html'); const html = `${route.pathname}`.replace(/\/?$/, '/index.html');
debug( debug(
'build', 'build',

View file

@ -11,7 +11,6 @@ import { runHookBuildSetup } from '../../integrations/index.js';
import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js'; import { rollupPluginAstroBuildCSS } from '../../vite-plugin-build-css/index.js';
import type { ViteConfigWithSSR } from '../create-vite'; import type { ViteConfigWithSSR } from '../create-vite';
import { info } from '../logger/core.js'; import { info } from '../logger/core.js';
import { isBuildingToSSR } from '../util.js';
import { generatePages } from './generate.js'; import { generatePages } from './generate.js';
import { trackPageData } from './internal.js'; import { trackPageData } from './internal.js';
import type { PageBuildData, StaticBuildOptions } from './types'; import type { PageBuildData, StaticBuildOptions } from './types';
@ -60,9 +59,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
info( info(
opts.logging, opts.logging,
'build', 'build',
isBuildingToSSR(astroConfig) `Building ${astroConfig.mode} entrypoints...`
? 'Building SSR entrypoints...'
: 'Building entrypoints for prerendering...'
); );
const ssrResult = (await ssrBuild(opts, internals, pageInput)) as RollupOutput; const ssrResult = (await ssrBuild(opts, internals, pageInput)) as RollupOutput;
info(opts.logging, 'build', dim(`Completed in ${getTimeStat(timer.ssr, performance.now())}.`)); info(opts.logging, 'build', dim(`Completed in ${getTimeStat(timer.ssr, performance.now())}.`));
@ -79,7 +76,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
await clientBuild(opts, internals, clientInput); await clientBuild(opts, internals, clientInput);
timer.generate = performance.now(); timer.generate = performance.now();
if (opts.buildConfig.staticMode) { if (astroConfig.mode === 'static') {
try { try {
await generatePages(ssrResult, opts, internals, facadeIdToPageDataMap); await generatePages(ssrResult, opts, internals, facadeIdToPageDataMap);
} finally { } finally {
@ -96,7 +93,7 @@ export async function staticBuild(opts: StaticBuildOptions) {
async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) { async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
const { astroConfig, viteConfig } = opts; const { astroConfig, viteConfig } = opts;
const ssr = isBuildingToSSR(astroConfig); const ssr = astroConfig.mode === 'server';
const out = ssr ? opts.buildConfig.server : astroConfig.outDir; const out = ssr ? opts.buildConfig.server : astroConfig.outDir;
const viteBuildConfig: ViteConfigWithSSR = { const viteBuildConfig: ViteConfigWithSSR = {
@ -140,7 +137,7 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
}), }),
...(viteConfig.plugins || []), ...(viteConfig.plugins || []),
// SSR needs to be last // SSR needs to be last
isBuildingToSSR(opts.astroConfig) && opts.astroConfig.mode === 'server' &&
vitePluginSSR(opts, internals, opts.astroConfig._ctx.adapter!), vitePluginSSR(opts, internals, opts.astroConfig._ctx.adapter!),
vitePluginAnalyzer(opts.astroConfig, internals), vitePluginAnalyzer(opts.astroConfig, internals),
], ],
@ -171,7 +168,7 @@ async function clientBuild(
) { ) {
const { astroConfig, viteConfig } = opts; const { astroConfig, viteConfig } = opts;
const timer = performance.now(); const timer = performance.now();
const ssr = isBuildingToSSR(astroConfig); const ssr = astroConfig.mode === 'server';
const out = ssr ? opts.buildConfig.client : astroConfig.outDir; const out = ssr ? opts.buildConfig.client : astroConfig.outDir;
// Nothing to do if there is no client-side JS. // Nothing to do if there is no client-side JS.
@ -272,7 +269,7 @@ async function copyFiles(fromFolder: URL, toFolder: URL) {
async function ssrMoveAssets(opts: StaticBuildOptions) { async function ssrMoveAssets(opts: StaticBuildOptions) {
info(opts.logging, 'build', 'Rearranging server assets...'); info(opts.logging, 'build', 'Rearranging server assets...');
const serverRoot = opts.buildConfig.staticMode const serverRoot = opts.astroConfig.mode === 'static'
? opts.buildConfig.client ? opts.buildConfig.client
: opts.buildConfig.server; : opts.buildConfig.server;
const clientRoot = opts.buildConfig.client; const clientRoot = opts.buildConfig.client;

View file

@ -1,6 +1,5 @@
import type { Plugin as VitePlugin } from 'vite'; import type { Plugin as VitePlugin } from 'vite';
import { pagesVirtualModuleId, resolvedPagesVirtualModuleId } from '../app/index.js'; import { pagesVirtualModuleId, resolvedPagesVirtualModuleId } from '../app/index.js';
import { isBuildingToSSR } 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 { eachPageData } from './internal.js'; import { eachPageData } from './internal.js';
@ -11,7 +10,7 @@ export function vitePluginPages(opts: StaticBuildOptions, internals: BuildIntern
name: '@astro/plugin-build-pages', name: '@astro/plugin-build-pages',
options(options) { options(options) {
if (!isBuildingToSSR(opts.astroConfig)) { if (opts.astroConfig.mode === 'static') {
return addRollupInput(options, [pagesVirtualModuleId]); return addRollupInput(options, [pagesVirtualModuleId]);
} }
}, },

View file

@ -93,7 +93,6 @@ export const LEGACY_ASTRO_CONFIG_KEYS = new Set([
]); ]);
export const AstroConfigSchema = z.object({ export const AstroConfigSchema = z.object({
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
root: z root: z
.string() .string()
.optional() .optional()
@ -132,6 +131,10 @@ export const AstroConfigSchema = z.object({
.union([z.literal('always'), z.literal('never'), z.literal('ignore')]) .union([z.literal('always'), z.literal('never'), z.literal('ignore')])
.optional() .optional()
.default(ASTRO_CONFIG_DEFAULTS.trailingSlash), .default(ASTRO_CONFIG_DEFAULTS.trailingSlash),
mode: z
.union([z.literal('static'), z.literal('server')])
.optional()
.default('static'),
build: z build: z
.object({ .object({
format: z format: z
@ -227,6 +230,9 @@ export const AstroConfigSchema = z.object({
}) })
.optional() .optional()
.default({}), .default({}),
// Deprecated:
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
}); });
/** Turn raw config values into normalized values */ /** Turn raw config values into normalized values */

View file

@ -11,7 +11,7 @@ import {
runHookServerStart, runHookServerStart,
} from '../../integrations/index.js'; } from '../../integrations/index.js';
import { createVite } from '../create-vite.js'; import { createVite } from '../create-vite.js';
import { info, LogOptions, warn, warnIfUsingExperimentalSSR } from '../logger/core.js'; import { info, LogOptions, warn } from '../logger/core.js';
import * as msg from '../messages.js'; import * as msg from '../messages.js';
import { apply as applyPolyfill } from '../polyfill.js'; import { apply as applyPolyfill } from '../polyfill.js';
@ -58,7 +58,6 @@ export default async function dev(config: AstroConfig, options: DevOptions): Pro
{ astroConfig: config, logging: options.logging, mode: 'dev' } { astroConfig: config, logging: options.logging, mode: 'dev' }
); );
await runHookConfigDone({ config }); await runHookConfigDone({ config });
warnIfUsingExperimentalSSR(options.logging, config);
const viteServer = await vite.createServer(viteConfig); const viteServer = await vite.createServer(viteConfig);
runHookServerSetup({ config, server: viteServer }); runHookServerSetup({ config, server: viteServer });
await viteServer.listen(port); await viteServer.listen(port);

View file

@ -1,13 +1,12 @@
import type { EndpointHandler } from '../../../@types/astro'; import type { EndpointHandler } from '../../../@types/astro';
import type { SSROptions } from '../../render/dev'; import type { SSROptions } from '../../render/dev';
import { preload } from '../../render/dev/index.js'; import { preload } from '../../render/dev/index.js';
import { isBuildingToSSR } from '../../util.js';
import { call as callEndpoint } from '../index.js'; import { call as callEndpoint } from '../index.js';
export async function call(ssrOpts: SSROptions) { export async function call(ssrOpts: SSROptions) {
const [, mod] = await preload(ssrOpts); const [, mod] = await preload(ssrOpts);
return await callEndpoint(mod as unknown as EndpointHandler, { return await callEndpoint(mod as unknown as EndpointHandler, {
...ssrOpts, ...ssrOpts,
ssr: isBuildingToSSR(ssrOpts.astroConfig), ssr: ssrOpts.astroConfig.mode === 'server',
}); });
} }

View file

@ -128,16 +128,3 @@ export function timerMessage(message: string, startTime: number = Date.now()) {
return `${message} ${dim(timeDisplay)}`; return `${message} ${dim(timeDisplay)}`;
} }
/**
* A warning that SSR is experimental. Remove when we can.
*/
export function warnIfUsingExperimentalSSR(opts: LogOptions, config: AstroConfig) {
if (config._ctx.adapter?.serverEntrypoint) {
warn(
opts,
'warning',
bold(`Warning:`),
`SSR support is still experimental and subject to API changes. If using in production, pin your dependencies to prevent accidental breakage.`
);
}
}

View file

@ -126,6 +126,7 @@ export const logger = {
export function enableVerboseLogging() { export function enableVerboseLogging() {
//debugPackage.enable('*,-babel'); //debugPackage.enable('*,-babel');
console.log('ah!');
debug('cli', '--verbose flag enabled! Enabling: DEBUG="*,-babel"'); debug('cli', '--verbose flag enabled! Enabling: DEBUG="*,-babel"');
debug( debug(
'cli', 'cli',

View file

@ -248,29 +248,28 @@ export function printHelp({
commandName, commandName,
headline, headline,
usage, usage,
commands, tables,
flags, description,
}: { }: {
commandName: string; commandName: string;
headline?: string; headline?: string;
usage?: string; usage?: string;
commands?: [command: string, help: string][]; tables?: Record<string, [command: string, help: string][]>;
flags?: [flag: string, help: string][]; description?: string,
}) { }) {
const linebreak = () => ''; const linebreak = () => '';
const title = (label: string) => ` ${bgWhite(black(` ${label} `))}`; const title = (label: string) => ` ${bgWhite(black(` ${label} `))}`;
const table = (rows: [string, string][], opts: { padding: number; prefix: string }) => { const table = (rows: [string, string][], { padding }: { padding: number }) => {
const split = rows.some((row) => { const split = process.stdout.columns < 60;
const message = `${opts.prefix}${' '.repeat(opts.padding)}${row[1]}`;
return message.length > process.stdout.columns;
});
let raw = ''; let raw = '';
for (const row of rows) { for (const row of rows) {
raw += `${opts.prefix}${bold(`${row[0]}`.padStart(opts.padding - opts.prefix.length))}`; if (split) {
if (split) raw += '\n '; raw += ` ${row[0]}\n `;
raw += ' ' + dim(row[1]) + '\n'; } else {
raw += `${(`${row[0]}`.padStart(padding))}`;
}
raw += ' ' + dim(row[1]) + '\n';
} }
return raw.slice(0, -1); // remove latest \n return raw.slice(0, -1); // remove latest \n
@ -291,18 +290,21 @@ export function printHelp({
message.push(linebreak(), ` ${green(commandName)} ${bold(usage)}`); message.push(linebreak(), ` ${green(commandName)} ${bold(usage)}`);
} }
if (commands) { if (tables) {
message.push( function calculateTablePadding(rows: [string, string][]) {
linebreak(), return rows.reduce((val, [first]) => Math.max(val, first.length), 0) + 2;
title('Commands'), };
table(commands, { padding: 28, prefix: ` ${commandName || 'astro'} ` }) const tableEntries = Object.entries(tables);
); const padding = Math.max(...tableEntries.map(([, rows]) => calculateTablePadding(rows)));
for (const [tableTitle, tableRows] of tableEntries) {
message.push(linebreak(), title(tableTitle), table(tableRows, { padding }));
}
} }
if (flags) { if (description) {
message.push(linebreak(), title('Flags'), table(flags, { padding: 28, prefix: ' ' })); message.push(linebreak(), `${description}`);
} }
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log(message.join('\n')); console.log(message.join('\n') + '\n');
} }

View file

@ -11,7 +11,6 @@ import type {
} from '../../../@types/astro'; } from '../../../@types/astro';
import { prependForwardSlash } from '../../../core/path.js'; import { prependForwardSlash } from '../../../core/path.js';
import { LogOptions } from '../../logger/core.js'; import { LogOptions } from '../../logger/core.js';
import { isBuildingToSSR } from '../../util.js';
import { render as coreRender } from '../core.js'; import { render as coreRender } from '../core.js';
import { RouteCache } from '../route-cache.js'; import { RouteCache } from '../route-cache.js';
import { createModuleScriptElementWithSrcSet } from '../ssr-element.js'; import { createModuleScriptElementWithSrcSet } from '../ssr-element.js';
@ -183,7 +182,7 @@ export async function render(
route, route,
routeCache, routeCache,
site: astroConfig.site ? new URL(astroConfig.base, astroConfig.site).toString() : undefined, site: astroConfig.site ? new URL(astroConfig.base, astroConfig.site).toString() : undefined,
ssr: isBuildingToSSR(astroConfig), ssr: astroConfig.mode === 'server',
}); });
return response; return response;

View file

@ -62,7 +62,7 @@ export function createRequest({
warn( warn(
logging, logging,
'ssg', 'ssg',
`Headers are not exposed in static-site generation (SSG) mode. To enable reading headers you need to set an SSR adapter in your config.` `Headers are not exposed in static-site generation (SSG) mode. To enable reading headers: set \`mode: "server"\` in your config file.`
); );
return _headers; return _headers;
}, },

View file

@ -7,12 +7,11 @@ import {
BuildConfig, BuildConfig,
RouteData, RouteData,
} from '../@types/astro.js'; } from '../@types/astro.js';
import ssgAdapter from '../adapter-ssg/index.js'; import { getGenericNodeAdapter } from '../adapter-node/index.js';
import type { SerializedSSRManifest } from '../core/app/types'; import type { SerializedSSRManifest } from '../core/app/types';
import type { PageBuildData } from '../core/build/types'; import type { PageBuildData } from '../core/build/types';
import { mergeConfig } from '../core/config.js'; import { mergeConfig } from '../core/config.js';
import type { ViteConfigWithSSR } from '../core/create-vite.js'; import type { ViteConfigWithSSR } from '../core/create-vite.js';
import { isBuildingToSSR } from '../core/util.js';
type Hooks< type Hooks<
Hook extends keyof AstroIntegration['hooks'], Hook extends keyof AstroIntegration['hooks'],
@ -26,9 +25,10 @@ export async function runHookConfigSetup({
config: AstroConfig; config: AstroConfig;
command: 'dev' | 'build'; command: 'dev' | 'build';
}): Promise<AstroConfig> { }): Promise<AstroConfig> {
if (_config.adapter) { // DEPRECATED
_config.integrations.push(_config.adapter); // if (_config.adapter) {
} // _config.integrations.push(_config.adapter);
// }
let updatedConfig: AstroConfig = { ..._config }; let updatedConfig: AstroConfig = { ..._config };
for (const integration of _config.integrations) { for (const integration of _config.integrations) {
@ -86,7 +86,7 @@ export async function runHookConfigDone({ config }: { config: AstroConfig }) {
setAdapter(adapter) { setAdapter(adapter) {
if (config._ctx.adapter && config._ctx.adapter.name !== adapter.name) { if (config._ctx.adapter && config._ctx.adapter.name !== adapter.name) {
throw new Error( throw new Error(
`Adapter already set to ${config._ctx.adapter.name}. You can only have one adapter.` `Integration "${integration.name}" conflicts with "${config._ctx.adapter.name}". You can only configure one deployment integration.`
); );
} }
config._ctx.adapter = adapter; config._ctx.adapter = adapter;
@ -94,22 +94,13 @@ export async function runHookConfigDone({ config }: { config: AstroConfig }) {
}); });
} }
} }
// Call the default adapter // Call the default adapter
if (!config._ctx.adapter) { if (!config._ctx.adapter && config.mode === 'server') {
const integration = ssgAdapter(); config._ctx.adapter = getGenericNodeAdapter();
config.integrations.push(integration); }
if (integration?.hooks?.['astro:config:done']) {
await integration.hooks['astro:config:done']({
config,
setAdapter(adapter) {
config._ctx.adapter = adapter;
},
});
}
}
} }
export async function runHookServerSetup({ export async function runHookServerSetup({
config, config,
server, server,
}: { }: {
@ -209,7 +200,7 @@ export async function runHookBuildDone({
pages: string[]; pages: string[];
routes: RouteData[]; routes: RouteData[];
}) { }) {
const dir = isBuildingToSSR(config) ? buildConfig.client : config.outDir; const dir = config.mode === 'server' ? buildConfig.client : config.outDir;
for (const integration of config.integrations) { for (const integration of config.integrations) {
if (integration?.hooks?.['astro:build:done']) { if (integration?.hooks?.['astro:build:done']) {

View file

@ -15,7 +15,7 @@ import { preload, ssr } from '../core/render/dev/index.js';
import { RouteCache } from '../core/render/route-cache.js'; import { RouteCache } from '../core/render/route-cache.js';
import { createRequest } from '../core/request.js'; import { createRequest } from '../core/request.js';
import { createRouteManifest, matchRoute } from '../core/routing/index.js'; import { createRouteManifest, matchRoute } from '../core/routing/index.js';
import { createSafeError, isBuildingToSSR, resolvePages } from '../core/util.js'; import { createSafeError, resolvePages } from '../core/util.js';
import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.js'; import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.js';
import serverErrorTemplate from '../template/5xx.js'; import serverErrorTemplate from '../template/5xx.js';
@ -188,7 +188,7 @@ async function handleRequest(
const site = config.site ? new URL(config.base, config.site) : undefined; const site = config.site ? new URL(config.base, config.site) : undefined;
const devRoot = site ? site.pathname : '/'; const devRoot = site ? site.pathname : '/';
const origin = `${viteServer.config.server.https ? 'https' : 'http'}://${req.headers.host}`; const origin = `${viteServer.config.server.https ? 'https' : 'http'}://${req.headers.host}`;
const buildingToSSR = isBuildingToSSR(config); const buildingToSSR = config.mode === 'server';
// Ignore `.html` extensions and `index.html` in request URLS to ensure that // Ignore `.html` extensions and `index.html` in request URLS to ensure that
// routing behavior matches production builds. This supports both file and directory // routing behavior matches production builds. This supports both file and directory
// build formats, and is necessary based on how the manifest tracks build targets. // build formats, and is necessary based on how the manifest tracks build targets.
@ -257,7 +257,7 @@ async function handleRequest(
routeCache, routeCache,
pathname: rootRelativeUrl, pathname: rootRelativeUrl,
logging, logging,
ssr: isBuildingToSSR(config), ssr: config.mode === 'server',
}); });
if (paramsAndPropsRes === GetParamsAndPropsError.NoMatchingStaticPath) { if (paramsAndPropsRes === GetParamsAndPropsError.NoMatchingStaticPath) {
warn( warn(

View file

@ -30,8 +30,6 @@ export default function vercelStatic(): AstroIntegration {
); );
} }
buildConfig.staticMode = true;
// Ensure to have `.vercel/output` empty. // Ensure to have `.vercel/output` empty.
// This is because, when building to static, outDir = .vercel/output/static/, // This is because, when building to static, outDir = .vercel/output/static/,
// so .vercel/output itself won't get cleaned. // so .vercel/output itself won't get cleaned.

View file

@ -1,5 +1,5 @@
export { pathToPosix } from './lib/utils'; export { pathToPosix } from './lib/utils';
export { AbortController, AbortSignal, alert, atob, Blob, btoa, ByteLengthQueuingStrategy, cancelAnimationFrame, cancelIdleCallback, CanvasRenderingContext2D, CharacterData, clearTimeout, Comment, CountQueuingStrategy, CSSStyleSheet, CustomElementRegistry, CustomEvent, Document, DocumentFragment, DOMException, Element, Event, EventTarget, fetch, File, FormData, Headers, HTMLBodyElement, HTMLCanvasElement, HTMLDivElement, HTMLDocument, HTMLElement, HTMLHeadElement, HTMLHtmlElement, HTMLImageElement, HTMLSpanElement, HTMLStyleElement, HTMLTemplateElement, HTMLUnknownElement, Image, ImageData, IntersectionObserver, MediaQueryList, MutationObserver, Node, NodeFilter, NodeIterator, OffscreenCanvas, ReadableByteStreamController, ReadableStream, ReadableStreamBYOBReader, ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, Request, requestAnimationFrame, requestIdleCallback, ResizeObserver, Response, setTimeout, ShadowRoot, structuredClone, StyleSheet, Text, TransformStream, TreeWalker, URLPattern, Window, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter } from './mod.js'; export { AbortController, AbortSignal, alert, atob, Blob, btoa, ByteLengthQueuingStrategy, cancelAnimationFrame, cancelIdleCallback, CanvasRenderingContext2D, CharacterData, clearTimeout, Comment, CountQueuingStrategy, CSSStyleSheet, CustomElementRegistry, CustomEvent, Document, DocumentFragment, DOMException, Element, Event, EventTarget, fetch, File, FormData, Headers, HTMLBodyElement, HTMLCanvasElement, HTMLDivElement, HTMLDocument, HTMLElement, HTMLHeadElement, HTMLHtmlElement, HTMLImageElement, HTMLSpanElement, HTMLStyleElement, HTMLTemplateElement, HTMLUnknownElement, Image, ImageData, IntersectionObserver, MediaQueryList, MutationObserver, Node, NodeFilter, NodeIterator, OffscreenCanvas, ReadableByteStreamController, ReadableStream, ReadableStreamBYOBReader, ReadableStreamBYOBRequest, ReadableStreamDefaultController, ReadableStreamDefaultReader, Request, requestAnimationFrame, requestIdleCallback, ResizeObserver, Response, setTimeout, ShadowRoot, structuredClone, StyleSheet, Text, TransformStream, TreeWalker, URLPattern, Window, WritableStream, WritableStreamDefaultController, WritableStreamDefaultWriter, } from './mod.js';
export declare const polyfill: { export declare const polyfill: {
(target: any, options?: PolyfillOptions): any; (target: any, options?: PolyfillOptions): any;
internals(target: any, name: string): any; internals(target: any, name: string): any;

14890
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff