Refactor to remove AstroConfig['_ctx'] (#4771)
* Refactor to remove AstroConfig['_ctx'] * Fix type error * Export validateConfig * Move to an options bag for createSettings * Move config tests into test/untils/config * Add a changeste * fix build
This commit is contained in:
parent
48567639e0
commit
f3a81d82f6
44 changed files with 776 additions and 744 deletions
5
.changeset/new-llamas-wash.md
Normal file
5
.changeset/new-llamas-wash.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Internal refactor
|
|
@ -11,7 +11,7 @@ import type * as babel from '@babel/core';
|
|||
import type { AddressInfo } from 'net';
|
||||
import type { TsConfigJson } from 'tsconfig-resolver';
|
||||
import type * as vite from 'vite';
|
||||
import { z } from 'zod';
|
||||
import type { z } from 'zod';
|
||||
import type { SerializedSSRManifest } from '../core/app/types';
|
||||
import type { PageBuildData } from '../core/build/types';
|
||||
import type { AstroConfigSchema } from '../core/config';
|
||||
|
@ -871,20 +871,21 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
|
|||
// This is a more detailed type than zod validation gives us.
|
||||
// TypeScript still confirms zod validation matches this type.
|
||||
integrations: AstroIntegration[];
|
||||
}
|
||||
|
||||
// Private:
|
||||
// We have a need to pass context based on configured state,
|
||||
// that is different from the user-exposed configuration.
|
||||
// TODO: Create an AstroConfig class to manage this, long-term.
|
||||
_ctx: {
|
||||
export interface AstroSettings {
|
||||
config: AstroConfig;
|
||||
|
||||
adapter: AstroAdapter | undefined;
|
||||
injectedRoutes: InjectedRoute[];
|
||||
pageExtensions: string[];
|
||||
renderers: AstroRenderer[];
|
||||
scripts: {
|
||||
stage: InjectedScriptStage;
|
||||
content: string
|
||||
}[];
|
||||
tsConfig: TsConfigJson | undefined;
|
||||
tsConfigPath: string | undefined;
|
||||
pageExtensions: string[];
|
||||
injectedRoutes: InjectedRoute[];
|
||||
adapter: AstroAdapter | undefined;
|
||||
renderers: AstroRenderer[];
|
||||
scripts: { stage: InjectedScriptStage; content: string }[];
|
||||
};
|
||||
}
|
||||
|
||||
export type AsyncRendererComponentFn<U> = (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable no-console */
|
||||
import { AstroCheck, DiagnosticSeverity } from '@astrojs/language-server';
|
||||
import type { AstroConfig } from '../../@types/astro';
|
||||
import type { AstroSettings } from '../../@types/astro';
|
||||
|
||||
import glob from 'fast-glob';
|
||||
import * as fs from 'fs';
|
||||
|
@ -16,10 +16,10 @@ interface Result {
|
|||
hints: number;
|
||||
}
|
||||
|
||||
export async function check(astroConfig: AstroConfig) {
|
||||
export async function check(settings: AstroSettings) {
|
||||
console.log(bold('astro check'));
|
||||
|
||||
const root = astroConfig.root;
|
||||
const root = settings.config.root;
|
||||
|
||||
const spinner = ora(` Getting diagnostics for Astro files in ${fileURLToPath(root)}…`).start();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import yargs from 'yargs-parser';
|
|||
import { z } from 'zod';
|
||||
import add from '../core/add/index.js';
|
||||
import build from '../core/build/index.js';
|
||||
import { openConfig, resolveConfigPath, resolveFlags, resolveRoot } from '../core/config.js';
|
||||
import { openConfig, resolveConfigPath, resolveFlags, resolveRoot, createSettings, loadTSConfig } from '../core/config/index.js';
|
||||
import devServer from '../core/dev/index.js';
|
||||
import { collectErrorMetadata } from '../core/errors.js';
|
||||
import { debug, error, info, LogOptions } from '../core/logger/core.js';
|
||||
|
@ -150,7 +150,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
}
|
||||
}
|
||||
|
||||
let { astroConfig, userConfig } = await openConfig({
|
||||
let { astroConfig: initialAstroConfig, userConfig: initialUserConfig } = await openConfig({
|
||||
cwd: root,
|
||||
flags,
|
||||
cmd,
|
||||
|
@ -159,8 +159,14 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
await handleConfigError(e, { cwd: root, flags, logging });
|
||||
return {} as any;
|
||||
});
|
||||
if (!astroConfig) return;
|
||||
telemetry.record(event.eventCliSession(cmd, userConfig, flags));
|
||||
if (!initialAstroConfig) return;
|
||||
telemetry.record(event.eventCliSession(cmd, initialUserConfig, flags));
|
||||
let initialTsConfig = loadTSConfig(root);
|
||||
let settings = createSettings({
|
||||
config: initialAstroConfig,
|
||||
tsConfig: initialTsConfig?.config,
|
||||
tsConfigPath: initialTsConfig?.path,
|
||||
});
|
||||
|
||||
// Common CLI Commands:
|
||||
// These commands run normally. All commands are assumed to have been handled
|
||||
|
@ -168,7 +174,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
switch (cmd) {
|
||||
case 'dev': {
|
||||
async function startDevServer({ isRestart = false }: { isRestart?: boolean } = {}) {
|
||||
const { watcher, stop } = await devServer(astroConfig, { logging, telemetry, isRestart });
|
||||
const { watcher, stop } = await devServer(settings, { logging, telemetry, isRestart });
|
||||
let restartInFlight = false;
|
||||
const configFlag = resolveFlags(flags).config;
|
||||
const configFlagPath = configFlag
|
||||
|
@ -199,7 +205,13 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
isConfigReload: true,
|
||||
});
|
||||
info(logging, 'astro', logMsg + '\n');
|
||||
astroConfig = newConfig.astroConfig;
|
||||
let astroConfig = newConfig.astroConfig;
|
||||
let tsconfig = loadTSConfig(root);
|
||||
settings = createSettings({
|
||||
config: astroConfig,
|
||||
tsConfig: tsconfig?.config,
|
||||
tsConfigPath: tsconfig?.path
|
||||
});
|
||||
await stop();
|
||||
await startDevServer({ isRestart: true });
|
||||
} catch (e) {
|
||||
|
@ -220,16 +232,16 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
}
|
||||
|
||||
case 'build': {
|
||||
return await build(astroConfig, { logging, telemetry });
|
||||
return await build(settings, { logging, telemetry });
|
||||
}
|
||||
|
||||
case 'check': {
|
||||
const ret = await check(astroConfig);
|
||||
const ret = await check(settings);
|
||||
return process.exit(ret);
|
||||
}
|
||||
|
||||
case 'preview': {
|
||||
const server = await preview(astroConfig, { logging, telemetry });
|
||||
const server = await preview(settings, { logging, telemetry });
|
||||
return await server.closed(); // keep alive until the server is closed
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import preferredPM from 'preferred-pm';
|
|||
import prompts from 'prompts';
|
||||
import { fileURLToPath, pathToFileURL } from 'url';
|
||||
import type yargs from 'yargs-parser';
|
||||
import { resolveConfigPath } from '../config.js';
|
||||
import { resolveConfigPath } from '../config/index.js';
|
||||
import { debug, info, LogOptions } from '../logger/core.js';
|
||||
import * as msg from '../messages.js';
|
||||
import { printHelp } from '../messages.js';
|
||||
|
|
|
@ -6,6 +6,7 @@ import type { OutputAsset, OutputChunk } from 'rollup';
|
|||
import { fileURLToPath } from 'url';
|
||||
import type {
|
||||
AstroConfig,
|
||||
AstroSettings,
|
||||
ComponentInstance,
|
||||
EndpointHandler,
|
||||
RouteType,
|
||||
|
@ -62,10 +63,10 @@ function* throttle(max: number, inPaths: string[]) {
|
|||
}
|
||||
}
|
||||
|
||||
function shouldSkipDraft(pageModule: ComponentInstance, astroConfig: AstroConfig): boolean {
|
||||
function shouldSkipDraft(pageModule: ComponentInstance, settings: AstroSettings): boolean {
|
||||
return (
|
||||
// Drafts are disabled
|
||||
!astroConfig.markdown.drafts &&
|
||||
!settings.config.markdown.drafts &&
|
||||
// This is a draft post
|
||||
'frontmatter' in pageModule &&
|
||||
(pageModule as any).frontmatter?.draft === true
|
||||
|
@ -74,13 +75,13 @@ function shouldSkipDraft(pageModule: ComponentInstance, astroConfig: AstroConfig
|
|||
|
||||
// Gives back a facadeId that is relative to the root.
|
||||
// ie, src/pages/index.astro instead of /Users/name..../src/pages/index.astro
|
||||
export function rootRelativeFacadeId(facadeId: string, astroConfig: AstroConfig): string {
|
||||
return facadeId.slice(fileURLToPath(astroConfig.root).length);
|
||||
export function rootRelativeFacadeId(facadeId: string, settings: AstroSettings): string {
|
||||
return facadeId.slice(fileURLToPath(settings.config.root).length);
|
||||
}
|
||||
|
||||
// Determines of a Rollup chunk is an entrypoint page.
|
||||
export function chunkIsPage(
|
||||
astroConfig: AstroConfig,
|
||||
settings: AstroSettings,
|
||||
output: OutputAsset | OutputChunk,
|
||||
internals: BuildInternals
|
||||
) {
|
||||
|
@ -90,7 +91,7 @@ export function chunkIsPage(
|
|||
const chunk = output as OutputChunk;
|
||||
if (chunk.facadeModuleId) {
|
||||
const facadeToEntryId = prependForwardSlash(
|
||||
rootRelativeFacadeId(chunk.facadeModuleId, astroConfig)
|
||||
rootRelativeFacadeId(chunk.facadeModuleId, settings)
|
||||
);
|
||||
return internals.entrySpecifierToBundleMap.has(facadeToEntryId);
|
||||
}
|
||||
|
@ -101,9 +102,9 @@ export async function generatePages(opts: StaticBuildOptions, internals: BuildIn
|
|||
const timer = performance.now();
|
||||
info(opts.logging, null, `\n${bgGreen(black(' generating static routes '))}`);
|
||||
|
||||
const ssr = opts.astroConfig.output === 'server';
|
||||
const ssr = opts.settings.config.output === 'server';
|
||||
const serverEntry = opts.buildConfig.serverEntry;
|
||||
const outFolder = ssr ? opts.buildConfig.server : getOutDirWithinCwd(opts.astroConfig.outDir);
|
||||
const outFolder = ssr ? opts.buildConfig.server : getOutDirWithinCwd(opts.settings.config.outDir);
|
||||
const ssrEntryURL = new URL('./' + serverEntry + `?time=${Date.now()}`, outFolder);
|
||||
const ssrEntry = await import(ssrEntryURL.toString());
|
||||
const builtPaths = new Set<string>();
|
||||
|
@ -137,7 +138,7 @@ async function generatePage(
|
|||
);
|
||||
}
|
||||
|
||||
if (shouldSkipDraft(pageModule, opts.astroConfig)) {
|
||||
if (shouldSkipDraft(pageModule, opts.settings)) {
|
||||
info(opts.logging, null, `${magenta('⚠️')} Skipping draft ${pageData.route.component}`);
|
||||
return;
|
||||
}
|
||||
|
@ -163,7 +164,7 @@ async function generatePage(
|
|||
const timeEnd = performance.now();
|
||||
const timeChange = getTimeStat(timeStart, timeEnd);
|
||||
const timeIncrease = `(+${timeChange})`;
|
||||
const filePath = getOutputFilename(opts.astroConfig, path, pageData.route.type);
|
||||
const filePath = getOutputFilename(opts.settings.config, path, pageData.route.type);
|
||||
const lineIcon = i === paths.length - 1 ? '└─' : '├─';
|
||||
info(opts.logging, null, ` ${cyan(lineIcon)} ${dim(filePath)} ${dim(timeIncrease)}`);
|
||||
}
|
||||
|
@ -186,7 +187,7 @@ async function getPathsForRoute(
|
|||
route: pageData.route,
|
||||
isValidate: false,
|
||||
logging: opts.logging,
|
||||
ssr: opts.astroConfig.output === 'server',
|
||||
ssr: opts.settings.config.output === 'server',
|
||||
})
|
||||
.then((_result) => {
|
||||
const label = _result.staticPaths.length === 1 ? 'page' : 'pages';
|
||||
|
@ -262,8 +263,8 @@ function shouldAppendForwardSlash(
|
|||
}
|
||||
|
||||
function addPageName(pathname: string, opts: StaticBuildOptions): void {
|
||||
const trailingSlash = opts.astroConfig.trailingSlash;
|
||||
const buildFormat = opts.astroConfig.build.format;
|
||||
const trailingSlash = opts.settings.config.trailingSlash;
|
||||
const buildFormat = opts.settings.config.build.format;
|
||||
const pageName = shouldAppendForwardSlash(trailingSlash, buildFormat)
|
||||
? pathname.replace(/\/?$/, '/').replace(/^\//, '')
|
||||
: pathname.replace(/^\//, '');
|
||||
|
@ -303,7 +304,7 @@ async function generatePath(
|
|||
opts: StaticBuildOptions,
|
||||
gopts: GeneratePathOptions
|
||||
) {
|
||||
const { astroConfig, logging, origin, routeCache } = opts;
|
||||
const { settings, logging, origin, routeCache } = opts;
|
||||
const { mod, internals, linkIds, scripts: hoistedScripts, pageData, renderers } = gopts;
|
||||
|
||||
// This adds the page name to the array so it can be shown as part of stats.
|
||||
|
@ -316,18 +317,18 @@ async function generatePath(
|
|||
// If a base path was provided, append it to the site URL. This ensures that
|
||||
// all injected scripts and links are referenced relative to the site and subpath.
|
||||
const site =
|
||||
astroConfig.base !== '/'
|
||||
? joinPaths(astroConfig.site?.toString() || 'http://localhost/', astroConfig.base)
|
||||
: astroConfig.site;
|
||||
settings.config.base !== '/'
|
||||
? joinPaths(settings.config.site?.toString() || 'http://localhost/', settings.config.base)
|
||||
: settings.config.site;
|
||||
const links = createLinkStylesheetElementSet(linkIds, site);
|
||||
const scripts = createModuleScriptsSet(hoistedScripts ? [hoistedScripts] : [], site);
|
||||
|
||||
if (astroConfig._ctx.scripts.some((script) => script.stage === 'page')) {
|
||||
if (settings.scripts.some((script) => script.stage === 'page')) {
|
||||
const hashedFilePath = internals.entrySpecifierToBundleMap.get(PAGE_SCRIPT_ID);
|
||||
if (typeof hashedFilePath !== 'string') {
|
||||
throw new Error(`Cannot find the built path for ${PAGE_SCRIPT_ID}`);
|
||||
}
|
||||
const src = prependForwardSlash(npath.posix.join(astroConfig.base, hashedFilePath));
|
||||
const src = prependForwardSlash(npath.posix.join(settings.config.base, hashedFilePath));
|
||||
scripts.add({
|
||||
props: { type: 'module', src },
|
||||
children: '',
|
||||
|
@ -335,7 +336,7 @@ async function generatePath(
|
|||
}
|
||||
|
||||
// Add all injected scripts to the page.
|
||||
for (const script of astroConfig._ctx.scripts) {
|
||||
for (const script of settings.scripts) {
|
||||
if (script.stage === 'head-inline') {
|
||||
scripts.add({
|
||||
props: {},
|
||||
|
@ -344,12 +345,12 @@ async function generatePath(
|
|||
}
|
||||
}
|
||||
|
||||
const ssr = opts.astroConfig.output === 'server';
|
||||
const ssr = settings.config.output === 'server';
|
||||
const url = getUrlForPath(
|
||||
pathname,
|
||||
opts.astroConfig.base,
|
||||
opts.settings.config.base,
|
||||
origin,
|
||||
opts.astroConfig.build.format,
|
||||
opts.settings.config.build.format,
|
||||
pageData.route.type
|
||||
);
|
||||
const options: RenderOptions = {
|
||||
|
@ -357,8 +358,8 @@ async function generatePath(
|
|||
links,
|
||||
logging,
|
||||
markdown: {
|
||||
...astroConfig.markdown,
|
||||
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
|
||||
...settings.config.markdown,
|
||||
isAstroFlavoredMd: settings.config.legacy.astroFlavoredMarkdown,
|
||||
},
|
||||
mod,
|
||||
mode: opts.mode,
|
||||
|
@ -376,14 +377,14 @@ async function generatePath(
|
|||
}
|
||||
throw new Error(`Cannot find the built path for ${specifier}`);
|
||||
}
|
||||
return prependForwardSlash(npath.posix.join(astroConfig.base, hashedFilePath));
|
||||
return prependForwardSlash(npath.posix.join(settings.config.base, hashedFilePath));
|
||||
},
|
||||
request: createRequest({ url, headers: new Headers(), logging, ssr }),
|
||||
route: pageData.route,
|
||||
routeCache,
|
||||
site: astroConfig.site
|
||||
? new URL(astroConfig.base, astroConfig.site).toString()
|
||||
: astroConfig.site,
|
||||
site: settings.config.site
|
||||
? new URL(settings.config.base, settings.config.site).toString()
|
||||
: settings.config.site,
|
||||
ssr,
|
||||
streaming: true,
|
||||
};
|
||||
|
@ -409,8 +410,8 @@ async function generatePath(
|
|||
body = await response.text();
|
||||
}
|
||||
|
||||
const outFolder = getOutFolder(astroConfig, pathname, pageData.route.type);
|
||||
const outFile = getOutFile(astroConfig, outFolder, pathname, pageData.route.type);
|
||||
const outFolder = getOutFolder(settings.config, pathname, pageData.route.type);
|
||||
const outFile = getOutFile(settings.config, outFolder, pathname, pageData.route.type);
|
||||
pageData.route.distURL = outFile;
|
||||
await fs.promises.mkdir(outFolder, { recursive: true });
|
||||
await fs.promises.writeFile(outFile, body, encoding ?? 'utf-8');
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { AstroTelemetry } from '@astrojs/telemetry';
|
||||
import type { AstroConfig, BuildConfig, ManifestData, RuntimeMode } from '../../@types/astro';
|
||||
import type { AstroSettings, BuildConfig, ManifestData, RuntimeMode } from '../../@types/astro';
|
||||
import type { LogOptions } from '../logger/core';
|
||||
|
||||
import fs from 'fs';
|
||||
|
@ -28,14 +28,14 @@ export interface BuildOptions {
|
|||
}
|
||||
|
||||
/** `astro build` */
|
||||
export default async function build(config: AstroConfig, options: BuildOptions): Promise<void> {
|
||||
export default async function build(settings: AstroSettings, options: BuildOptions): Promise<void> {
|
||||
applyPolyfill();
|
||||
const builder = new AstroBuilder(config, options);
|
||||
const builder = new AstroBuilder(settings, options);
|
||||
await builder.run();
|
||||
}
|
||||
|
||||
class AstroBuilder {
|
||||
private config: AstroConfig;
|
||||
private settings: AstroSettings;
|
||||
private logging: LogOptions;
|
||||
private mode: RuntimeMode = 'production';
|
||||
private origin: string;
|
||||
|
@ -43,16 +43,16 @@ class AstroBuilder {
|
|||
private manifest: ManifestData;
|
||||
private timer: Record<string, number>;
|
||||
|
||||
constructor(config: AstroConfig, options: BuildOptions) {
|
||||
constructor(settings: AstroSettings, options: BuildOptions) {
|
||||
if (options.mode) {
|
||||
this.mode = options.mode;
|
||||
}
|
||||
this.config = config;
|
||||
this.settings = settings;
|
||||
this.logging = options.logging;
|
||||
this.routeCache = new RouteCache(this.logging);
|
||||
this.origin = config.site
|
||||
? new URL(config.site).origin
|
||||
: `http://localhost:${config.server.port}`;
|
||||
this.origin = settings.config.site
|
||||
? new URL(settings.config.site).origin
|
||||
: `http://localhost:${settings.config.server.port}`;
|
||||
this.manifest = { routes: [] };
|
||||
this.timer = {};
|
||||
}
|
||||
|
@ -62,8 +62,8 @@ class AstroBuilder {
|
|||
debug('build', 'Initial setup...');
|
||||
const { logging } = this;
|
||||
this.timer.init = performance.now();
|
||||
this.config = await runHookConfigSetup({ config: this.config, command: 'build', logging });
|
||||
this.manifest = createRouteManifest({ config: this.config }, this.logging);
|
||||
this.settings = await runHookConfigSetup({ settings: this.settings, command: 'build', logging });
|
||||
this.manifest = createRouteManifest({ settings: this.settings }, this.logging);
|
||||
|
||||
const viteConfig = await createVite(
|
||||
{
|
||||
|
@ -73,29 +73,29 @@ class AstroBuilder {
|
|||
middlewareMode: true,
|
||||
},
|
||||
},
|
||||
{ astroConfig: this.config, logging, mode: 'build' }
|
||||
{ settings: this.settings, logging, mode: 'build' }
|
||||
);
|
||||
await runHookConfigDone({ config: this.config, logging });
|
||||
await runHookConfigDone({ settings: this.settings, logging });
|
||||
return { viteConfig };
|
||||
}
|
||||
|
||||
/** Run the build logic. build() is marked private because usage should go through ".run()" */
|
||||
private async build({ viteConfig }: { viteConfig: ViteConfigWithSSR }) {
|
||||
const buildConfig: BuildConfig = {
|
||||
client: new URL('./client/', this.config.outDir),
|
||||
server: new URL('./server/', this.config.outDir),
|
||||
client: new URL('./client/', this.settings.config.outDir),
|
||||
server: new URL('./server/', this.settings.config.outDir),
|
||||
serverEntry: 'entry.mjs',
|
||||
};
|
||||
await runHookBuildStart({ config: this.config, buildConfig, logging: this.logging });
|
||||
await runHookBuildStart({ config: this.settings.config, buildConfig, logging: this.logging });
|
||||
|
||||
info(this.logging, 'build', `output target: ${colors.green(this.config.output)}`);
|
||||
if (this.config._ctx.adapter) {
|
||||
info(this.logging, 'build', `deploy adapter: ${colors.green(this.config._ctx.adapter.name)}`);
|
||||
info(this.logging, 'build', `output target: ${colors.green(this.settings.config.output)}`);
|
||||
if (this.settings.adapter) {
|
||||
info(this.logging, 'build', `deploy adapter: ${colors.green(this.settings.adapter.name)}`);
|
||||
}
|
||||
info(this.logging, 'build', 'Collecting build info...');
|
||||
this.timer.loadStart = performance.now();
|
||||
const { assets, allPages } = await collectPagesData({
|
||||
astroConfig: this.config,
|
||||
settings: this.settings,
|
||||
logging: this.logging,
|
||||
manifest: this.manifest,
|
||||
});
|
||||
|
@ -116,7 +116,7 @@ class AstroBuilder {
|
|||
|
||||
await staticBuild({
|
||||
allPages,
|
||||
astroConfig: this.config,
|
||||
settings: this.settings,
|
||||
logging: this.logging,
|
||||
manifest: this.manifest,
|
||||
mode: this.mode,
|
||||
|
@ -140,7 +140,7 @@ class AstroBuilder {
|
|||
|
||||
// You're done! Time to clean up.
|
||||
await runHookBuildDone({
|
||||
config: this.config,
|
||||
config: this.settings.config,
|
||||
buildConfig,
|
||||
pages: pageNames,
|
||||
routes: Object.values(allPages).map((pd) => pd.route),
|
||||
|
@ -152,7 +152,7 @@ class AstroBuilder {
|
|||
logging: this.logging,
|
||||
timeStart: this.timer.init,
|
||||
pageCount: pageNames.length,
|
||||
buildMode: this.config.output,
|
||||
buildMode: this.settings.config.output,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { AstroConfig, ManifestData } from '../../@types/astro';
|
||||
import type { AstroSettings, ManifestData } from '../../@types/astro';
|
||||
import type { LogOptions } from '../logger/core';
|
||||
import { info } from '../logger/core.js';
|
||||
import type { AllPagesData } from './types';
|
||||
|
@ -7,7 +7,7 @@ import * as colors from 'kleur/colors';
|
|||
import { debug } from '../logger/core.js';
|
||||
|
||||
export interface CollectPagesDataOptions {
|
||||
astroConfig: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
manifest: ManifestData;
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ export interface CollectPagesDataResult {
|
|||
export async function collectPagesData(
|
||||
opts: CollectPagesDataOptions
|
||||
): Promise<CollectPagesDataResult> {
|
||||
const { astroConfig, manifest } = opts;
|
||||
const { settings, manifest } = opts;
|
||||
|
||||
const assets: Record<string, string> = {};
|
||||
const allPages: AllPagesData = {};
|
||||
|
@ -58,7 +58,7 @@ export async function collectPagesData(
|
|||
};
|
||||
|
||||
clearInterval(routeCollectionLogTimeout);
|
||||
if (astroConfig.output === 'static') {
|
||||
if (settings.config.output === 'static') {
|
||||
const html = `${route.pathname}`.replace(/\/?$/, '/index.html');
|
||||
debug(
|
||||
'build',
|
||||
|
|
|
@ -23,10 +23,10 @@ import { vitePluginPages } from './vite-plugin-pages.js';
|
|||
import { injectManifest, vitePluginSSR } from './vite-plugin-ssr.js';
|
||||
|
||||
export async function staticBuild(opts: StaticBuildOptions) {
|
||||
const { allPages, astroConfig } = opts;
|
||||
const { allPages, settings } = opts;
|
||||
|
||||
// Verify this app is buildable.
|
||||
if (isModeServerWithNoAdapter(opts.astroConfig)) {
|
||||
if (isModeServerWithNoAdapter(opts.settings)) {
|
||||
throw new Error(`Cannot use \`output: 'server'\` without an adapter.
|
||||
Install and configure the appropriate server adapter for your final deployment.
|
||||
Learn more: https://docs.astro.build/en/guides/server-side-rendering/
|
||||
|
@ -55,7 +55,7 @@ Learn more: https://docs.astro.build/en/guides/server-side-rendering/
|
|||
timer.buildStart = performance.now();
|
||||
|
||||
for (const [component, pageData] of Object.entries(allPages)) {
|
||||
const astroModuleURL = new URL('./' + component, astroConfig.root);
|
||||
const astroModuleURL = new URL('./' + component, settings.config.root);
|
||||
const astroModuleId = prependForwardSlash(component);
|
||||
|
||||
// Track the page data in internals
|
||||
|
@ -68,15 +68,15 @@ Learn more: https://docs.astro.build/en/guides/server-side-rendering/
|
|||
// Empty out the dist folder, if needed. Vite has a config for doing this
|
||||
// but because we are running 2 vite builds in parallel, that would cause a race
|
||||
// condition, so we are doing it ourselves
|
||||
emptyDir(astroConfig.outDir, new Set('.git'));
|
||||
emptyDir(settings.config.outDir, new Set('.git'));
|
||||
|
||||
// Build your project (SSR application code, assets, client JS, etc.)
|
||||
timer.ssr = performance.now();
|
||||
info(opts.logging, 'build', `Building ${astroConfig.output} entrypoints...`);
|
||||
info(opts.logging, 'build', `Building ${settings.config.output} entrypoints...`);
|
||||
await ssrBuild(opts, internals, pageInput);
|
||||
info(opts.logging, 'build', dim(`Completed in ${getTimeStat(timer.ssr, performance.now())}.`));
|
||||
|
||||
const rendererClientEntrypoints = opts.astroConfig._ctx.renderers
|
||||
const rendererClientEntrypoints = settings.renderers
|
||||
.map((r) => r.clientEntrypoint)
|
||||
.filter((a) => typeof a === 'string') as string[];
|
||||
|
||||
|
@ -87,7 +87,7 @@ Learn more: https://docs.astro.build/en/guides/server-side-rendering/
|
|||
...internals.discoveredScripts,
|
||||
]);
|
||||
|
||||
if (astroConfig._ctx.scripts.some((script) => script.stage === 'page')) {
|
||||
if (settings.scripts.some((script) => script.stage === 'page')) {
|
||||
clientInput.add(PAGE_SCRIPT_ID);
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ Learn more: https://docs.astro.build/en/guides/server-side-rendering/
|
|||
await clientBuild(opts, internals, clientInput);
|
||||
|
||||
timer.generate = performance.now();
|
||||
if (astroConfig.output === 'static') {
|
||||
if (settings.config.output === 'static') {
|
||||
await generatePages(opts, internals);
|
||||
await cleanSsrOutput(opts);
|
||||
} else {
|
||||
|
@ -109,9 +109,9 @@ Learn more: https://docs.astro.build/en/guides/server-side-rendering/
|
|||
}
|
||||
|
||||
async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, input: Set<string>) {
|
||||
const { astroConfig, viteConfig } = opts;
|
||||
const ssr = astroConfig.output === 'server';
|
||||
const out = ssr ? opts.buildConfig.server : getOutDirWithinCwd(astroConfig.outDir);
|
||||
const { settings, viteConfig } = opts;
|
||||
const ssr = settings.config.output === 'server';
|
||||
const out = ssr ? opts.buildConfig.server : getOutDirWithinCwd(settings.config.outDir);
|
||||
|
||||
const viteBuildConfig: ViteConfigWithSSR = {
|
||||
...viteConfig,
|
||||
|
@ -148,21 +148,20 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
|
|||
buildOptions: opts,
|
||||
internals,
|
||||
target: 'server',
|
||||
astroConfig,
|
||||
}),
|
||||
...(viteConfig.plugins || []),
|
||||
// SSR needs to be last
|
||||
opts.astroConfig.output === 'server' &&
|
||||
vitePluginSSR(internals, opts.astroConfig._ctx.adapter!),
|
||||
settings.config.output === 'server' &&
|
||||
vitePluginSSR(internals, settings.adapter!),
|
||||
vitePluginAnalyzer(internals),
|
||||
],
|
||||
publicDir: ssr ? false : viteConfig.publicDir,
|
||||
envPrefix: 'PUBLIC_',
|
||||
base: astroConfig.base,
|
||||
base: settings.config.base,
|
||||
};
|
||||
|
||||
await runHookBuildSetup({
|
||||
config: astroConfig,
|
||||
config: settings.config,
|
||||
pages: internals.pagesByComponent,
|
||||
vite: viteBuildConfig,
|
||||
target: 'server',
|
||||
|
@ -177,16 +176,16 @@ async function clientBuild(
|
|||
internals: BuildInternals,
|
||||
input: Set<string>
|
||||
) {
|
||||
const { astroConfig, viteConfig } = opts;
|
||||
const { settings, viteConfig } = opts;
|
||||
const timer = performance.now();
|
||||
const ssr = astroConfig.output === 'server';
|
||||
const out = ssr ? opts.buildConfig.client : astroConfig.outDir;
|
||||
const ssr = settings.config.output === 'server';
|
||||
const out = ssr ? opts.buildConfig.client : settings.config.outDir;
|
||||
|
||||
// Nothing to do if there is no client-side JS.
|
||||
if (!input.size) {
|
||||
// If SSR, copy public over
|
||||
if (ssr) {
|
||||
await copyFiles(astroConfig.publicDir, out);
|
||||
await copyFiles(settings.config.publicDir, out);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -219,21 +218,20 @@ async function clientBuild(
|
|||
},
|
||||
plugins: [
|
||||
vitePluginInternals(input, internals),
|
||||
vitePluginHoistedScripts(astroConfig, internals),
|
||||
vitePluginHoistedScripts(settings, internals),
|
||||
rollupPluginAstroBuildCSS({
|
||||
buildOptions: opts,
|
||||
internals,
|
||||
target: 'client',
|
||||
astroConfig,
|
||||
}),
|
||||
...(viteConfig.plugins || []),
|
||||
],
|
||||
envPrefix: 'PUBLIC_',
|
||||
base: astroConfig.base,
|
||||
base: settings.config.base,
|
||||
} as ViteConfigWithSSR;
|
||||
|
||||
await runHookBuildSetup({
|
||||
config: astroConfig,
|
||||
config: settings.config,
|
||||
pages: internals.pagesByComponent,
|
||||
vite: viteBuildConfig,
|
||||
target: 'client',
|
||||
|
@ -246,9 +244,9 @@ async function clientBuild(
|
|||
}
|
||||
|
||||
async function cleanSsrOutput(opts: StaticBuildOptions) {
|
||||
const out = getOutDirWithinCwd(opts.astroConfig.outDir);
|
||||
const out = getOutDirWithinCwd(opts.settings.config.outDir);
|
||||
// Clean out directly if the outDir is outside of root
|
||||
if (out.toString() !== opts.astroConfig.outDir.toString()) {
|
||||
if (out.toString() !== opts.settings.config.outDir.toString()) {
|
||||
await fs.promises.rm(out, { recursive: true });
|
||||
return;
|
||||
}
|
||||
|
@ -284,7 +282,7 @@ async function copyFiles(fromFolder: URL, toFolder: URL) {
|
|||
async function ssrMoveAssets(opts: StaticBuildOptions) {
|
||||
info(opts.logging, 'build', 'Rearranging server assets...');
|
||||
const serverRoot =
|
||||
opts.astroConfig.output === 'static' ? opts.buildConfig.client : opts.buildConfig.server;
|
||||
opts.settings.config.output === 'static' ? opts.buildConfig.client : opts.buildConfig.server;
|
||||
const clientRoot = opts.buildConfig.client;
|
||||
const serverAssets = new URL('./assets/', serverRoot);
|
||||
const clientAssets = new URL('./assets/', clientRoot);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type {
|
||||
AstroConfig,
|
||||
AstroSettings,
|
||||
BuildConfig,
|
||||
ComponentInstance,
|
||||
ManifestData,
|
||||
|
@ -26,7 +26,7 @@ export type AllPagesData = Record<ComponentPath, PageBuildData>;
|
|||
/** Options for the static build */
|
||||
export interface StaticBuildOptions {
|
||||
allPages: AllPagesData;
|
||||
astroConfig: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
buildConfig: BuildConfig;
|
||||
logging: LogOptions;
|
||||
manifest: ManifestData;
|
||||
|
|
|
@ -22,7 +22,6 @@ interface PluginOptions {
|
|||
internals: BuildInternals;
|
||||
buildOptions: StaticBuildOptions;
|
||||
target: 'client' | 'server';
|
||||
astroConfig: AstroConfig;
|
||||
}
|
||||
|
||||
// Arbitrary magic number, can change.
|
||||
|
@ -30,13 +29,13 @@ const MAX_NAME_LENGTH = 70;
|
|||
|
||||
export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[] {
|
||||
const { internals, buildOptions } = options;
|
||||
const { astroConfig } = buildOptions;
|
||||
const { settings } = buildOptions;
|
||||
|
||||
let resolvedConfig: ResolvedConfig;
|
||||
|
||||
// Turn a page location into a name to be used for the CSS file.
|
||||
function nameifyPage(id: string) {
|
||||
let rel = relativeToSrcDir(astroConfig, id);
|
||||
let rel = relativeToSrcDir(settings.config, id);
|
||||
// Remove pages, ex. blog/posts/something.astro
|
||||
if (rel.startsWith('pages/')) {
|
||||
rel = rel.slice(6);
|
||||
|
@ -240,7 +239,7 @@ export function rollupPluginAstroBuildCSS(options: PluginOptions): VitePlugin[]
|
|||
for (const [, output] of Object.entries(bundle)) {
|
||||
if (output.type === 'asset') {
|
||||
if (output.name?.endsWith('.css') && typeof output.source === 'string') {
|
||||
const cssTarget = options.astroConfig.vite.build?.cssTarget;
|
||||
const cssTarget = settings.config.vite.build?.cssTarget;
|
||||
const { code: minifiedCSS } = await esbuild.transform(output.source, {
|
||||
loader: 'css',
|
||||
minify: true,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { Plugin as VitePlugin } from 'vite';
|
||||
import type { AstroConfig } from '../../@types/astro';
|
||||
import type { AstroSettings } from '../../@types/astro';
|
||||
import type { BuildInternals } from '../../core/build/internal.js';
|
||||
import { viteID } from '../util.js';
|
||||
import { getPageDataByViteID } from './internal.js';
|
||||
|
@ -9,7 +9,7 @@ function virtualHoistedEntry(id: string) {
|
|||
}
|
||||
|
||||
export function vitePluginHoistedScripts(
|
||||
astroConfig: AstroConfig,
|
||||
settings: AstroSettings,
|
||||
internals: BuildInternals
|
||||
): VitePlugin {
|
||||
return {
|
||||
|
@ -40,7 +40,7 @@ export function vitePluginHoistedScripts(
|
|||
},
|
||||
|
||||
async generateBundle(_options, bundle) {
|
||||
let assetInlineLimit = astroConfig.vite?.build?.assetsInlineLimit || 4096;
|
||||
let assetInlineLimit = settings.config.vite?.build?.assetsInlineLimit || 4096;
|
||||
|
||||
// Find all page entry points and create a map of the entry point to the hashed hoisted script.
|
||||
// This is used when we render so that we can add the script to the head.
|
||||
|
@ -58,7 +58,7 @@ export function vitePluginHoistedScripts(
|
|||
const facadeId = output.facadeModuleId!;
|
||||
const pages = internals.hoistedScriptIdToPagesMap.get(facadeId)!;
|
||||
for (const pathname of pages) {
|
||||
const vid = viteID(new URL('.' + pathname, astroConfig.root));
|
||||
const vid = viteID(new URL('.' + pathname, settings.config.root));
|
||||
const pageInfo = getPageDataByViteID(internals, vid);
|
||||
if (pageInfo) {
|
||||
if (canBeInlined) {
|
||||
|
|
|
@ -10,7 +10,7 @@ export function vitePluginPages(opts: StaticBuildOptions, internals: BuildIntern
|
|||
name: '@astro/plugin-build-pages',
|
||||
|
||||
options(options) {
|
||||
if (opts.astroConfig.output === 'static') {
|
||||
if (opts.settings.config.output === 'static') {
|
||||
return addRollupInput(options, [pagesVirtualModuleId]);
|
||||
}
|
||||
},
|
||||
|
@ -35,7 +35,7 @@ export function vitePluginPages(opts: StaticBuildOptions, internals: BuildIntern
|
|||
|
||||
i = 0;
|
||||
let rendererItems = '';
|
||||
for (const renderer of opts.astroConfig._ctx.renderers) {
|
||||
for (const renderer of opts.settings.renderers) {
|
||||
const variable = `_renderer${i}`;
|
||||
// Use unshift so that renderers are imported before user code, in case they set globals
|
||||
// that user code depends on.
|
||||
|
|
|
@ -103,7 +103,7 @@ export async function injectManifest(buildOpts: StaticBuildOptions, internals: B
|
|||
|
||||
const staticFiles = internals.staticFiles;
|
||||
const manifest = buildManifest(buildOpts, internals, Array.from(staticFiles));
|
||||
await runHookBuildSsr({ config: buildOpts.astroConfig, manifest, logging: buildOpts.logging });
|
||||
await runHookBuildSsr({ config: buildOpts.settings.config, manifest, logging: buildOpts.logging });
|
||||
|
||||
const chunk = internals.ssrEntryChunk;
|
||||
const code = chunk.code;
|
||||
|
@ -120,11 +120,11 @@ function buildManifest(
|
|||
internals: BuildInternals,
|
||||
staticFiles: string[]
|
||||
): SerializedSSRManifest {
|
||||
const { astroConfig } = opts;
|
||||
const { settings } = opts;
|
||||
|
||||
const routes: SerializedRouteInfo[] = [];
|
||||
const entryModules = Object.fromEntries(internals.entrySpecifierToBundleMap.entries());
|
||||
if (astroConfig._ctx.scripts.some((script) => script.stage === 'page')) {
|
||||
if (settings.scripts.some((script) => script.stage === 'page')) {
|
||||
staticFiles.push(entryModules[PAGE_SCRIPT_ID]);
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ function buildManifest(
|
|||
if (pageData.hoistedScript) {
|
||||
scripts.unshift(pageData.hoistedScript);
|
||||
}
|
||||
if (astroConfig._ctx.scripts.some((script) => script.stage === 'page')) {
|
||||
if (settings.scripts.some((script) => script.stage === 'page')) {
|
||||
scripts.push({ type: 'external', value: entryModules[PAGE_SCRIPT_ID] });
|
||||
}
|
||||
|
||||
|
@ -142,11 +142,11 @@ function buildManifest(
|
|||
links: sortedCSS(pageData),
|
||||
scripts: [
|
||||
...scripts,
|
||||
...astroConfig._ctx.scripts
|
||||
...settings.scripts
|
||||
.filter((script) => script.stage === 'head-inline')
|
||||
.map(({ stage, content }) => ({ stage, children: content })),
|
||||
],
|
||||
routeData: serializeRouteData(pageData.route, astroConfig.trailingSlash),
|
||||
routeData: serializeRouteData(pageData.route, settings.config.trailingSlash),
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -157,13 +157,13 @@ function buildManifest(
|
|||
}
|
||||
|
||||
const ssrManifest: SerializedSSRManifest = {
|
||||
adapterName: opts.astroConfig._ctx.adapter!.name,
|
||||
adapterName: opts.settings.adapter!.name,
|
||||
routes,
|
||||
site: astroConfig.site,
|
||||
base: astroConfig.base,
|
||||
site: settings.config.site,
|
||||
base: settings.config.base,
|
||||
markdown: {
|
||||
...astroConfig.markdown,
|
||||
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
|
||||
...settings.config.markdown,
|
||||
isAstroFlavoredMd: settings.config.legacy.astroFlavoredMarkdown,
|
||||
},
|
||||
pageMap: null as any,
|
||||
renderers: [],
|
||||
|
|
|
@ -1,90 +1,20 @@
|
|||
import type { RehypePlugin, RemarkPlugin, RemarkRehype } from '@astrojs/markdown-remark';
|
||||
import fs from 'fs';
|
||||
import type * as Postcss from 'postcss';
|
||||
import type { ILanguageRegistration, IThemeRegistration, Theme } from 'shiki';
|
||||
import type { Arguments as Flags } from 'yargs-parser';
|
||||
import type { AstroConfig, AstroUserConfig, CLIFlags, ViteUserConfig } from '../@types/astro';
|
||||
import type { AstroConfig, AstroUserConfig, CLIFlags } from '../../@types/astro';
|
||||
|
||||
import fs from 'fs';
|
||||
import load, { ProloadError, resolve } from '@proload/core';
|
||||
import loadTypeScript from '@proload/plugin-tsm';
|
||||
import * as colors from 'kleur/colors';
|
||||
import path from 'path';
|
||||
import postcssrc from 'postcss-load-config';
|
||||
import { BUNDLED_THEMES } from 'shiki';
|
||||
import * as tsr from 'tsconfig-resolver';
|
||||
import { fileURLToPath, pathToFileURL } from 'url';
|
||||
import * as vite from 'vite';
|
||||
import { mergeConfig as mergeViteConfig } from 'vite';
|
||||
import { z } from 'zod';
|
||||
import jsxRenderer from '../jsx/renderer.js';
|
||||
import { LogOptions } from './logger/core.js';
|
||||
import { appendForwardSlash, prependForwardSlash, trimSlashes } from './path.js';
|
||||
import { arraify, isObject } from './util.js';
|
||||
import { LogOptions } from '../logger/core.js';
|
||||
import { arraify, isObject } from '../util.js';
|
||||
import { createRelativeSchema } from './schema.js';
|
||||
|
||||
load.use([loadTypeScript]);
|
||||
|
||||
interface PostCSSConfigResult {
|
||||
options: Postcss.ProcessOptions;
|
||||
plugins: Postcss.Plugin[];
|
||||
}
|
||||
|
||||
const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
|
||||
root: '.',
|
||||
srcDir: './src',
|
||||
publicDir: './public',
|
||||
outDir: './dist',
|
||||
base: '/',
|
||||
trailingSlash: 'ignore',
|
||||
build: { format: 'directory' },
|
||||
server: {
|
||||
host: false,
|
||||
port: 3000,
|
||||
streaming: true,
|
||||
},
|
||||
style: { postcss: { options: {}, plugins: [] } },
|
||||
integrations: [],
|
||||
markdown: {
|
||||
drafts: false,
|
||||
syntaxHighlight: 'shiki',
|
||||
shikiConfig: {
|
||||
langs: [],
|
||||
theme: 'github-dark',
|
||||
wrap: false,
|
||||
},
|
||||
remarkPlugins: [],
|
||||
rehypePlugins: [],
|
||||
remarkRehype: {},
|
||||
},
|
||||
vite: {},
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: false,
|
||||
},
|
||||
};
|
||||
|
||||
async function resolvePostcssConfig(inlineOptions: any, root: URL): Promise<PostCSSConfigResult> {
|
||||
if (isObject(inlineOptions)) {
|
||||
const options = { ...inlineOptions };
|
||||
delete options.plugins;
|
||||
return {
|
||||
options,
|
||||
plugins: inlineOptions.plugins || [],
|
||||
};
|
||||
}
|
||||
const searchPath = typeof inlineOptions === 'string' ? inlineOptions : fileURLToPath(root);
|
||||
try {
|
||||
// @ts-ignore
|
||||
return await postcssrc({}, searchPath);
|
||||
} catch (err: any) {
|
||||
if (!/No PostCSS Config found/.test(err.message)) {
|
||||
throw err;
|
||||
}
|
||||
return {
|
||||
options: {},
|
||||
plugins: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const LEGACY_ASTRO_CONFIG_KEYS = new Set([
|
||||
'projectRoot',
|
||||
'src',
|
||||
|
@ -97,146 +27,6 @@ export const LEGACY_ASTRO_CONFIG_KEYS = new Set([
|
|||
'devOptions',
|
||||
]);
|
||||
|
||||
export const AstroConfigSchema = z.object({
|
||||
root: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.root)
|
||||
.transform((val) => new URL(val)),
|
||||
srcDir: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.srcDir)
|
||||
.transform((val) => new URL(val)),
|
||||
publicDir: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.publicDir)
|
||||
.transform((val) => new URL(val)),
|
||||
outDir: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.outDir)
|
||||
.transform((val) => new URL(val)),
|
||||
site: z
|
||||
.string()
|
||||
.url()
|
||||
.optional()
|
||||
.transform((val) => (val ? appendForwardSlash(val) : val)),
|
||||
base: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.base)
|
||||
.transform((val) => prependForwardSlash(appendForwardSlash(trimSlashes(val)))),
|
||||
trailingSlash: z
|
||||
.union([z.literal('always'), z.literal('never'), z.literal('ignore')])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.trailingSlash),
|
||||
output: z
|
||||
.union([z.literal('static'), z.literal('server')])
|
||||
.optional()
|
||||
.default('static'),
|
||||
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
|
||||
integrations: z.preprocess(
|
||||
// preprocess
|
||||
(val) => (Array.isArray(val) ? val.flat(Infinity).filter(Boolean) : val),
|
||||
// validate
|
||||
z
|
||||
.array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }))
|
||||
.default(ASTRO_CONFIG_DEFAULTS.integrations)
|
||||
),
|
||||
build: z
|
||||
.object({
|
||||
format: z
|
||||
.union([z.literal('file'), z.literal('directory')])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.build.format),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
server: z.preprocess(
|
||||
// preprocess
|
||||
// NOTE: Uses the "error" command here because this is overwritten by the
|
||||
// individualized schema parser with the correct command.
|
||||
(val) => (typeof val === 'function' ? val({ command: 'error' }) : val),
|
||||
// validate
|
||||
z
|
||||
.object({
|
||||
host: z
|
||||
.union([z.string(), z.boolean()])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.server.host),
|
||||
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
|
||||
})
|
||||
.optional()
|
||||
.default({})
|
||||
),
|
||||
style: z
|
||||
.object({
|
||||
postcss: z
|
||||
.object({
|
||||
options: z.any(),
|
||||
plugins: z.array(z.any()),
|
||||
})
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.style.postcss),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
markdown: z
|
||||
.object({
|
||||
drafts: z.boolean().default(false),
|
||||
syntaxHighlight: z
|
||||
.union([z.literal('shiki'), z.literal('prism'), z.literal(false)])
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight),
|
||||
shikiConfig: z
|
||||
.object({
|
||||
langs: z.custom<ILanguageRegistration>().array().default([]),
|
||||
theme: z
|
||||
.enum(BUNDLED_THEMES as [Theme, ...Theme[]])
|
||||
.or(z.custom<IThemeRegistration>())
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme),
|
||||
wrap: z.boolean().or(z.null()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.wrap),
|
||||
})
|
||||
.default({}),
|
||||
remarkPlugins: z
|
||||
.union([
|
||||
z.string(),
|
||||
z.tuple([z.string(), z.any()]),
|
||||
z.custom<RemarkPlugin>((data) => typeof data === 'function'),
|
||||
z.tuple([z.custom<RemarkPlugin>((data) => typeof data === 'function'), z.any()]),
|
||||
])
|
||||
.array()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.remarkPlugins),
|
||||
rehypePlugins: z
|
||||
.union([
|
||||
z.string(),
|
||||
z.tuple([z.string(), z.any()]),
|
||||
z.custom<RehypePlugin>((data) => typeof data === 'function'),
|
||||
z.tuple([z.custom<RehypePlugin>((data) => typeof data === 'function'), z.any()]),
|
||||
])
|
||||
.array()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.rehypePlugins),
|
||||
remarkRehype: z
|
||||
.custom<RemarkRehype>((data) => data instanceof Object && !Array.isArray(data))
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype),
|
||||
extendDefaultPlugins: z.boolean().default(false),
|
||||
})
|
||||
.default({}),
|
||||
vite: z
|
||||
.custom<ViteUserConfig>((data) => data instanceof Object && !Array.isArray(data))
|
||||
.default(ASTRO_CONFIG_DEFAULTS.vite),
|
||||
legacy: z
|
||||
.object({
|
||||
astroFlavoredMarkdown: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.legacy.astroFlavoredMarkdown),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
});
|
||||
|
||||
/** Turn raw config values into normalized values */
|
||||
export async function validateConfig(
|
||||
|
@ -294,72 +84,10 @@ export async function validateConfig(
|
|||
}
|
||||
/* eslint-enable no-console */
|
||||
|
||||
// We need to extend the global schema to add transforms that are relative to root.
|
||||
// This is type checked against the global schema to make sure we still match.
|
||||
const AstroConfigRelativeSchema = AstroConfigSchema.extend({
|
||||
root: z
|
||||
.string()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.root)
|
||||
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
|
||||
srcDir: z
|
||||
.string()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.srcDir)
|
||||
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
|
||||
publicDir: z
|
||||
.string()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.publicDir)
|
||||
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
|
||||
outDir: z
|
||||
.string()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.outDir)
|
||||
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
|
||||
server: z.preprocess(
|
||||
// preprocess
|
||||
(val) =>
|
||||
typeof val === 'function' ? val({ command: cmd === 'dev' ? 'dev' : 'preview' }) : val,
|
||||
// validate
|
||||
z
|
||||
.object({
|
||||
host: z
|
||||
.union([z.string(), z.boolean()])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.server.host),
|
||||
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
|
||||
streaming: z.boolean().optional().default(true),
|
||||
})
|
||||
.optional()
|
||||
.default({})
|
||||
),
|
||||
style: z
|
||||
.object({
|
||||
postcss: z.preprocess(
|
||||
(val) => resolvePostcssConfig(val, fileProtocolRoot),
|
||||
z
|
||||
.object({
|
||||
options: z.any(),
|
||||
plugins: z.array(z.any()),
|
||||
})
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.style.postcss)
|
||||
),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
});
|
||||
const tsconfig = loadTSConfig(root);
|
||||
const AstroConfigRelativeSchema = createRelativeSchema(cmd, fileProtocolRoot);
|
||||
|
||||
// First-Pass Validation
|
||||
const result = {
|
||||
...(await AstroConfigRelativeSchema.parseAsync(userConfig)),
|
||||
_ctx: {
|
||||
pageExtensions: ['.astro', '.md', '.html'],
|
||||
tsConfig: tsconfig?.config,
|
||||
tsConfigPath: tsconfig?.path,
|
||||
scripts: [],
|
||||
renderers: [jsxRenderer],
|
||||
injectedRoutes: [],
|
||||
adapter: undefined,
|
||||
},
|
||||
};
|
||||
const result = await AstroConfigRelativeSchema.parseAsync(userConfig);
|
||||
|
||||
// If successful, return the result as a verified AstroConfig object.
|
||||
return result;
|
||||
|
@ -554,16 +282,6 @@ async function tryLoadConfig(
|
|||
}
|
||||
}
|
||||
|
||||
function loadTSConfig(cwd: string | undefined): tsr.TsConfigResult | undefined {
|
||||
for (const searchName of ['tsconfig.json', 'jsconfig.json']) {
|
||||
const config = tsr.tsconfigResolverSync({ cwd, searchName });
|
||||
if (config.exists) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to load an `astro.config.mjs` file
|
||||
* @deprecated
|
20
packages/astro/src/core/config/index.ts
Normal file
20
packages/astro/src/core/config/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
|
||||
export type {
|
||||
AstroConfigSchema
|
||||
} from './schema';
|
||||
|
||||
export {
|
||||
openConfig,
|
||||
resolveConfigPath,
|
||||
resolveFlags,
|
||||
resolveRoot,
|
||||
validateConfig,
|
||||
} from './config.js';
|
||||
|
||||
export {
|
||||
createSettings
|
||||
} from './settings.js';
|
||||
|
||||
export {
|
||||
loadTSConfig
|
||||
} from './tsconfig.js';
|
271
packages/astro/src/core/config/schema.ts
Normal file
271
packages/astro/src/core/config/schema.ts
Normal file
|
@ -0,0 +1,271 @@
|
|||
import type { RehypePlugin, RemarkPlugin, RemarkRehype } from '@astrojs/markdown-remark';
|
||||
import type * as Postcss from 'postcss';
|
||||
import type { ILanguageRegistration, IThemeRegistration, Theme } from 'shiki';
|
||||
import type { AstroUserConfig, ViteUserConfig } from '../../@types/astro';
|
||||
|
||||
import postcssrc from 'postcss-load-config';
|
||||
import { BUNDLED_THEMES } from 'shiki';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { z } from 'zod';
|
||||
import { appendForwardSlash, prependForwardSlash, trimSlashes } from '../path.js';
|
||||
import { isObject } from '../util.js';
|
||||
|
||||
const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
|
||||
root: '.',
|
||||
srcDir: './src',
|
||||
publicDir: './public',
|
||||
outDir: './dist',
|
||||
base: '/',
|
||||
trailingSlash: 'ignore',
|
||||
build: { format: 'directory' },
|
||||
server: {
|
||||
host: false,
|
||||
port: 3000,
|
||||
streaming: true,
|
||||
},
|
||||
style: { postcss: { options: {}, plugins: [] } },
|
||||
integrations: [],
|
||||
markdown: {
|
||||
drafts: false,
|
||||
syntaxHighlight: 'shiki',
|
||||
shikiConfig: {
|
||||
langs: [],
|
||||
theme: 'github-dark',
|
||||
wrap: false,
|
||||
},
|
||||
remarkPlugins: [],
|
||||
rehypePlugins: [],
|
||||
remarkRehype: {},
|
||||
},
|
||||
vite: {},
|
||||
legacy: {
|
||||
astroFlavoredMarkdown: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const AstroConfigSchema = z.object({
|
||||
root: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.root)
|
||||
.transform((val) => new URL(val)),
|
||||
srcDir: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.srcDir)
|
||||
.transform((val) => new URL(val)),
|
||||
publicDir: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.publicDir)
|
||||
.transform((val) => new URL(val)),
|
||||
outDir: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.outDir)
|
||||
.transform((val) => new URL(val)),
|
||||
site: z
|
||||
.string()
|
||||
.url()
|
||||
.optional()
|
||||
.transform((val) => (val ? appendForwardSlash(val) : val)),
|
||||
base: z
|
||||
.string()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.base)
|
||||
.transform((val) => prependForwardSlash(appendForwardSlash(trimSlashes(val)))),
|
||||
trailingSlash: z
|
||||
.union([z.literal('always'), z.literal('never'), z.literal('ignore')])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.trailingSlash),
|
||||
output: z
|
||||
.union([z.literal('static'), z.literal('server')])
|
||||
.optional()
|
||||
.default('static'),
|
||||
adapter: z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }).optional(),
|
||||
integrations: z.preprocess(
|
||||
// preprocess
|
||||
(val) => (Array.isArray(val) ? val.flat(Infinity).filter(Boolean) : val),
|
||||
// validate
|
||||
z
|
||||
.array(z.object({ name: z.string(), hooks: z.object({}).passthrough().default({}) }))
|
||||
.default(ASTRO_CONFIG_DEFAULTS.integrations)
|
||||
),
|
||||
build: z
|
||||
.object({
|
||||
format: z
|
||||
.union([z.literal('file'), z.literal('directory')])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.build.format),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
server: z.preprocess(
|
||||
// preprocess
|
||||
// NOTE: Uses the "error" command here because this is overwritten by the
|
||||
// individualized schema parser with the correct command.
|
||||
(val) => (typeof val === 'function' ? val({ command: 'error' }) : val),
|
||||
// validate
|
||||
z
|
||||
.object({
|
||||
host: z
|
||||
.union([z.string(), z.boolean()])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.server.host),
|
||||
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
|
||||
})
|
||||
.optional()
|
||||
.default({})
|
||||
),
|
||||
style: z
|
||||
.object({
|
||||
postcss: z
|
||||
.object({
|
||||
options: z.any(),
|
||||
plugins: z.array(z.any()),
|
||||
})
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.style.postcss),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
markdown: z
|
||||
.object({
|
||||
drafts: z.boolean().default(false),
|
||||
syntaxHighlight: z
|
||||
.union([z.literal('shiki'), z.literal('prism'), z.literal(false)])
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.syntaxHighlight),
|
||||
shikiConfig: z
|
||||
.object({
|
||||
langs: z.custom<ILanguageRegistration>().array().default([]),
|
||||
theme: z
|
||||
.enum(BUNDLED_THEMES as [Theme, ...Theme[]])
|
||||
.or(z.custom<IThemeRegistration>())
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.theme),
|
||||
wrap: z.boolean().or(z.null()).default(ASTRO_CONFIG_DEFAULTS.markdown.shikiConfig.wrap),
|
||||
})
|
||||
.default({}),
|
||||
remarkPlugins: z
|
||||
.union([
|
||||
z.string(),
|
||||
z.tuple([z.string(), z.any()]),
|
||||
z.custom<RemarkPlugin>((data) => typeof data === 'function'),
|
||||
z.tuple([z.custom<RemarkPlugin>((data) => typeof data === 'function'), z.any()]),
|
||||
])
|
||||
.array()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.remarkPlugins),
|
||||
rehypePlugins: z
|
||||
.union([
|
||||
z.string(),
|
||||
z.tuple([z.string(), z.any()]),
|
||||
z.custom<RehypePlugin>((data) => typeof data === 'function'),
|
||||
z.tuple([z.custom<RehypePlugin>((data) => typeof data === 'function'), z.any()]),
|
||||
])
|
||||
.array()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.rehypePlugins),
|
||||
remarkRehype: z
|
||||
.custom<RemarkRehype>((data) => data instanceof Object && !Array.isArray(data))
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.markdown.remarkRehype),
|
||||
extendDefaultPlugins: z.boolean().default(false),
|
||||
})
|
||||
.default({}),
|
||||
vite: z
|
||||
.custom<ViteUserConfig>((data) => data instanceof Object && !Array.isArray(data))
|
||||
.default(ASTRO_CONFIG_DEFAULTS.vite),
|
||||
legacy: z
|
||||
.object({
|
||||
astroFlavoredMarkdown: z
|
||||
.boolean()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.legacy.astroFlavoredMarkdown),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
});
|
||||
|
||||
interface PostCSSConfigResult {
|
||||
options: Postcss.ProcessOptions;
|
||||
plugins: Postcss.Plugin[];
|
||||
}
|
||||
|
||||
async function resolvePostcssConfig(inlineOptions: any, root: URL): Promise<PostCSSConfigResult> {
|
||||
if (isObject(inlineOptions)) {
|
||||
const options = { ...inlineOptions };
|
||||
delete options.plugins;
|
||||
return {
|
||||
options,
|
||||
plugins: inlineOptions.plugins || [],
|
||||
};
|
||||
}
|
||||
const searchPath = typeof inlineOptions === 'string' ? inlineOptions : fileURLToPath(root);
|
||||
try {
|
||||
// @ts-ignore
|
||||
return await postcssrc({}, searchPath);
|
||||
} catch (err: any) {
|
||||
if (!/No PostCSS Config found/.test(err.message)) {
|
||||
throw err;
|
||||
}
|
||||
return {
|
||||
options: {},
|
||||
plugins: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function createRelativeSchema(cmd: string, fileProtocolRoot: URL) {
|
||||
// We need to extend the global schema to add transforms that are relative to root.
|
||||
// This is type checked against the global schema to make sure we still match.
|
||||
const AstroConfigRelativeSchema = AstroConfigSchema.extend({
|
||||
root: z
|
||||
.string()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.root)
|
||||
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
|
||||
srcDir: z
|
||||
.string()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.srcDir)
|
||||
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
|
||||
publicDir: z
|
||||
.string()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.publicDir)
|
||||
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
|
||||
outDir: z
|
||||
.string()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.outDir)
|
||||
.transform((val) => new URL(appendForwardSlash(val), fileProtocolRoot)),
|
||||
server: z.preprocess(
|
||||
// preprocess
|
||||
(val) =>
|
||||
typeof val === 'function' ? val({ command: cmd === 'dev' ? 'dev' : 'preview' }) : val,
|
||||
// validate
|
||||
z
|
||||
.object({
|
||||
host: z
|
||||
.union([z.string(), z.boolean()])
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.server.host),
|
||||
port: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.server.port),
|
||||
streaming: z.boolean().optional().default(true),
|
||||
})
|
||||
.optional()
|
||||
.default({})
|
||||
),
|
||||
style: z
|
||||
.object({
|
||||
postcss: z.preprocess(
|
||||
(val) => resolvePostcssConfig(val, fileProtocolRoot),
|
||||
z
|
||||
.object({
|
||||
options: z.any(),
|
||||
plugins: z.array(z.any()),
|
||||
})
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.style.postcss)
|
||||
),
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
});
|
||||
|
||||
return AstroConfigRelativeSchema;
|
||||
}
|
31
packages/astro/src/core/config/settings.ts
Normal file
31
packages/astro/src/core/config/settings.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import type {
|
||||
AstroConfig,
|
||||
AstroSettings,
|
||||
} from '../../@types/astro';
|
||||
import type { TsConfigJson } from 'tsconfig-resolver';
|
||||
|
||||
import jsxRenderer from '../../jsx/renderer.js';
|
||||
|
||||
export interface CreateSettings {
|
||||
config: AstroConfig;
|
||||
tsConfig?: TsConfigJson;
|
||||
tsConfigPath?: string;
|
||||
}
|
||||
|
||||
export function createSettings({
|
||||
config,
|
||||
tsConfig,
|
||||
tsConfigPath,
|
||||
}: CreateSettings): AstroSettings {
|
||||
return {
|
||||
config,
|
||||
tsConfig,
|
||||
tsConfigPath,
|
||||
|
||||
adapter: undefined,
|
||||
injectedRoutes: [],
|
||||
pageExtensions: ['.astro', '.md', '.html'],
|
||||
renderers: [jsxRenderer],
|
||||
scripts: [],
|
||||
};
|
||||
}
|
11
packages/astro/src/core/config/tsconfig.ts
Normal file
11
packages/astro/src/core/config/tsconfig.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import * as tsr from 'tsconfig-resolver';
|
||||
|
||||
export function loadTSConfig(cwd: string | undefined): tsr.TsConfigResult | undefined {
|
||||
for (const searchName of ['tsconfig.json', 'jsconfig.json']) {
|
||||
const config = tsr.tsconfigResolverSync({ cwd, searchName });
|
||||
if (config.exists) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import type { AstroConfig } from '../@types/astro';
|
||||
import type { AstroSettings } from '../@types/astro';
|
||||
import type { LogOptions } from './logger/core';
|
||||
|
||||
import fs from 'fs';
|
||||
|
@ -25,7 +25,7 @@ import { resolveDependency } from './util.js';
|
|||
export type ViteConfigWithSSR = vite.InlineConfig & { ssr?: vite.SSROptions };
|
||||
|
||||
interface CreateViteOptions {
|
||||
astroConfig: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
mode: 'dev' | 'build' | string;
|
||||
}
|
||||
|
@ -59,12 +59,12 @@ function getSsrNoExternalDeps(projectRoot: URL): string[] {
|
|||
/** Return a common starting point for all Vite actions */
|
||||
export async function createVite(
|
||||
commandConfig: ViteConfigWithSSR,
|
||||
{ astroConfig, logging, mode }: CreateViteOptions
|
||||
{ settings, logging, mode }: CreateViteOptions
|
||||
): Promise<ViteConfigWithSSR> {
|
||||
const thirdPartyAstroPackages = await getAstroPackages(astroConfig);
|
||||
const thirdPartyAstroPackages = await getAstroPackages(settings);
|
||||
// Start with the Vite configuration that Astro core needs
|
||||
const commonConfig: ViteConfigWithSSR = {
|
||||
cacheDir: fileURLToPath(new URL('./node_modules/.vite/', astroConfig.root)), // using local caches allows Astro to be used in monorepos, etc.
|
||||
cacheDir: fileURLToPath(new URL('./node_modules/.vite/', settings.config.root)), // using local caches allows Astro to be used in monorepos, etc.
|
||||
clearScreen: false, // we want to control the output, not Vite
|
||||
logLevel: 'warn', // log warnings and errors only
|
||||
appType: 'custom',
|
||||
|
@ -73,27 +73,27 @@ export async function createVite(
|
|||
exclude: ['node-fetch'],
|
||||
},
|
||||
plugins: [
|
||||
configAliasVitePlugin({ config: astroConfig }),
|
||||
astroVitePlugin({ config: astroConfig, logging }),
|
||||
astroScriptsPlugin({ config: astroConfig }),
|
||||
configAliasVitePlugin({ settings }),
|
||||
astroVitePlugin({ settings, logging }),
|
||||
astroScriptsPlugin({ settings }),
|
||||
// The server plugin is for dev only and having it run during the build causes
|
||||
// the build to run very slow as the filewatcher is triggered often.
|
||||
mode !== 'build' && astroViteServerPlugin({ config: astroConfig, logging }),
|
||||
envVitePlugin({ config: astroConfig }),
|
||||
astroConfig.legacy.astroFlavoredMarkdown
|
||||
? legacyMarkdownVitePlugin({ config: astroConfig, logging })
|
||||
: markdownVitePlugin({ config: astroConfig, logging }),
|
||||
mode !== 'build' && astroViteServerPlugin({ settings, logging }),
|
||||
envVitePlugin({ settings }),
|
||||
settings.config.legacy.astroFlavoredMarkdown
|
||||
? legacyMarkdownVitePlugin({ settings, logging })
|
||||
: markdownVitePlugin({ settings, logging }),
|
||||
htmlVitePlugin(),
|
||||
jsxVitePlugin({ config: astroConfig, logging }),
|
||||
astroPostprocessVitePlugin({ config: astroConfig }),
|
||||
astroIntegrationsContainerPlugin({ config: astroConfig, logging }),
|
||||
astroScriptsPageSSRPlugin({ config: astroConfig }),
|
||||
jsxVitePlugin({ settings, logging }),
|
||||
astroPostprocessVitePlugin({ settings }),
|
||||
astroIntegrationsContainerPlugin({ settings, logging }),
|
||||
astroScriptsPageSSRPlugin({ settings }),
|
||||
],
|
||||
publicDir: fileURLToPath(astroConfig.publicDir),
|
||||
root: fileURLToPath(astroConfig.root),
|
||||
publicDir: fileURLToPath(settings.config.publicDir),
|
||||
root: fileURLToPath(settings.config.root),
|
||||
envPrefix: 'PUBLIC_',
|
||||
define: {
|
||||
'import.meta.env.SITE': astroConfig.site ? `'${astroConfig.site}'` : 'undefined',
|
||||
'import.meta.env.SITE': settings.config.site ? `'${settings.config.site}'` : 'undefined',
|
||||
},
|
||||
server: {
|
||||
hmr:
|
||||
|
@ -110,7 +110,7 @@ export async function createVite(
|
|||
},
|
||||
},
|
||||
css: {
|
||||
postcss: astroConfig.style.postcss || {},
|
||||
postcss: settings.config.style.postcss || {},
|
||||
},
|
||||
resolve: {
|
||||
alias: [
|
||||
|
@ -129,7 +129,7 @@ export async function createVite(
|
|||
conditions: ['astro'],
|
||||
},
|
||||
ssr: {
|
||||
noExternal: [...getSsrNoExternalDeps(astroConfig.root), ...thirdPartyAstroPackages],
|
||||
noExternal: [...getSsrNoExternalDeps(settings.config.root), ...thirdPartyAstroPackages],
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -140,7 +140,7 @@ export async function createVite(
|
|||
// 3. integration-provided vite config, via the `config:setup` hook
|
||||
// 4. command vite config, passed as the argument to this function
|
||||
let result = commonConfig;
|
||||
result = vite.mergeConfig(result, astroConfig.vite || {});
|
||||
result = vite.mergeConfig(result, settings.config.vite || {});
|
||||
result = vite.mergeConfig(result, commandConfig);
|
||||
if (result.plugins) {
|
||||
sortPlugins(result.plugins);
|
||||
|
@ -175,8 +175,8 @@ function sortPlugins(pluginOptions: vite.PluginOption[]) {
|
|||
|
||||
// Scans `projectRoot` for third-party Astro packages that could export an `.astro` file
|
||||
// `.astro` files need to be built by Vite, so these should use `noExternal`
|
||||
async function getAstroPackages({ root }: AstroConfig): Promise<string[]> {
|
||||
const { astroPackages } = new DependencyWalker(root);
|
||||
async function getAstroPackages(settings: AstroSettings): Promise<string[]> {
|
||||
const { astroPackages } = new DependencyWalker(settings.config.root);
|
||||
return astroPackages;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import type { AstroTelemetry } from '@astrojs/telemetry';
|
|||
import type { AddressInfo } from 'net';
|
||||
import { performance } from 'perf_hooks';
|
||||
import * as vite from 'vite';
|
||||
import type { AstroConfig } from '../../@types/astro';
|
||||
import type { AstroSettings } from '../../@types/astro';
|
||||
import {
|
||||
runHookConfigDone,
|
||||
runHookConfigSetup,
|
||||
|
@ -28,17 +28,17 @@ export interface DevServer {
|
|||
}
|
||||
|
||||
/** `astro dev` */
|
||||
export default async function dev(config: AstroConfig, options: DevOptions): Promise<DevServer> {
|
||||
export default async function dev(settings: AstroSettings, options: DevOptions): Promise<DevServer> {
|
||||
const devStart = performance.now();
|
||||
applyPolyfill();
|
||||
await options.telemetry.record([]);
|
||||
config = await runHookConfigSetup({ config, command: 'dev', logging: options.logging });
|
||||
const { host, port } = config.server;
|
||||
settings = await runHookConfigSetup({ settings, command: 'dev', logging: options.logging });
|
||||
const { host, port } = settings.config.server;
|
||||
const { isRestart = false } = options;
|
||||
|
||||
// The client entrypoint for renderers. Since these are imported dynamically
|
||||
// we need to tell Vite to preoptimize them.
|
||||
const rendererClientEntries = config._ctx.renderers
|
||||
const rendererClientEntries = settings.renderers
|
||||
.map((r) => r.clientEntrypoint)
|
||||
.filter(Boolean) as string[];
|
||||
|
||||
|
@ -50,21 +50,21 @@ export default async function dev(config: AstroConfig, options: DevOptions): Pro
|
|||
include: rendererClientEntries,
|
||||
},
|
||||
},
|
||||
{ astroConfig: config, logging: options.logging, mode: 'dev' }
|
||||
{ settings, logging: options.logging, mode: 'dev' }
|
||||
);
|
||||
await runHookConfigDone({ config, logging: options.logging });
|
||||
await runHookConfigDone({ settings, logging: options.logging });
|
||||
const viteServer = await vite.createServer(viteConfig);
|
||||
runHookServerSetup({ config, server: viteServer, logging: options.logging });
|
||||
runHookServerSetup({ config: settings.config, server: viteServer, logging: options.logging });
|
||||
await viteServer.listen(port);
|
||||
|
||||
const devServerAddressInfo = viteServer.httpServer!.address() as AddressInfo;
|
||||
const site = config.site ? new URL(config.base, config.site) : undefined;
|
||||
const site = settings.config.site ? new URL(settings.config.base, settings.config.site) : undefined;
|
||||
info(
|
||||
options.logging,
|
||||
null,
|
||||
msg.devStart({
|
||||
startupTime: performance.now() - devStart,
|
||||
config,
|
||||
config: settings.config,
|
||||
devServerAddressInfo,
|
||||
site,
|
||||
https: !!viteConfig.server?.https,
|
||||
|
@ -80,7 +80,7 @@ export default async function dev(config: AstroConfig, options: DevOptions): Pro
|
|||
warn(options.logging, null, msg.fsStrictWarning());
|
||||
}
|
||||
|
||||
await runHookServerStart({ config, address: devServerAddressInfo, logging: options.logging });
|
||||
await runHookServerStart({ config: settings.config, address: devServerAddressInfo, logging: options.logging });
|
||||
|
||||
return {
|
||||
address: devServerAddressInfo,
|
||||
|
@ -89,7 +89,7 @@ export default async function dev(config: AstroConfig, options: DevOptions): Pro
|
|||
},
|
||||
stop: async () => {
|
||||
await viteServer.close();
|
||||
await runHookServerDone({ config, logging: options.logging });
|
||||
await runHookServerDone({ config: settings.config, logging: options.logging });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@ export async function call(ssrOpts: SSROptions) {
|
|||
const [, mod] = await preload(ssrOpts);
|
||||
return await callEndpoint(mod as unknown as EndpointHandler, {
|
||||
...ssrOpts,
|
||||
ssr: ssrOpts.astroConfig.output === 'server',
|
||||
ssr: ssrOpts.settings.config.output === 'server',
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { AstroTelemetry } from '@astrojs/telemetry';
|
||||
import type { AddressInfo } from 'net';
|
||||
import type { AstroConfig } from '../../@types/astro';
|
||||
import type { AstroSettings } from '../../@types/astro';
|
||||
import type { LogOptions } from '../logger/core';
|
||||
|
||||
import fs from 'fs';
|
||||
|
@ -30,20 +30,20 @@ const HAS_FILE_EXTENSION_REGEXP = /^.*\.[^\\]+$/;
|
|||
|
||||
/** The primary dev action */
|
||||
export default async function preview(
|
||||
config: AstroConfig,
|
||||
settings: AstroSettings,
|
||||
{ logging }: PreviewOptions
|
||||
): Promise<PreviewServer> {
|
||||
if (config.output === 'server') {
|
||||
if (settings.config.output === 'server') {
|
||||
throw new Error(
|
||||
`[preview] 'output: server' not supported. Use your deploy platform's preview command directly instead, if one exists. (ex: 'netlify dev', 'vercel dev', 'wrangler', etc.)`
|
||||
);
|
||||
}
|
||||
const startServerTime = performance.now();
|
||||
const defaultOrigin = 'http://localhost';
|
||||
const trailingSlash = config.trailingSlash;
|
||||
const trailingSlash = settings.config.trailingSlash;
|
||||
/** Base request URL. */
|
||||
let baseURL = new URL(config.base, new URL(config.site || '/', defaultOrigin));
|
||||
const staticFileServer = sirv(fileURLToPath(config.outDir), {
|
||||
let baseURL = new URL(settings.config.base, new URL(settings.config.site || '/', defaultOrigin));
|
||||
const staticFileServer = sirv(fileURLToPath(settings.config.outDir), {
|
||||
dev: true,
|
||||
etag: true,
|
||||
maxAge: 0,
|
||||
|
@ -84,7 +84,7 @@ export default async function preview(
|
|||
// HACK: rewrite req.url so that sirv finds the file
|
||||
req.url = '/' + req.url?.replace(baseURL.pathname, '');
|
||||
staticFileServer(req, res, () => {
|
||||
const errorPagePath = fileURLToPath(config.outDir + '/404.html');
|
||||
const errorPagePath = fileURLToPath(settings.config.outDir + '/404.html');
|
||||
if (fs.existsSync(errorPagePath)) {
|
||||
res.statusCode = 404;
|
||||
res.setHeader('Content-Type', 'text/html;charset=utf-8');
|
||||
|
@ -100,8 +100,8 @@ export default async function preview(
|
|||
}
|
||||
});
|
||||
|
||||
let { port } = config.server;
|
||||
const host = getResolvedHostForHttpServer(config.server.host);
|
||||
let { port } = settings.config.server;
|
||||
const host = getResolvedHostForHttpServer(settings.config.server.host);
|
||||
|
||||
let httpServer: http.Server;
|
||||
|
||||
|
@ -119,7 +119,7 @@ export default async function preview(
|
|||
null,
|
||||
msg.devStart({
|
||||
startupTime: performance.now() - timerStart,
|
||||
config,
|
||||
config: settings.config,
|
||||
devServerAddressInfo,
|
||||
https: false,
|
||||
site: baseURL,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { fileURLToPath } from 'url';
|
||||
import type { ViteDevServer } from 'vite';
|
||||
import type {
|
||||
AstroConfig,
|
||||
AstroSettings,
|
||||
AstroRenderer,
|
||||
ComponentInstance,
|
||||
RouteData,
|
||||
|
@ -20,8 +20,8 @@ import { resolveClientDevPath } from './resolve.js';
|
|||
import { getScriptsForURL } from './scripts.js';
|
||||
|
||||
export interface SSROptions {
|
||||
/** an instance of the AstroConfig */
|
||||
astroConfig: AstroConfig;
|
||||
/** an instance of the AstroSettings */
|
||||
settings: AstroSettings;
|
||||
/** location of file on disk */
|
||||
filePath: URL;
|
||||
/** logging options */
|
||||
|
@ -58,18 +58,18 @@ async function loadRenderer(
|
|||
|
||||
export async function loadRenderers(
|
||||
viteServer: ViteDevServer,
|
||||
astroConfig: AstroConfig
|
||||
settings: AstroSettings
|
||||
): Promise<SSRLoadedRenderer[]> {
|
||||
return Promise.all(astroConfig._ctx.renderers.map((r) => loadRenderer(viteServer, r)));
|
||||
return Promise.all(settings.renderers.map((r) => loadRenderer(viteServer, r)));
|
||||
}
|
||||
|
||||
export async function preload({
|
||||
astroConfig,
|
||||
settings,
|
||||
filePath,
|
||||
viteServer,
|
||||
}: Pick<SSROptions, 'astroConfig' | 'filePath' | 'viteServer'>): Promise<ComponentPreload> {
|
||||
}: Pick<SSROptions, 'settings' | 'filePath' | 'viteServer'>): Promise<ComponentPreload> {
|
||||
// Important: This needs to happen first, in case a renderer provides polyfills.
|
||||
const renderers = await loadRenderers(viteServer, astroConfig);
|
||||
const renderers = await loadRenderers(viteServer, settings);
|
||||
// Load the module from the Vite SSR Runtime.
|
||||
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
|
||||
if (viteServer.config.mode === 'development' || !mod?.$$metadata) {
|
||||
|
@ -92,7 +92,7 @@ export async function render(
|
|||
ssrOpts: SSROptions
|
||||
): Promise<Response> {
|
||||
const {
|
||||
astroConfig,
|
||||
settings,
|
||||
filePath,
|
||||
logging,
|
||||
mode,
|
||||
|
@ -104,10 +104,10 @@ export async function render(
|
|||
viteServer,
|
||||
} = ssrOpts;
|
||||
// Add hoisted script tags
|
||||
const scripts = await getScriptsForURL(filePath, astroConfig, viteServer);
|
||||
const scripts = await getScriptsForURL(filePath, viteServer);
|
||||
|
||||
// Inject HMR scripts
|
||||
if (isPage(filePath, astroConfig) && mode === 'development') {
|
||||
if (isPage(filePath, settings) && mode === 'development') {
|
||||
scripts.add({
|
||||
props: { type: 'module', src: '/@vite/client' },
|
||||
children: '',
|
||||
|
@ -122,13 +122,13 @@ export async function render(
|
|||
}
|
||||
|
||||
// TODO: We should allow adding generic HTML elements to the head, not just scripts
|
||||
for (const script of astroConfig._ctx.scripts) {
|
||||
for (const script of settings.scripts) {
|
||||
if (script.stage === 'head-inline') {
|
||||
scripts.add({
|
||||
props: {},
|
||||
children: script.content,
|
||||
});
|
||||
} else if (script.stage === 'page' && isPage(filePath, astroConfig)) {
|
||||
} else if (script.stage === 'page' && isPage(filePath, settings)) {
|
||||
scripts.add({
|
||||
props: { type: 'module', src: `/@id/${PAGE_SCRIPT_ID}` },
|
||||
children: '',
|
||||
|
@ -167,13 +167,13 @@ export async function render(
|
|||
});
|
||||
|
||||
let response = await coreRender({
|
||||
adapterName: astroConfig.adapter?.name,
|
||||
adapterName: settings.config.adapter?.name,
|
||||
links,
|
||||
styles,
|
||||
logging,
|
||||
markdown: {
|
||||
...astroConfig.markdown,
|
||||
isAstroFlavoredMd: astroConfig.legacy.astroFlavoredMarkdown,
|
||||
...settings.config.markdown,
|
||||
isAstroFlavoredMd: settings.config.legacy.astroFlavoredMarkdown,
|
||||
},
|
||||
mod,
|
||||
mode,
|
||||
|
@ -191,8 +191,8 @@ export async function render(
|
|||
request,
|
||||
route,
|
||||
routeCache,
|
||||
site: astroConfig.site ? new URL(astroConfig.base, astroConfig.site).toString() : undefined,
|
||||
ssr: astroConfig.output === 'server',
|
||||
site: settings.config.site ? new URL(settings.config.base, settings.config.site).toString() : undefined,
|
||||
ssr: settings.config.output === 'server',
|
||||
streaming: true,
|
||||
});
|
||||
|
||||
|
|
|
@ -10,19 +10,17 @@ import { crawlGraph } from './vite.js';
|
|||
|
||||
export async function getScriptsForURL(
|
||||
filePath: URL,
|
||||
astroConfig: AstroConfig,
|
||||
viteServer: vite.ViteDevServer
|
||||
): Promise<Set<SSRElement>> {
|
||||
const elements = new Set<SSRElement>();
|
||||
const rootID = viteID(filePath);
|
||||
let rootProjectFolder = slash(fileURLToPath(astroConfig.root));
|
||||
const modInfo = viteServer.pluginContainer.getModuleInfo(rootID);
|
||||
addHoistedScripts(elements, modInfo, rootProjectFolder);
|
||||
addHoistedScripts(elements, modInfo);
|
||||
for await (const moduleNode of crawlGraph(viteServer, rootID, true)) {
|
||||
const id = moduleNode.id;
|
||||
if (id) {
|
||||
const info = viteServer.pluginContainer.getModuleInfo(id);
|
||||
addHoistedScripts(elements, info, rootProjectFolder);
|
||||
addHoistedScripts(elements, info);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +30,6 @@ export async function getScriptsForURL(
|
|||
function addHoistedScripts(
|
||||
set: Set<SSRElement>,
|
||||
info: ModuleInfo | null,
|
||||
rootProjectFolder: string
|
||||
) {
|
||||
if (!info?.meta?.astro) {
|
||||
return;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type {
|
||||
AstroConfig,
|
||||
AstroSettings,
|
||||
InjectedRoute,
|
||||
ManifestData,
|
||||
RouteData,
|
||||
|
@ -190,7 +191,7 @@ function injectedRouteToItem(
|
|||
|
||||
/** Create manifest of all static routes */
|
||||
export function createRouteManifest(
|
||||
{ config, cwd }: { config: AstroConfig; cwd?: string },
|
||||
{ settings, cwd }: { settings: AstroSettings; cwd?: string },
|
||||
logging: LogOptions
|
||||
): ManifestData {
|
||||
const components: string[] = [];
|
||||
|
@ -198,7 +199,7 @@ export function createRouteManifest(
|
|||
const validPageExtensions: Set<string> = new Set([
|
||||
'.astro',
|
||||
'.md',
|
||||
...config._ctx.pageExtensions,
|
||||
...settings.pageExtensions,
|
||||
]);
|
||||
const validEndpointExtensions: Set<string> = new Set(['.js', '.ts']);
|
||||
|
||||
|
@ -206,7 +207,7 @@ export function createRouteManifest(
|
|||
let items: Item[] = [];
|
||||
fs.readdirSync(dir).forEach((basename) => {
|
||||
const resolved = path.join(dir, basename);
|
||||
const file = slash(path.relative(cwd || fileURLToPath(config.root), resolved));
|
||||
const file = slash(path.relative(cwd || fileURLToPath(settings.config.root), resolved));
|
||||
const isDir = fs.statSync(resolved).isDirectory();
|
||||
|
||||
const ext = path.extname(basename);
|
||||
|
@ -283,7 +284,7 @@ export function createRouteManifest(
|
|||
} else {
|
||||
components.push(item.file);
|
||||
const component = item.file;
|
||||
const trailingSlash = item.isPage ? config.trailingSlash : 'never';
|
||||
const trailingSlash = item.isPage ? settings.config.trailingSlash : 'never';
|
||||
const pattern = getPattern(segments, trailingSlash);
|
||||
const generate = getRouteGenerator(segments, trailingSlash);
|
||||
const pathname = segments.every((segment) => segment.length === 1 && !segment[0].dynamic)
|
||||
|
@ -307,17 +308,18 @@ export function createRouteManifest(
|
|||
});
|
||||
}
|
||||
|
||||
const { config } = settings;
|
||||
const pages = resolvePages(config);
|
||||
|
||||
if (fs.existsSync(pages)) {
|
||||
walk(fileURLToPath(pages), [], []);
|
||||
} else if (config?._ctx?.injectedRoutes?.length === 0) {
|
||||
const pagesDirRootRelative = pages.href.slice(config.root.href.length);
|
||||
} else if (settings.injectedRoutes.length === 0) {
|
||||
const pagesDirRootRelative = pages.href.slice(settings.config.root.href.length);
|
||||
|
||||
warn(logging, 'astro', `Missing pages directory: ${pagesDirRootRelative}`);
|
||||
}
|
||||
|
||||
config?._ctx?.injectedRoutes
|
||||
settings.injectedRoutes
|
||||
?.sort((a, b) =>
|
||||
// sort injected routes in the same way as user-defined routes
|
||||
comparator(injectedRouteToItem({ config, cwd }, a), injectedRouteToItem({ config, cwd }, b))
|
||||
|
|
|
@ -5,7 +5,7 @@ import resolve from 'resolve';
|
|||
import slash from 'slash';
|
||||
import { fileURLToPath, pathToFileURL } from 'url';
|
||||
import type { ErrorPayload, ViteDevServer } from 'vite';
|
||||
import type { AstroConfig, RouteType } from '../@types/astro';
|
||||
import type { AstroConfig, AstroSettings, RouteType } from '../@types/astro';
|
||||
import { prependForwardSlash, removeTrailingForwardSlash } from './path.js';
|
||||
|
||||
// process.env.PACKAGE_VERSION is injected when we build and publish the astro package.
|
||||
|
@ -172,21 +172,21 @@ function isPublicRoute(file: URL, config: AstroConfig): boolean {
|
|||
return true;
|
||||
}
|
||||
|
||||
function endsWithPageExt(file: URL, config: AstroConfig): boolean {
|
||||
for (const ext of config._ctx.pageExtensions) {
|
||||
function endsWithPageExt(file: URL, settings: AstroSettings): boolean {
|
||||
for (const ext of settings.pageExtensions) {
|
||||
if (file.toString().endsWith(ext)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function isPage(file: URL, config: AstroConfig): boolean {
|
||||
if (!isInPagesDir(file, config)) return false;
|
||||
if (!isPublicRoute(file, config)) return false;
|
||||
return endsWithPageExt(file, config);
|
||||
export function isPage(file: URL, settings: AstroSettings): boolean {
|
||||
if (!isInPagesDir(file, settings.config)) return false;
|
||||
if (!isPublicRoute(file, settings.config)) return false;
|
||||
return endsWithPageExt(file, settings);
|
||||
}
|
||||
|
||||
export function isModeServerWithNoAdapter(config: AstroConfig): boolean {
|
||||
return config.output === 'server' && !config._ctx.adapter;
|
||||
export function isModeServerWithNoAdapter(settings: AstroSettings): boolean {
|
||||
return settings.config.output === 'server' && !settings.adapter;
|
||||
}
|
||||
|
||||
export function relativeToSrcDir(config: AstroConfig, idOrUrl: URL | string) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import type { AddressInfo } from 'net';
|
|||
import type { ViteDevServer } from 'vite';
|
||||
import {
|
||||
AstroConfig,
|
||||
AstroSettings,
|
||||
AstroRenderer,
|
||||
BuildConfig,
|
||||
HookParameters,
|
||||
|
@ -10,7 +11,7 @@ import {
|
|||
} from '../@types/astro.js';
|
||||
import type { SerializedSSRManifest } from '../core/app/types';
|
||||
import type { PageBuildData } from '../core/build/types';
|
||||
import { mergeConfig } from '../core/config.js';
|
||||
import { mergeConfig } from '../core/config/config.js';
|
||||
import type { ViteConfigWithSSR } from '../core/create-vite.js';
|
||||
import { info, LogOptions } from '../core/logger/core.js';
|
||||
|
||||
|
@ -34,21 +35,22 @@ async function withTakingALongTimeMsg<T>({
|
|||
}
|
||||
|
||||
export async function runHookConfigSetup({
|
||||
config: _config,
|
||||
settings,
|
||||
command,
|
||||
logging,
|
||||
}: {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
command: 'dev' | 'build';
|
||||
logging: LogOptions;
|
||||
}): Promise<AstroConfig> {
|
||||
}): Promise<AstroSettings> {
|
||||
// An adapter is an integration, so if one is provided push it.
|
||||
if (_config.adapter) {
|
||||
_config.integrations.push(_config.adapter);
|
||||
if (settings.config.adapter) {
|
||||
settings.config.integrations.push(settings.config.adapter);
|
||||
}
|
||||
|
||||
let updatedConfig: AstroConfig = { ..._config };
|
||||
for (const integration of _config.integrations) {
|
||||
let updatedConfig: AstroConfig = { ...settings.config };
|
||||
let updatedSettings: AstroSettings = { ...settings, config: updatedConfig };
|
||||
for (const integration of settings.config.integrations) {
|
||||
/**
|
||||
* By making integration hooks optional, Astro can now ignore null or undefined Integrations
|
||||
* instead of giving an internal error most people can't read
|
||||
|
@ -74,22 +76,22 @@ export async function runHookConfigSetup({
|
|||
throw new Error(`Renderer ${bold(renderer.name)} does not provide a serverEntrypoint.`);
|
||||
}
|
||||
|
||||
updatedConfig._ctx.renderers.push(renderer);
|
||||
updatedSettings.renderers.push(renderer);
|
||||
},
|
||||
injectScript: (stage, content) => {
|
||||
updatedConfig._ctx.scripts.push({ stage, content });
|
||||
updatedSettings.scripts.push({ stage, content });
|
||||
},
|
||||
updateConfig: (newConfig) => {
|
||||
updatedConfig = mergeConfig(updatedConfig, newConfig) as AstroConfig;
|
||||
},
|
||||
injectRoute: (injectRoute) => {
|
||||
updatedConfig._ctx.injectedRoutes.push(injectRoute);
|
||||
updatedSettings.injectedRoutes.push(injectRoute);
|
||||
},
|
||||
};
|
||||
// Semi-private `addPageExtension` hook
|
||||
function addPageExtension(...input: (string | string[])[]) {
|
||||
const exts = (input.flat(Infinity) as string[]).map((ext) => `.${ext.replace(/^\./, '')}`);
|
||||
updatedConfig._ctx.pageExtensions.push(...exts);
|
||||
updatedSettings.pageExtensions.push(...exts);
|
||||
}
|
||||
Object.defineProperty(hooks, 'addPageExtension', {
|
||||
value: addPageExtension,
|
||||
|
@ -103,29 +105,31 @@ export async function runHookConfigSetup({
|
|||
});
|
||||
}
|
||||
}
|
||||
return updatedConfig;
|
||||
|
||||
updatedSettings.config = updatedConfig;
|
||||
return updatedSettings;
|
||||
}
|
||||
|
||||
export async function runHookConfigDone({
|
||||
config,
|
||||
settings,
|
||||
logging,
|
||||
}: {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
}) {
|
||||
for (const integration of config.integrations) {
|
||||
for (const integration of settings.config.integrations) {
|
||||
if (integration?.hooks?.['astro:config:done']) {
|
||||
await withTakingALongTimeMsg({
|
||||
name: integration.name,
|
||||
hookResult: integration.hooks['astro:config:done']({
|
||||
config,
|
||||
config: settings.config,
|
||||
setAdapter(adapter) {
|
||||
if (config._ctx.adapter && config._ctx.adapter.name !== adapter.name) {
|
||||
if (settings.adapter && settings.adapter.name !== adapter.name) {
|
||||
throw new Error(
|
||||
`Integration "${integration.name}" conflicts with "${config._ctx.adapter.name}". You can only configure one deployment integration.`
|
||||
`Integration "${integration.name}" conflicts with "${settings.adapter.name}". You can only configure one deployment integration.`
|
||||
);
|
||||
}
|
||||
config._ctx.adapter = adapter;
|
||||
settings.adapter = adapter;
|
||||
},
|
||||
}),
|
||||
logging,
|
||||
|
|
|
@ -3,18 +3,18 @@ import type { ArrowFunctionExpressionKind, CallExpressionKind } from 'ast-types/
|
|||
import type { NodePath } from 'ast-types/lib/node-path';
|
||||
import { parse, print, types, visit } from 'recast';
|
||||
import type { Plugin } from 'vite';
|
||||
import type { AstroConfig } from '../@types/astro';
|
||||
import type { AstroSettings } from '../@types/astro';
|
||||
|
||||
// Check for `Astro.glob()`. Be very forgiving of whitespace. False positives are okay.
|
||||
const ASTRO_GLOB_REGEX = /Astro2?\s*\.\s*glob\s*\(/;
|
||||
interface AstroPluginOptions {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
}
|
||||
|
||||
// esbuild transforms the component-scoped Astro into Astro2, so need to check both.
|
||||
const validAstroGlobalNames = new Set(['Astro', 'Astro2']);
|
||||
|
||||
export default function astro({ config }: AstroPluginOptions): Plugin {
|
||||
export default function astro(_opts: AstroPluginOptions): Plugin {
|
||||
return {
|
||||
name: 'astro:postprocess',
|
||||
async transform(code, id) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type http from 'http';
|
||||
import mime from 'mime';
|
||||
import type * as vite from 'vite';
|
||||
import type { AstroConfig, ManifestData } from '../@types/astro';
|
||||
import type { AstroSettings, ManifestData } from '../@types/astro';
|
||||
import type { SSROptions } from '../core/render/dev/index';
|
||||
|
||||
import { Readable } from 'stream';
|
||||
|
@ -24,7 +24,7 @@ import { resolvePages } from '../core/util.js';
|
|||
import notFoundTemplate, { subpathNotUsedTemplate } from '../template/4xx.js';
|
||||
|
||||
interface AstroPluginOptions {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ async function writeSSRResult(webResponse: Response, res: http.ServerResponse) {
|
|||
|
||||
async function handle404Response(
|
||||
origin: string,
|
||||
config: AstroConfig,
|
||||
settings: AstroSettings,
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse
|
||||
) {
|
||||
|
@ -129,7 +129,7 @@ async function handle500Response(
|
|||
}
|
||||
}
|
||||
|
||||
function getCustom404Route(config: AstroConfig, manifest: ManifestData) {
|
||||
function getCustom404Route({ config }: AstroSettings, manifest: ManifestData) {
|
||||
// For Windows compat, use relative page paths to match the 404 route
|
||||
const relPages = resolvePages(config).href.replace(config.root.href, '');
|
||||
const pattern = new RegExp(`${appendForwardSlash(relPages)}404.(astro|md)`);
|
||||
|
@ -141,9 +141,10 @@ function log404(logging: LogOptions, pathname: string) {
|
|||
}
|
||||
|
||||
export function baseMiddleware(
|
||||
config: AstroConfig,
|
||||
settings: AstroSettings,
|
||||
logging: LogOptions
|
||||
): vite.Connect.NextHandleFunction {
|
||||
const { config } = settings;
|
||||
const site = config.site ? new URL(config.base, config.site) : undefined;
|
||||
const devRoot = site ? site.pathname : '/';
|
||||
|
||||
|
@ -184,13 +185,13 @@ async function matchRoute(
|
|||
viteServer: vite.ViteDevServer,
|
||||
logging: LogOptions,
|
||||
manifest: ManifestData,
|
||||
config: AstroConfig
|
||||
settings: AstroSettings
|
||||
) {
|
||||
const matches = matchAllRoutes(pathname, manifest);
|
||||
|
||||
for await (const maybeRoute of matches) {
|
||||
const filePath = new URL(`./${maybeRoute.component}`, config.root);
|
||||
const preloadedComponent = await preload({ astroConfig: config, filePath, viteServer });
|
||||
const filePath = new URL(`./${maybeRoute.component}`, settings.config.root);
|
||||
const preloadedComponent = await preload({ settings, filePath, viteServer });
|
||||
const [, mod] = preloadedComponent;
|
||||
// attempt to get static paths
|
||||
// if this fails, we have a bad URL match!
|
||||
|
@ -200,7 +201,7 @@ async function matchRoute(
|
|||
routeCache,
|
||||
pathname: pathname,
|
||||
logging,
|
||||
ssr: config.output === 'server',
|
||||
ssr: settings.config.output === 'server',
|
||||
});
|
||||
|
||||
if (paramsAndPropsRes !== GetParamsAndPropsError.NoMatchingStaticPath) {
|
||||
|
@ -222,11 +223,11 @@ async function matchRoute(
|
|||
}
|
||||
|
||||
log404(logging, pathname);
|
||||
const custom404 = getCustom404Route(config, manifest);
|
||||
const custom404 = getCustom404Route(settings, manifest);
|
||||
|
||||
if (custom404) {
|
||||
const filePath = new URL(`./${custom404.component}`, config.root);
|
||||
const preloadedComponent = await preload({ astroConfig: config, filePath, viteServer });
|
||||
const filePath = new URL(`./${custom404.component}`, settings.config.root);
|
||||
const preloadedComponent = await preload({ settings, filePath, viteServer });
|
||||
const [, mod] = preloadedComponent;
|
||||
|
||||
return {
|
||||
|
@ -246,10 +247,11 @@ async function handleRequest(
|
|||
viteServer: vite.ViteDevServer,
|
||||
logging: LogOptions,
|
||||
manifest: ManifestData,
|
||||
config: AstroConfig,
|
||||
settings: AstroSettings,
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse
|
||||
) {
|
||||
const { config } = settings;
|
||||
const origin = `${viteServer.config.server.https ? 'https' : 'http'}://${req.headers.host}`;
|
||||
const buildingToSSR = config.output === 'server';
|
||||
// Ignore `.html` extensions and `index.html` in request URLS to ensure that
|
||||
|
@ -292,7 +294,7 @@ async function handleRequest(
|
|||
viteServer,
|
||||
logging,
|
||||
manifest,
|
||||
config
|
||||
settings
|
||||
);
|
||||
filePath = matchedRoute?.filePath;
|
||||
|
||||
|
@ -306,7 +308,7 @@ async function handleRequest(
|
|||
viteServer,
|
||||
manifest,
|
||||
logging,
|
||||
config,
|
||||
settings,
|
||||
req,
|
||||
res
|
||||
);
|
||||
|
@ -328,14 +330,15 @@ async function handleRoute(
|
|||
viteServer: vite.ViteDevServer,
|
||||
manifest: ManifestData,
|
||||
logging: LogOptions,
|
||||
config: AstroConfig,
|
||||
settings: AstroSettings,
|
||||
req: http.IncomingMessage,
|
||||
res: http.ServerResponse
|
||||
): Promise<void> {
|
||||
if (!matchedRoute) {
|
||||
return handle404Response(origin, config, req, res);
|
||||
return handle404Response(origin, settings, req, res);
|
||||
}
|
||||
|
||||
const { config } = settings;
|
||||
const filePath: URL | undefined = matchedRoute.filePath;
|
||||
const { route, preloadedComponent, mod } = matchedRoute;
|
||||
const buildingToSSR = config.output === 'server';
|
||||
|
@ -363,7 +366,7 @@ async function handleRoute(
|
|||
});
|
||||
|
||||
const options: SSROptions = {
|
||||
astroConfig: config,
|
||||
settings,
|
||||
filePath,
|
||||
logging,
|
||||
mode: 'development',
|
||||
|
@ -386,7 +389,7 @@ async function handleRoute(
|
|||
viteServer,
|
||||
logging,
|
||||
manifest,
|
||||
config
|
||||
settings
|
||||
);
|
||||
return handleRoute(
|
||||
fourOhFourRoute,
|
||||
|
@ -398,7 +401,7 @@ async function handleRoute(
|
|||
viteServer,
|
||||
manifest,
|
||||
logging,
|
||||
config,
|
||||
settings,
|
||||
req,
|
||||
res
|
||||
);
|
||||
|
@ -423,17 +426,17 @@ async function handleRoute(
|
|||
}
|
||||
}
|
||||
|
||||
export default function createPlugin({ config, logging }: AstroPluginOptions): vite.Plugin {
|
||||
export default function createPlugin({ settings, logging }: AstroPluginOptions): vite.Plugin {
|
||||
return {
|
||||
name: 'astro:server',
|
||||
configureServer(viteServer) {
|
||||
let routeCache = new RouteCache(logging);
|
||||
let manifest: ManifestData = createRouteManifest({ config: config }, logging);
|
||||
let manifest: ManifestData = createRouteManifest({ settings }, logging);
|
||||
/** rebuild the route cache + manifest, as needed. */
|
||||
function rebuildManifest(needsManifestRebuild: boolean, file: string) {
|
||||
routeCache.clearAll();
|
||||
if (needsManifestRebuild) {
|
||||
manifest = createRouteManifest({ config: config }, logging);
|
||||
manifest = createRouteManifest({ settings }, logging);
|
||||
}
|
||||
}
|
||||
// Rebuild route manifest on file change, if needed.
|
||||
|
@ -442,17 +445,17 @@ export default function createPlugin({ config, logging }: AstroPluginOptions): v
|
|||
viteServer.watcher.on('change', rebuildManifest.bind(null, false));
|
||||
return () => {
|
||||
// Push this middleware to the front of the stack so that it can intercept responses.
|
||||
if (config.base !== '/') {
|
||||
if (settings.config.base !== '/') {
|
||||
viteServer.middlewares.stack.unshift({
|
||||
route: '',
|
||||
handle: baseMiddleware(config, logging),
|
||||
handle: baseMiddleware(settings, logging),
|
||||
});
|
||||
}
|
||||
viteServer.middlewares.use(async (req, res) => {
|
||||
if (!req.url || !req.method) {
|
||||
throw new Error('Incomplete request');
|
||||
}
|
||||
handleRequest(routeCache, viteServer, logging, manifest, config, req, res);
|
||||
handleRequest(routeCache, viteServer, logging, manifest, settings, req, res);
|
||||
});
|
||||
};
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type { PluginContext, SourceDescription } from 'rollup';
|
||||
import type * as vite from 'vite';
|
||||
import type { AstroConfig } from '../@types/astro';
|
||||
import type { AstroSettings } from '../@types/astro';
|
||||
import type { LogOptions } from '../core/logger/core.js';
|
||||
import type { ViteStyleTransformer } from '../vite-style-transform';
|
||||
import type { PluginMetadata as AstroPluginMetadata } from './types';
|
||||
|
@ -22,12 +22,13 @@ import { parseAstroRequest, ParsedRequestResult } from './query.js';
|
|||
|
||||
const FRONTMATTER_PARSE_REGEXP = /^\-\-\-(.*)^\-\-\-/ms;
|
||||
interface AstroPluginOptions {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
/** Transform .astro files for Vite */
|
||||
export default function astro({ config, logging }: AstroPluginOptions): vite.Plugin {
|
||||
export default function astro({ settings, logging }: AstroPluginOptions): vite.Plugin {
|
||||
const { config } = settings;
|
||||
function normalizeFilename(filename: string) {
|
||||
if (filename.startsWith('/@fs')) {
|
||||
filename = filename.slice('/@fs'.length);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as path from 'path';
|
||||
import type { AstroConfig } from '../@types/astro';
|
||||
import type { AstroSettings } from '../@types/astro';
|
||||
|
||||
import type * as vite from 'vite';
|
||||
|
||||
|
@ -13,10 +13,10 @@ export declare interface Alias {
|
|||
const normalize = (pathname: string) => String(pathname).split(path.sep).join(path.posix.sep);
|
||||
|
||||
/** Returns a list of compiled aliases. */
|
||||
const getConfigAlias = (astroConfig: AstroConfig): Alias[] | null => {
|
||||
const getConfigAlias = (settings: AstroSettings): Alias[] | null => {
|
||||
/** Closest tsconfig.json or jsconfig.json */
|
||||
const config = astroConfig._ctx.tsConfig;
|
||||
const configPath = astroConfig._ctx.tsConfigPath;
|
||||
const config = settings.tsConfig;
|
||||
const configPath = settings.tsConfigPath;
|
||||
|
||||
// if no config was found, return null
|
||||
if (!config || !configPath) return null;
|
||||
|
@ -77,12 +77,13 @@ const getConfigAlias = (astroConfig: AstroConfig): Alias[] | null => {
|
|||
|
||||
/** Returns a Vite plugin used to alias pathes from tsconfig.json and jsconfig.json. */
|
||||
export default function configAliasVitePlugin({
|
||||
config: astroConfig,
|
||||
settings,
|
||||
}: {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
}): vite.PluginOption {
|
||||
const { config } = settings;
|
||||
/** Aliases from the tsconfig.json or jsconfig.json configuration. */
|
||||
const configAlias = getConfigAlias(astroConfig);
|
||||
const configAlias = getConfigAlias(settings);
|
||||
|
||||
// if no config alias was found, bypass this plugin
|
||||
if (!configAlias) return {} as vite.PluginOption;
|
||||
|
|
|
@ -2,10 +2,10 @@ import MagicString from 'magic-string';
|
|||
import { fileURLToPath } from 'url';
|
||||
import type * as vite from 'vite';
|
||||
import { loadEnv } from 'vite';
|
||||
import type { AstroConfig } from '../@types/astro';
|
||||
import type { AstroConfig, AstroSettings } from '../@types/astro';
|
||||
|
||||
interface EnvPluginOptions {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
}
|
||||
|
||||
function getPrivateEnv(viteConfig: vite.ResolvedConfig, astroConfig: AstroConfig) {
|
||||
|
@ -51,12 +51,13 @@ function getReferencedPrivateKeys(source: string, privateEnv: Record<string, any
|
|||
}
|
||||
|
||||
export default function envVitePlugin({
|
||||
config: astroConfig,
|
||||
settings,
|
||||
}: EnvPluginOptions): vite.PluginOption {
|
||||
let privateEnv: Record<string, any> | null;
|
||||
let config: vite.ResolvedConfig;
|
||||
let replacements: Record<string, string>;
|
||||
let pattern: RegExp | undefined;
|
||||
const { config: astroConfig } = settings;
|
||||
return {
|
||||
name: 'astro:vite-plugin-env',
|
||||
enforce: 'pre',
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import { Plugin as VitePlugin } from 'vite';
|
||||
import { AstroConfig } from '../@types/astro.js';
|
||||
import { AstroSettings } from '../@types/astro.js';
|
||||
import { LogOptions } from '../core/logger/core.js';
|
||||
import { runHookServerSetup } from '../integrations/index.js';
|
||||
|
||||
/** Connect Astro integrations into Vite, as needed. */
|
||||
export default function astroIntegrationsContainerPlugin({
|
||||
config,
|
||||
settings,
|
||||
logging,
|
||||
}: {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
}): VitePlugin {
|
||||
return {
|
||||
name: 'astro:integration-container',
|
||||
configureServer(server) {
|
||||
runHookServerSetup({ config, server, logging });
|
||||
runHookServerSetup({ config: settings.config, server, logging });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { TransformResult } from 'rollup';
|
||||
import type { TsConfigJson } from 'tsconfig-resolver';
|
||||
import type { Plugin, ResolvedConfig } from 'vite';
|
||||
import type { AstroConfig, AstroRenderer } from '../@types/astro';
|
||||
import type { AstroSettings, AstroRenderer } from '../@types/astro';
|
||||
import type { LogOptions } from '../core/logger/core.js';
|
||||
import type { PluginMetadata } from '../vite-plugin-astro/types';
|
||||
|
||||
|
@ -152,12 +152,12 @@ async function transformJSX({
|
|||
}
|
||||
|
||||
interface AstroPluginJSXOptions {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
/** Use Astro config to allow for alternate or multiple JSX renderers (by default Vite will assume React) */
|
||||
export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin {
|
||||
export default function jsx({ settings, logging }: AstroPluginJSXOptions): Plugin {
|
||||
let viteConfig: ResolvedConfig;
|
||||
const jsxRenderers = new Map<string, AstroRenderer>();
|
||||
const jsxRenderersIntegrationOnly = new Map<string, AstroRenderer>();
|
||||
|
@ -172,7 +172,7 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin
|
|||
enforce: 'pre', // run transforms before other plugins
|
||||
async configResolved(resolvedConfig) {
|
||||
viteConfig = resolvedConfig;
|
||||
const possibleRenderers = collectJSXRenderers(config._ctx.renderers);
|
||||
const possibleRenderers = collectJSXRenderers(settings.renderers);
|
||||
for (const [importSource, renderer] of possibleRenderers) {
|
||||
jsxRenderers.set(importSource, renderer);
|
||||
if (importSource === 'astro') {
|
||||
|
@ -230,7 +230,7 @@ export default function jsx({ config, logging }: AstroPluginJSXOptions): Plugin
|
|||
|
||||
// Check the tsconfig
|
||||
if (!importSource) {
|
||||
const compilerOptions = config._ctx.tsConfig?.compilerOptions;
|
||||
const compilerOptions = settings.tsConfig?.compilerOptions;
|
||||
importSource = (compilerOptions as FixedCompilerOptions | undefined)?.jsxImportSource;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import fs from 'fs';
|
|||
import matter from 'gray-matter';
|
||||
import { fileURLToPath } from 'url';
|
||||
import type { Plugin, ViteDevServer } from 'vite';
|
||||
import type { AstroConfig } from '../@types/astro';
|
||||
import type { AstroSettings } from '../@types/astro';
|
||||
import { pagesVirtualModuleId } from '../core/app/index.js';
|
||||
import { cachedCompilation, CompileProps } from '../core/compile/index.js';
|
||||
import { collectErrorMetadata } from '../core/errors.js';
|
||||
|
@ -19,7 +19,7 @@ import {
|
|||
} from '../vite-style-transform/index.js';
|
||||
|
||||
interface AstroPluginOptions {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,8 @@ function safeMatter(source: string, id: string) {
|
|||
// TODO: Clean up some of the shared logic between this Markdown plugin and the Astro plugin.
|
||||
// Both end up connecting a `load()` hook to the Astro compiler, and share some copy-paste
|
||||
// logic in how that is done.
|
||||
export default function markdown({ config, logging }: AstroPluginOptions): Plugin {
|
||||
export default function markdown({ settings }: AstroPluginOptions): Plugin {
|
||||
const { config } = settings;
|
||||
function normalizeFilename(filename: string) {
|
||||
if (filename.startsWith('/@fs')) {
|
||||
filename = filename.slice('/@fs'.length);
|
||||
|
|
|
@ -2,7 +2,7 @@ import { renderMarkdown } from '@astrojs/markdown-remark';
|
|||
import fs from 'fs';
|
||||
import matter from 'gray-matter';
|
||||
import type { Plugin } from 'vite';
|
||||
import type { AstroConfig } from '../@types/astro';
|
||||
import type { AstroSettings } from '../@types/astro';
|
||||
import { collectErrorMetadata } from '../core/errors.js';
|
||||
import type { LogOptions } from '../core/logger/core.js';
|
||||
import { warn } from '../core/logger/core.js';
|
||||
|
@ -10,7 +10,7 @@ import type { PluginMetadata } from '../vite-plugin-astro/types.js';
|
|||
import { getFileInfo, safelyGetAstroData } from '../vite-plugin-utils/index.js';
|
||||
|
||||
interface AstroPluginOptions {
|
||||
config: AstroConfig;
|
||||
settings: AstroSettings;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ function safeMatter(source: string, id: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export default function markdown({ config, logging }: AstroPluginOptions): Plugin {
|
||||
export default function markdown({ settings, logging }: AstroPluginOptions): Plugin {
|
||||
return {
|
||||
enforce: 'pre',
|
||||
name: 'astro:markdown',
|
||||
|
@ -33,11 +33,11 @@ export default function markdown({ config, logging }: AstroPluginOptions): Plugi
|
|||
// to escape "import.meta.env" ourselves.
|
||||
async load(id) {
|
||||
if (id.endsWith('.md')) {
|
||||
const { fileId, fileUrl } = getFileInfo(id, config);
|
||||
const { fileId, fileUrl } = getFileInfo(id, settings.config);
|
||||
const rawFile = await fs.promises.readFile(fileId, 'utf-8');
|
||||
const raw = safeMatter(rawFile, id);
|
||||
const renderResult = await renderMarkdown(raw.content, {
|
||||
...config.markdown,
|
||||
...settings.config.markdown,
|
||||
fileURL: new URL(`file://${fileId}`),
|
||||
isAstroFlavoredMd: false,
|
||||
} as any);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ConfigEnv, Plugin as VitePlugin } from 'vite';
|
||||
import { AstroConfig, InjectedScriptStage } from '../@types/astro.js';
|
||||
import { AstroSettings, InjectedScriptStage } from '../@types/astro.js';
|
||||
|
||||
// NOTE: We can't use the virtual "\0" ID convention because we need to
|
||||
// inject these as ESM imports into actual code, where they would not
|
||||
|
@ -11,7 +11,7 @@ export const BEFORE_HYDRATION_SCRIPT_ID = `${SCRIPT_ID_PREFIX}${
|
|||
export const PAGE_SCRIPT_ID = `${SCRIPT_ID_PREFIX}${'page' as InjectedScriptStage}.js`;
|
||||
export const PAGE_SSR_SCRIPT_ID = `${SCRIPT_ID_PREFIX}${'page-ssr' as InjectedScriptStage}.js`;
|
||||
|
||||
export default function astroScriptsPlugin({ config }: { config: AstroConfig }): VitePlugin {
|
||||
export default function astroScriptsPlugin({ settings }: { settings: AstroSettings }): VitePlugin {
|
||||
let env: ConfigEnv | undefined = undefined;
|
||||
return {
|
||||
name: 'astro:scripts',
|
||||
|
@ -29,19 +29,19 @@ export default function astroScriptsPlugin({ config }: { config: AstroConfig }):
|
|||
|
||||
async load(id) {
|
||||
if (id === BEFORE_HYDRATION_SCRIPT_ID) {
|
||||
return config._ctx.scripts
|
||||
return settings.scripts
|
||||
.filter((s) => s.stage === 'before-hydration')
|
||||
.map((s) => s.content)
|
||||
.join('\n');
|
||||
}
|
||||
if (id === PAGE_SCRIPT_ID) {
|
||||
return config._ctx.scripts
|
||||
return settings.scripts
|
||||
.filter((s) => s.stage === 'page')
|
||||
.map((s) => s.content)
|
||||
.join('\n');
|
||||
}
|
||||
if (id === PAGE_SSR_SCRIPT_ID) {
|
||||
return config._ctx.scripts
|
||||
return settings.scripts
|
||||
.filter((s) => s.stage === 'page-ssr')
|
||||
.map((s) => s.content)
|
||||
.join('\n');
|
||||
|
@ -49,7 +49,7 @@ export default function astroScriptsPlugin({ config }: { config: AstroConfig }):
|
|||
return null;
|
||||
},
|
||||
buildStart(options) {
|
||||
const hasHydrationScripts = config._ctx.scripts.some((s) => s.stage === 'before-hydration');
|
||||
const hasHydrationScripts = settings.scripts.some((s) => s.stage === 'before-hydration');
|
||||
if (hasHydrationScripts && env?.command === 'build' && !env?.ssrBuild) {
|
||||
this.emitFile({
|
||||
type: 'chunk',
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { Plugin as VitePlugin } from 'vite';
|
||||
import { AstroConfig } from '../@types/astro.js';
|
||||
import { AstroSettings } from '../@types/astro.js';
|
||||
import { PAGE_SSR_SCRIPT_ID } from './index.js';
|
||||
|
||||
import ancestor from 'common-ancestor-path';
|
||||
import MagicString from 'magic-string';
|
||||
import { isPage } from '../core/util.js';
|
||||
|
||||
export default function astroScriptsPostPlugin({ config }: { config: AstroConfig }): VitePlugin {
|
||||
export default function astroScriptsPostPlugin({ settings }: { settings: AstroSettings }): VitePlugin {
|
||||
function normalizeFilename(filename: string) {
|
||||
if (filename.startsWith('/@fs')) {
|
||||
filename = filename.slice('/@fs'.length);
|
||||
} else if (filename.startsWith('/') && !ancestor(filename, config.root.pathname)) {
|
||||
filename = new URL('.' + filename, config.root).pathname;
|
||||
} else if (filename.startsWith('/') && !ancestor(filename, settings.config.root.pathname)) {
|
||||
filename = new URL('.' + filename, settings.config.root).pathname;
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export default function astroScriptsPostPlugin({ config }: { config: AstroConfig
|
|||
transform(this, code, id, options) {
|
||||
if (!options?.ssr) return;
|
||||
|
||||
const hasInjectedScript = config._ctx.scripts.some((s) => s.stage === 'page-ssr');
|
||||
const hasInjectedScript = settings.scripts.some((s) => s.stage === 'page-ssr');
|
||||
if (!hasInjectedScript) return;
|
||||
|
||||
const filename = normalizeFilename(id);
|
||||
|
@ -35,7 +35,7 @@ export default function astroScriptsPostPlugin({ config }: { config: AstroConfig
|
|||
return;
|
||||
}
|
||||
|
||||
const fileIsPage = isPage(fileURL, config);
|
||||
const fileIsPage = isPage(fileURL, settings);
|
||||
if (!fileIsPage) return;
|
||||
|
||||
const s = new MagicString(code, { filename });
|
||||
|
|
|
@ -3,7 +3,7 @@ import { load as cheerioLoad } from 'cheerio';
|
|||
import { loadFixture } from './test-utils.js';
|
||||
import testAdapter from './test-adapter.js';
|
||||
|
||||
describe('AstroConfig - config.mode', () => {
|
||||
describe('AstroConfig - config.output', () => {
|
||||
describe(`output: 'server'`, () => {
|
||||
describe('deploy config provided', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
|
@ -58,7 +58,7 @@ describe('AstroConfig - config.mode', () => {
|
|||
});
|
||||
|
||||
describe(`output: 'static'`, () => {
|
||||
describe('Deploy config omitted', () => {
|
||||
describe('Output config omitted', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
import { expect } from 'chai';
|
||||
import { loadFixture, cliServerLogSetup } from './test-utils.js';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { isIPv4 } from 'net';
|
||||
|
||||
describe('config', () => {
|
||||
let hostFixture;
|
||||
let portFixture;
|
||||
|
||||
before(async () => {
|
||||
[hostFixture, portFixture] = await Promise.all([
|
||||
loadFixture({
|
||||
root: './fixtures/config-host/',
|
||||
server: {
|
||||
host: true,
|
||||
},
|
||||
}),
|
||||
loadFixture({
|
||||
root: './fixtures/config-host/',
|
||||
server: {
|
||||
port: 5006,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
describe('host', () => {
|
||||
it('can be specified in astro.config.mjs', async () => {
|
||||
expect(hostFixture.config.server.host).to.equal(true);
|
||||
});
|
||||
|
||||
it('can be specified via --host flag', async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const { network } = await cliServerLogSetup([
|
||||
'--root',
|
||||
fileURLToPath(projectRootURL),
|
||||
'--host',
|
||||
]);
|
||||
|
||||
const networkURL = new URL(network);
|
||||
expect(isIPv4(networkURL.hostname)).to.be.equal(
|
||||
true,
|
||||
`Expected network URL to respect --host flag`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('path', () => {
|
||||
it('can be passed via --config', async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = new URL('./fixtures/config-path/config/my-config.mjs', import.meta.url);
|
||||
const { network } = await cliServerLogSetup([
|
||||
'--root',
|
||||
fileURLToPath(projectRootURL),
|
||||
'--config',
|
||||
configFileURL.pathname,
|
||||
]);
|
||||
|
||||
const networkURL = new URL(network);
|
||||
expect(isIPv4(networkURL.hostname)).to.be.equal(
|
||||
true,
|
||||
`Expected network URL to respect --host flag`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('relative path', () => {
|
||||
it('can be passed via relative --config', async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = 'my-config.mjs';
|
||||
const { local } = await cliServerLogSetup([
|
||||
'--root',
|
||||
fileURLToPath(projectRootURL),
|
||||
'--config',
|
||||
configFileURL,
|
||||
]);
|
||||
|
||||
const localURL = new URL(local);
|
||||
expect(localURL.port).to.equal('8080');
|
||||
});
|
||||
});
|
||||
|
||||
describe('relative path with leading ./', () => {
|
||||
it('can be passed via relative --config', async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = './my-config.mjs';
|
||||
const { local } = await cliServerLogSetup([
|
||||
'--root',
|
||||
fileURLToPath(projectRootURL),
|
||||
'--config',
|
||||
configFileURL,
|
||||
]);
|
||||
|
||||
const localURL = new URL(local);
|
||||
expect(localURL.port).to.equal('8080');
|
||||
});
|
||||
});
|
||||
|
||||
describe('incorrect path', () => {
|
||||
it('fails and exits when config does not exist', async () => {
|
||||
const projectRootURL = new URL('./fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = './does-not-exist.mjs';
|
||||
let exit = 0;
|
||||
try {
|
||||
await cliServerLogSetup([
|
||||
'--root',
|
||||
fileURLToPath(projectRootURL),
|
||||
'--config',
|
||||
configFileURL,
|
||||
]);
|
||||
} catch (e) {
|
||||
if (e.message.includes('Unable to resolve --config')) {
|
||||
exit = 1;
|
||||
}
|
||||
}
|
||||
|
||||
expect(exit).to.equal(1, 'Throws helpful error message when --config does not exist');
|
||||
});
|
||||
});
|
||||
|
||||
describe('port', () => {
|
||||
it('can be specified in astro.config.mjs', async () => {
|
||||
expect(portFixture.config.server.port).to.deep.equal(5006);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,7 +2,8 @@ import { execa } from 'execa';
|
|||
import { polyfill } from '@astrojs/webapi';
|
||||
import fs from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { loadConfig } from '../dist/core/config.js';
|
||||
import { loadConfig } from '../dist/core/config/config.js';
|
||||
import { createSettings, loadTSConfig } from '../dist/core/config/index.js';
|
||||
import dev from '../dist/core/dev/index.js';
|
||||
import build from '../dist/core/build/index.js';
|
||||
import preview from '../dist/core/preview/index.js';
|
||||
|
@ -18,7 +19,7 @@ polyfill(globalThis, {
|
|||
|
||||
/**
|
||||
* @typedef {import('node-fetch').Response} Response
|
||||
* @typedef {import('../src/core/dev/index').DevServer} DevServer
|
||||
* @typedef {import('../src/core/dev/index').DedvServer} DevServer
|
||||
* @typedef {import('../src/@types/astro').AstroConfig} AstroConfig
|
||||
* @typedef {import('../src/core/preview/index').PreviewServer} PreviewServer
|
||||
* @typedef {import('../src/core/app/index').App} App
|
||||
|
@ -39,6 +40,12 @@ polyfill(globalThis, {
|
|||
* @property {() => Promise<void>} onNextChange
|
||||
*/
|
||||
|
||||
/** @type {import('../src/core/logger/core').LogOptions} */
|
||||
export const defaultLogging = {
|
||||
dest: nodeLogDestination,
|
||||
level: 'error',
|
||||
};
|
||||
|
||||
/**
|
||||
* Load Astro fixture
|
||||
* @param {AstroConfig} inlineConfig Astro config partial (note: must specify `root`)
|
||||
|
@ -75,10 +82,7 @@ export async function loadFixture(inlineConfig) {
|
|||
}
|
||||
|
||||
/** @type {import('../src/core/logger/core').LogOptions} */
|
||||
const logging = {
|
||||
dest: nodeLogDestination,
|
||||
level: 'error',
|
||||
};
|
||||
const logging = defaultLogging;
|
||||
|
||||
// Load the config.
|
||||
let config = await loadConfig({ cwd: fileURLToPath(cwd), logging });
|
||||
|
@ -91,10 +95,16 @@ export async function loadFixture(inlineConfig) {
|
|||
if (inlineConfig.base && !inlineConfig.base.endsWith('/')) {
|
||||
config.base = inlineConfig.base + '/';
|
||||
}
|
||||
let tsconfig = loadTSConfig(fileURLToPath(cwd));
|
||||
let settings = createSettings({
|
||||
config,
|
||||
tsConfig: tsconfig?.config,
|
||||
tsConfigPath: tsconfig?.path
|
||||
});
|
||||
if (config.integrations.find((integration) => integration.name === '@astrojs/mdx')) {
|
||||
// Enable default JSX integration. It needs to come first, so unshift rather than push!
|
||||
const { default: jsxRenderer } = await import('astro/jsx/renderer.js');
|
||||
config._ctx.renderers.unshift(jsxRenderer);
|
||||
settings.renderers.unshift(jsxRenderer);
|
||||
}
|
||||
|
||||
/** @type {import('@astrojs/telemetry').AstroTelemetry} */
|
||||
|
@ -133,9 +143,9 @@ export async function loadFixture(inlineConfig) {
|
|||
let devServer;
|
||||
|
||||
return {
|
||||
build: (opts = {}) => build(config, { logging, telemetry, ...opts }),
|
||||
build: (opts = {}) => build(settings, { logging, telemetry, ...opts }),
|
||||
startDevServer: async (opts = {}) => {
|
||||
devServer = await dev(config, { logging, telemetry, ...opts });
|
||||
devServer = await dev(settings, { logging, telemetry, ...opts });
|
||||
config.server.port = devServer.address.port; // update port
|
||||
return devServer;
|
||||
},
|
||||
|
@ -143,7 +153,7 @@ export async function loadFixture(inlineConfig) {
|
|||
resolveUrl,
|
||||
fetch: (url, init) => fetch(resolveUrl(url), init),
|
||||
preview: async (opts = {}) => {
|
||||
const previewServer = await preview(config, { logging, telemetry, ...opts });
|
||||
const previewServer = await preview(settings, { logging, telemetry, ...opts });
|
||||
return previewServer;
|
||||
},
|
||||
readFile: (filePath, encoding) =>
|
||||
|
|
71
packages/astro/test/units/config/config-server.test.js
Normal file
71
packages/astro/test/units/config/config-server.test.js
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { expect } from 'chai';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { defaultLogging as logging } from '../../test-utils.js';
|
||||
import { openConfig } from '../../../dist/core/config/index.js';
|
||||
|
||||
const cwd = fileURLToPath(new URL('../../fixtures/config-host/', import.meta.url));
|
||||
|
||||
describe('config.server', () => {
|
||||
function openConfigWithFlags(flags) {
|
||||
return openConfig({
|
||||
cwd: flags.root || cwd,
|
||||
flags,
|
||||
cmd: 'dev',
|
||||
logging
|
||||
});
|
||||
}
|
||||
|
||||
describe('host', () => {
|
||||
it('can be specified via --host flag', async () => {
|
||||
const projectRootURL = new URL('../../fixtures/astro-basic/', import.meta.url);
|
||||
const { astroConfig } = await openConfigWithFlags({
|
||||
root: fileURLToPath(projectRootURL),
|
||||
host: true
|
||||
});
|
||||
|
||||
expect(astroConfig.server.host).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('config', () => {
|
||||
describe('relative path', () => {
|
||||
it('can be passed via relative --config', async () => {
|
||||
const projectRootURL = new URL('../../fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = 'my-config.mjs';
|
||||
const { astroConfig } = await openConfigWithFlags({
|
||||
root: fileURLToPath(projectRootURL),
|
||||
config: configFileURL
|
||||
});
|
||||
expect(astroConfig.server.port).to.equal(8080);
|
||||
});
|
||||
});
|
||||
|
||||
describe('relative path with leading ./', () => {
|
||||
it('can be passed via relative --config', async () => {
|
||||
const projectRootURL = new URL('../../fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = './my-config.mjs';
|
||||
const { astroConfig } = await openConfigWithFlags({
|
||||
root: fileURLToPath(projectRootURL),
|
||||
config: configFileURL
|
||||
});
|
||||
expect(astroConfig.server.port).to.equal(8080);
|
||||
});
|
||||
});
|
||||
|
||||
describe('incorrect path', () => {
|
||||
it('fails and exits when config does not exist', async () => {
|
||||
const projectRootURL = new URL('../../fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = './does-not-exist.mjs';
|
||||
try {
|
||||
await openConfigWithFlags({
|
||||
root: fileURLToPath(projectRootURL),
|
||||
config: configFileURL
|
||||
});
|
||||
expect(false).to.equal(true, 'this should not have resolved');
|
||||
} catch(err) {
|
||||
expect(err.message).to.match(/Unable to resolve/);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
|
@ -1,8 +1,8 @@
|
|||
import { expect } from 'chai';
|
||||
import { z } from 'zod';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import { formatConfigErrorMessage } from '../dist/core/messages.js';
|
||||
import { validateConfig } from '../dist/core/config.js';
|
||||
import { formatConfigErrorMessage } from '../../../dist/core/messages.js';
|
||||
import { validateConfig } from '../../../dist/core/config/index.js';
|
||||
|
||||
describe('Config Validation', () => {
|
||||
it('empty user config is valid', async () => {
|
Loading…
Reference in a new issue