Refactor and improve Astro config loading flow (#7879)
This commit is contained in:
parent
9e22038472
commit
ebf7ebbf7a
57 changed files with 740 additions and 825 deletions
5
.changeset/twenty-oranges-poke.md
Normal file
5
.changeset/twenty-oranges-poke.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Refactor and improve Astro config loading flow
|
|
@ -1,5 +1,5 @@
|
|||
import { expect } from '@playwright/test';
|
||||
import { getErrorOverlayContent, silentLogging, testFactory } from './test-utils.js';
|
||||
import { getErrorOverlayContent, testFactory } from './test-utils.js';
|
||||
|
||||
const test = testFactory({
|
||||
root: './fixtures/errors/',
|
||||
|
@ -12,10 +12,7 @@ const test = testFactory({
|
|||
let devServer;
|
||||
|
||||
test.beforeAll(async ({ astro }) => {
|
||||
devServer = await astro.startDevServer({
|
||||
// Only test the error overlay, don't print to console
|
||||
logging: silentLogging,
|
||||
});
|
||||
devServer = await astro.startDevServer();
|
||||
});
|
||||
|
||||
test.afterAll(async ({ astro }) => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { expect, test as testBase } from '@playwright/test';
|
||||
import fs from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { loadFixture as baseLoadFixture } from '../test/test-utils.js';
|
||||
|
||||
export const isWindows = process.platform === 'win32';
|
||||
|
@ -24,7 +25,7 @@ export function loadFixture(inlineConfig) {
|
|||
// without this, the main `loadFixture` helper will resolve relative to `packages/astro/test`
|
||||
return baseLoadFixture({
|
||||
...inlineConfig,
|
||||
root: new URL(inlineConfig.root, import.meta.url).toString(),
|
||||
root: fileURLToPath(new URL(inlineConfig.root, import.meta.url)),
|
||||
server: {
|
||||
port: testFileToPort.get(path.basename(inlineConfig.root)),
|
||||
},
|
||||
|
|
|
@ -19,7 +19,7 @@ import type { PageBuildData } from '../core/build/types';
|
|||
import type { AstroConfigSchema } from '../core/config';
|
||||
import type { AstroTimer } from '../core/config/timer';
|
||||
import type { AstroCookies } from '../core/cookies';
|
||||
import type { LogOptions } from '../core/logger/core';
|
||||
import type { LogOptions, LoggerLevel } from '../core/logger/core';
|
||||
import type { AstroComponentFactory, AstroComponentInstance } from '../runtime/server';
|
||||
import type { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../core/constants.js';
|
||||
export type {
|
||||
|
@ -1331,6 +1331,16 @@ export interface AstroConfig extends z.output<typeof AstroConfigSchema> {
|
|||
// TypeScript still confirms zod validation matches this type.
|
||||
integrations: AstroIntegration[];
|
||||
}
|
||||
export interface AstroInlineConfig extends AstroUserConfig, AstroInlineOnlyConfig {}
|
||||
export interface AstroInlineOnlyConfig {
|
||||
configFile?: string | false;
|
||||
mode?: RuntimeMode;
|
||||
logLevel?: LoggerLevel;
|
||||
/**
|
||||
* @internal for testing only
|
||||
*/
|
||||
logging?: LogOptions;
|
||||
}
|
||||
|
||||
export type ContentEntryModule = {
|
||||
id: string;
|
||||
|
|
|
@ -9,7 +9,7 @@ import ora from 'ora';
|
|||
import preferredPM from 'preferred-pm';
|
||||
import prompts from 'prompts';
|
||||
import type yargs from 'yargs-parser';
|
||||
import { loadTSConfig, resolveConfigPath } from '../../core/config/index.js';
|
||||
import { loadTSConfig, resolveConfigPath, resolveRoot } from '../../core/config/index.js';
|
||||
import {
|
||||
defaultTSConfig,
|
||||
presets,
|
||||
|
@ -23,12 +23,12 @@ import { appendForwardSlash } from '../../core/path.js';
|
|||
import { apply as applyPolyfill } from '../../core/polyfill.js';
|
||||
import { parseNpmName } from '../../core/util.js';
|
||||
import { eventCliSession, telemetry } from '../../events/index.js';
|
||||
import { createLoggingFromFlags } from '../flags.js';
|
||||
import { generate, parse, t, visit } from './babel.js';
|
||||
import { ensureImport } from './imports.js';
|
||||
import { wrapDefaultExport } from './wrapper.js';
|
||||
|
||||
interface AddOptions {
|
||||
logging: LogOptions;
|
||||
flags: yargs.Arguments;
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,7 @@ async function getRegistry(): Promise<string> {
|
|||
}
|
||||
}
|
||||
|
||||
export async function add(names: string[], { flags, logging }: AddOptions) {
|
||||
export async function add(names: string[], { flags }: AddOptions) {
|
||||
telemetry.record(eventCliSession('add'));
|
||||
applyPolyfill();
|
||||
if (flags.help || names.length === 0) {
|
||||
|
@ -130,10 +130,12 @@ export async function add(names: string[], { flags, logging }: AddOptions) {
|
|||
|
||||
// Some packages might have a common alias! We normalize those here.
|
||||
const cwd = flags.root;
|
||||
const logging = createLoggingFromFlags(flags);
|
||||
const integrationNames = names.map((name) => (ALIASES.has(name) ? ALIASES.get(name)! : name));
|
||||
const integrations = await validateIntegrations(integrationNames);
|
||||
let installResult = await tryToInstallIntegrations({ integrations, cwd, flags, logging });
|
||||
const root = pathToFileURL(cwd ? path.resolve(cwd) : process.cwd());
|
||||
const rootPath = resolveRoot(cwd);
|
||||
const root = pathToFileURL(rootPath);
|
||||
// Append forward slash to compute relative paths
|
||||
root.href = appendForwardSlash(root.href);
|
||||
|
||||
|
@ -199,7 +201,11 @@ export async function add(names: string[], { flags, logging }: AddOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
const rawConfigPath = await resolveConfigPath({ cwd, flags, fs: fsMod });
|
||||
const rawConfigPath = await resolveConfigPath({
|
||||
root: rootPath,
|
||||
configFile: flags.config,
|
||||
fs: fsMod,
|
||||
});
|
||||
let configURL = rawConfigPath ? pathToFileURL(rawConfigPath) : undefined;
|
||||
|
||||
if (configURL) {
|
||||
|
|
|
@ -1,21 +1,31 @@
|
|||
import type yargs from 'yargs-parser';
|
||||
import _build from '../../core/build/index.js';
|
||||
import type { LogOptions } from '../../core/logger/core.js';
|
||||
import { loadSettings } from '../load-settings.js';
|
||||
import { printHelp } from '../../core/messages.js';
|
||||
import { flagsToAstroInlineConfig } from '../flags.js';
|
||||
|
||||
interface BuildOptions {
|
||||
flags: yargs.Arguments;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
export async function build({ flags, logging }: BuildOptions) {
|
||||
const settings = await loadSettings({ cmd: 'build', flags, logging });
|
||||
if (!settings) return;
|
||||
export async function build({ flags }: BuildOptions) {
|
||||
if (flags?.help || flags?.h) {
|
||||
printHelp({
|
||||
commandName: 'astro build',
|
||||
usage: '[...flags]',
|
||||
tables: {
|
||||
Flags: [
|
||||
['--drafts', `Include Markdown draft pages in the build.`],
|
||||
['--help (-h)', 'See all available flags.'],
|
||||
],
|
||||
},
|
||||
description: `Builds your site for deployment.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await _build(settings, {
|
||||
flags,
|
||||
logging,
|
||||
const inlineConfig = flagsToAstroInlineConfig(flags);
|
||||
|
||||
await _build(inlineConfig, {
|
||||
teardownCompiler: true,
|
||||
mode: flags.mode,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -13,11 +13,16 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|||
import ora from 'ora';
|
||||
import type { Arguments as Flags } from 'yargs-parser';
|
||||
import type { AstroSettings } from '../../@types/astro';
|
||||
import { resolveConfig } from '../../core/config/config.js';
|
||||
import { createNodeLogging } from '../../core/config/logging.js';
|
||||
import { createSettings } from '../../core/config/settings.js';
|
||||
import type { LogOptions } from '../../core/logger/core.js';
|
||||
import { debug, info } from '../../core/logger/core.js';
|
||||
import { printHelp } from '../../core/messages.js';
|
||||
import type { ProcessExit, SyncOptions } from '../../core/sync';
|
||||
import { loadSettings } from '../load-settings.js';
|
||||
import type { syncInternal } from '../../core/sync';
|
||||
import { eventCliSession, telemetry } from '../../events/index.js';
|
||||
import { runHookConfigSetup } from '../../integrations/index.js';
|
||||
import { flagsToAstroInlineConfig } from '../flags.js';
|
||||
import { printDiagnostic } from './print.js';
|
||||
|
||||
type DiagnosticResult = {
|
||||
|
@ -31,11 +36,6 @@ export type CheckPayload = {
|
|||
* Flags passed via CLI
|
||||
*/
|
||||
flags: Flags;
|
||||
|
||||
/**
|
||||
* Logging options
|
||||
*/
|
||||
logging: LogOptions;
|
||||
};
|
||||
|
||||
type CheckFlags = {
|
||||
|
@ -77,9 +77,8 @@ const ASTRO_GLOB_PATTERN = '**/*.astro';
|
|||
*
|
||||
* @param {CheckPayload} options Options passed {@link AstroChecker}
|
||||
* @param {Flags} options.flags Flags coming from the CLI
|
||||
* @param {LogOptions} options.logging Logging options
|
||||
*/
|
||||
export async function check({ logging, flags }: CheckPayload): Promise<AstroChecker | undefined> {
|
||||
export async function check({ flags }: CheckPayload): Promise<AstroChecker | undefined> {
|
||||
if (flags.help || flags.h) {
|
||||
printHelp({
|
||||
commandName: 'astro check',
|
||||
|
@ -95,8 +94,12 @@ export async function check({ logging, flags }: CheckPayload): Promise<AstroChec
|
|||
return;
|
||||
}
|
||||
|
||||
const settings = await loadSettings({ cmd: 'check', flags, logging });
|
||||
if (!settings) return;
|
||||
// Load settings
|
||||
const inlineConfig = flagsToAstroInlineConfig(flags);
|
||||
const logging = createNodeLogging(inlineConfig);
|
||||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig, 'check');
|
||||
telemetry.record(eventCliSession('check', userConfig, flags));
|
||||
const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const checkFlags = parseFlags(flags);
|
||||
if (checkFlags.watch) {
|
||||
|
@ -105,7 +108,7 @@ export async function check({ logging, flags }: CheckPayload): Promise<AstroChec
|
|||
info(logging, 'check', 'Checking files');
|
||||
}
|
||||
|
||||
const { syncCli } = await import('../../core/sync/index.js');
|
||||
const { syncInternal } = await import('../../core/sync/index.js');
|
||||
const root = settings.config.root;
|
||||
const require = createRequire(import.meta.url);
|
||||
const diagnosticChecker = new AstroCheck(
|
||||
|
@ -116,7 +119,7 @@ export async function check({ logging, flags }: CheckPayload): Promise<AstroChec
|
|||
);
|
||||
|
||||
return new AstroChecker({
|
||||
syncCli,
|
||||
syncInternal,
|
||||
settings,
|
||||
fileSystem: fs,
|
||||
logging,
|
||||
|
@ -130,7 +133,7 @@ type CheckerConstructor = {
|
|||
|
||||
isWatchMode: boolean;
|
||||
|
||||
syncCli: (settings: AstroSettings, options: SyncOptions) => Promise<ProcessExit>;
|
||||
syncInternal: typeof syncInternal;
|
||||
|
||||
settings: Readonly<AstroSettings>;
|
||||
|
||||
|
@ -148,7 +151,7 @@ type CheckerConstructor = {
|
|||
export class AstroChecker {
|
||||
readonly #diagnosticsChecker: AstroCheck;
|
||||
readonly #shouldWatch: boolean;
|
||||
readonly #syncCli: (settings: AstroSettings, opts: SyncOptions) => Promise<ProcessExit>;
|
||||
readonly #syncInternal: CheckerConstructor['syncInternal'];
|
||||
|
||||
readonly #settings: AstroSettings;
|
||||
|
||||
|
@ -162,14 +165,14 @@ export class AstroChecker {
|
|||
constructor({
|
||||
diagnosticChecker,
|
||||
isWatchMode,
|
||||
syncCli,
|
||||
syncInternal,
|
||||
settings,
|
||||
fileSystem,
|
||||
logging,
|
||||
}: CheckerConstructor) {
|
||||
this.#diagnosticsChecker = diagnosticChecker;
|
||||
this.#shouldWatch = isWatchMode;
|
||||
this.#syncCli = syncCli;
|
||||
this.#syncInternal = syncInternal;
|
||||
this.#logging = logging;
|
||||
this.#settings = settings;
|
||||
this.#fs = fileSystem;
|
||||
|
@ -223,7 +226,14 @@ export class AstroChecker {
|
|||
* @param openDocuments Whether the operation should open all `.astro` files
|
||||
*/
|
||||
async #checkAllFiles(openDocuments: boolean): Promise<CheckResult> {
|
||||
const processExit = await this.#syncCli(this.#settings, {
|
||||
// Run `astro:config:setup` before syncing to initialize integrations.
|
||||
// We do this manually as we're calling `syncInternal` directly.
|
||||
const syncSettings = await runHookConfigSetup({
|
||||
settings: this.#settings,
|
||||
logging: this.#logging,
|
||||
command: 'build',
|
||||
});
|
||||
const processExit = await this.#syncInternal(syncSettings, {
|
||||
logging: this.#logging,
|
||||
fs: this.#fs,
|
||||
});
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
import fs from 'node:fs';
|
||||
import { cyan } from 'kleur/colors';
|
||||
import type yargs from 'yargs-parser';
|
||||
import { resolveConfigPath, resolveFlags } from '../../core/config/index.js';
|
||||
import devServer from '../../core/dev/index.js';
|
||||
import { info, type LogOptions } from '../../core/logger/core.js';
|
||||
import { handleConfigError, loadSettings } from '../load-settings.js';
|
||||
import { printHelp } from '../../core/messages.js';
|
||||
import { flagsToAstroInlineConfig } from '../flags.js';
|
||||
|
||||
interface DevOptions {
|
||||
flags: yargs.Arguments;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
export async function dev({ flags, logging }: DevOptions) {
|
||||
const settings = await loadSettings({ cmd: 'dev', flags, logging });
|
||||
if (!settings) return;
|
||||
export async function dev({ flags }: DevOptions) {
|
||||
if (flags.help || flags.h) {
|
||||
printHelp({
|
||||
commandName: 'astro dev',
|
||||
usage: '[...flags]',
|
||||
tables: {
|
||||
Flags: [
|
||||
['--port', `Specify which port to run on. Defaults to 3000.`],
|
||||
['--host', `Listen on all addresses, including LAN and public addresses.`],
|
||||
['--host <custom-address>', `Expose on a network IP address at <custom-address>`],
|
||||
['--open', 'Automatically open the app in the browser on server start'],
|
||||
['--help (-h)', 'See all available flags.'],
|
||||
],
|
||||
},
|
||||
description: `Check ${cyan(
|
||||
'https://docs.astro.build/en/reference/cli-reference/#astro-dev'
|
||||
)} for more information.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const root = flags.root;
|
||||
const configFlag = resolveFlags(flags).config;
|
||||
const configFlagPath = configFlag ? await resolveConfigPath({ cwd: root, flags, fs }) : undefined;
|
||||
const inlineConfig = flagsToAstroInlineConfig(flags);
|
||||
|
||||
return await devServer(settings, {
|
||||
configFlag,
|
||||
configFlagPath,
|
||||
flags,
|
||||
logging,
|
||||
handleConfigError(e) {
|
||||
handleConfigError(e, { cmd: 'dev', cwd: root, flags, logging });
|
||||
info(logging, 'astro', 'Continuing with previous valid configuration\n');
|
||||
},
|
||||
});
|
||||
return await devServer(inlineConfig);
|
||||
}
|
||||
|
|
49
packages/astro/src/cli/flags.ts
Normal file
49
packages/astro/src/cli/flags.ts
Normal file
|
@ -0,0 +1,49 @@
|
|||
import type { Arguments as Flags } from 'yargs-parser';
|
||||
import type { AstroInlineConfig } from '../@types/astro.js';
|
||||
import type { LogOptions } from '../core/logger/core.js';
|
||||
import { nodeLogDestination } from '../core/logger/node.js';
|
||||
|
||||
export function flagsToAstroInlineConfig(flags: Flags): AstroInlineConfig {
|
||||
return {
|
||||
// Inline-only configs
|
||||
configFile: typeof flags.config === 'string' ? flags.config : undefined,
|
||||
mode: typeof flags.mode === 'string' ? (flags.mode as AstroInlineConfig['mode']) : undefined,
|
||||
logLevel: flags.verbose ? 'debug' : flags.silent ? 'silent' : undefined,
|
||||
|
||||
// Astro user configs
|
||||
root: typeof flags.root === 'string' ? flags.root : undefined,
|
||||
site: typeof flags.site === 'string' ? flags.site : undefined,
|
||||
base: typeof flags.base === 'string' ? flags.base : undefined,
|
||||
markdown: {
|
||||
drafts: typeof flags.drafts === 'boolean' ? flags.drafts : undefined,
|
||||
},
|
||||
server: {
|
||||
port: typeof flags.port === 'number' ? flags.port : undefined,
|
||||
host:
|
||||
typeof flags.host === 'string' || typeof flags.host === 'boolean' ? flags.host : undefined,
|
||||
open: typeof flags.open === 'boolean' ? flags.open : undefined,
|
||||
},
|
||||
experimental: {
|
||||
assets: typeof flags.experimentalAssets === 'boolean' ? flags.experimentalAssets : undefined,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The `logging` is usually created from an `AstroInlineConfig`, but some flows like `add`
|
||||
* doesn't read the AstroConfig directly, so we create a `logging` object from the CLI flags instead.
|
||||
*/
|
||||
export function createLoggingFromFlags(flags: Flags): LogOptions {
|
||||
const logging: LogOptions = {
|
||||
dest: nodeLogDestination,
|
||||
level: 'info',
|
||||
};
|
||||
|
||||
if (flags.verbose) {
|
||||
logging.level = 'debug';
|
||||
} else if (flags.silent) {
|
||||
logging.level = 'silent';
|
||||
}
|
||||
|
||||
return logging;
|
||||
}
|
|
@ -2,7 +2,6 @@
|
|||
import * as colors from 'kleur/colors';
|
||||
import yargs from 'yargs-parser';
|
||||
import { ASTRO_VERSION } from '../core/constants.js';
|
||||
import type { LogOptions } from '../core/logger/core.js';
|
||||
|
||||
type CLICommand =
|
||||
| 'help'
|
||||
|
@ -112,16 +111,10 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
}
|
||||
}
|
||||
|
||||
const { enableVerboseLogging, nodeLogDestination } = await import('../core/logger/node.js');
|
||||
const logging: LogOptions = {
|
||||
dest: nodeLogDestination,
|
||||
level: 'info',
|
||||
};
|
||||
// In verbose/debug mode, we log the debug logs asap before any potential errors could appear
|
||||
if (flags.verbose) {
|
||||
logging.level = 'debug';
|
||||
const { enableVerboseLogging } = await import('../core/logger/node.js');
|
||||
enableVerboseLogging();
|
||||
} else if (flags.silent) {
|
||||
logging.level = 'silent';
|
||||
}
|
||||
|
||||
// Start with a default NODE_ENV so Vite doesn't set an incorrect default when loading the Astro config
|
||||
|
@ -135,12 +128,12 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
case 'add': {
|
||||
const { add } = await import('./add/index.js');
|
||||
const packages = flags._.slice(3) as string[];
|
||||
await add(packages, { flags, logging });
|
||||
await add(packages, { flags });
|
||||
return;
|
||||
}
|
||||
case 'dev': {
|
||||
const { dev } = await import('./dev/index.js');
|
||||
const server = await dev({ flags, logging });
|
||||
const server = await dev({ flags });
|
||||
if (server) {
|
||||
return await new Promise(() => {}); // lives forever
|
||||
}
|
||||
|
@ -148,12 +141,12 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
}
|
||||
case 'build': {
|
||||
const { build } = await import('./build/index.js');
|
||||
await build({ flags, logging });
|
||||
await build({ flags });
|
||||
return;
|
||||
}
|
||||
case 'preview': {
|
||||
const { preview } = await import('./preview/index.js');
|
||||
const server = await preview({ flags, logging });
|
||||
const server = await preview({ flags });
|
||||
if (server) {
|
||||
return await server.closed(); // keep alive until the server is closed
|
||||
}
|
||||
|
@ -162,7 +155,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
case 'check': {
|
||||
const { check } = await import('./check/index.js');
|
||||
// We create a server to start doing our operations
|
||||
const checkServer = await check({ flags, logging });
|
||||
const checkServer = await check({ flags });
|
||||
if (checkServer) {
|
||||
if (checkServer.isWatchMode) {
|
||||
await checkServer.watch();
|
||||
|
@ -176,7 +169,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
|
|||
}
|
||||
case 'sync': {
|
||||
const { sync } = await import('./sync/index.js');
|
||||
const exitCode = await sync({ flags, logging });
|
||||
const exitCode = await sync({ flags });
|
||||
return process.exit(exitCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,16 @@ import * as colors from 'kleur/colors';
|
|||
import { arch, platform } from 'node:os';
|
||||
import whichPm from 'which-pm';
|
||||
import type yargs from 'yargs-parser';
|
||||
import { openConfig } from '../../core/config/index.js';
|
||||
import { resolveConfig } from '../../core/config/index.js';
|
||||
import { ASTRO_VERSION } from '../../core/constants.js';
|
||||
import { flagsToAstroInlineConfig } from '../flags.js';
|
||||
|
||||
interface InfoOptions {
|
||||
flags: yargs.Arguments;
|
||||
}
|
||||
|
||||
export async function printInfo({ flags }: InfoOptions) {
|
||||
const inlineConfig = flagsToAstroInlineConfig(flags);
|
||||
const packageManager = await whichPm(process.cwd());
|
||||
let adapter = "Couldn't determine.";
|
||||
let integrations = [];
|
||||
|
@ -22,11 +24,7 @@ export async function printInfo({ flags }: InfoOptions) {
|
|||
}
|
||||
|
||||
try {
|
||||
const { userConfig } = await openConfig({
|
||||
cwd: flags.root,
|
||||
flags,
|
||||
cmd: 'info',
|
||||
});
|
||||
const { userConfig } = await resolveConfig(inlineConfig, 'info');
|
||||
if (userConfig.adapter?.name) {
|
||||
adapter = userConfig.adapter.name;
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
/* eslint-disable no-console */
|
||||
import * as colors from 'kleur/colors';
|
||||
import fs from 'node:fs';
|
||||
import type { Arguments as Flags } from 'yargs-parser';
|
||||
import { ZodError } from 'zod';
|
||||
import { createSettings, openConfig, resolveConfigPath } from '../core/config/index.js';
|
||||
import { collectErrorMetadata } from '../core/errors/dev/index.js';
|
||||
import { error, type LogOptions } from '../core/logger/core.js';
|
||||
import { formatConfigErrorMessage, formatErrorMessage } from '../core/messages.js';
|
||||
import * as event from '../events/index.js';
|
||||
import { eventConfigError, telemetry } from '../events/index.js';
|
||||
|
||||
interface LoadSettingsOptions {
|
||||
cmd: string;
|
||||
flags: Flags;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
export async function loadSettings({ cmd, flags, logging }: LoadSettingsOptions) {
|
||||
const root = flags.root;
|
||||
const { astroConfig: initialAstroConfig, userConfig: initialUserConfig } = await openConfig({
|
||||
cwd: root,
|
||||
flags,
|
||||
cmd,
|
||||
}).catch(async (e) => {
|
||||
await handleConfigError(e, { cmd, cwd: root, flags, logging });
|
||||
return {} as any;
|
||||
});
|
||||
|
||||
if (!initialAstroConfig) return;
|
||||
telemetry.record(event.eventCliSession(cmd, initialUserConfig, flags));
|
||||
return createSettings(initialAstroConfig, root);
|
||||
}
|
||||
|
||||
export async function handleConfigError(
|
||||
e: any,
|
||||
{ cmd, cwd, flags, logging }: { cmd: string; cwd?: string; flags?: Flags; logging: LogOptions }
|
||||
) {
|
||||
const path = await resolveConfigPath({ cwd, flags, fs });
|
||||
error(logging, 'astro', `Unable to load ${path ? colors.bold(path) : 'your Astro config'}\n`);
|
||||
if (e instanceof ZodError) {
|
||||
console.error(formatConfigErrorMessage(e) + '\n');
|
||||
telemetry.record(eventConfigError({ cmd, err: e, isFatal: true }));
|
||||
} else if (e instanceof Error) {
|
||||
console.error(formatErrorMessage(collectErrorMetadata(e)) + '\n');
|
||||
}
|
||||
}
|
|
@ -1,16 +1,32 @@
|
|||
import { cyan } from 'kleur/colors';
|
||||
import type yargs from 'yargs-parser';
|
||||
import type { LogOptions } from '../../core/logger/core.js';
|
||||
import { printHelp } from '../../core/messages.js';
|
||||
import previewServer from '../../core/preview/index.js';
|
||||
import { loadSettings } from '../load-settings.js';
|
||||
import { flagsToAstroInlineConfig } from '../flags.js';
|
||||
|
||||
interface PreviewOptions {
|
||||
flags: yargs.Arguments;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
export async function preview({ flags, logging }: PreviewOptions) {
|
||||
const settings = await loadSettings({ cmd: 'preview', flags, logging });
|
||||
if (!settings) return;
|
||||
export async function preview({ flags }: PreviewOptions) {
|
||||
if (flags?.help || flags?.h) {
|
||||
printHelp({
|
||||
commandName: 'astro preview',
|
||||
usage: '[...flags]',
|
||||
tables: {
|
||||
Flags: [
|
||||
['--open', 'Automatically open the app in the browser on server start'],
|
||||
['--help (-h)', 'See all available flags.'],
|
||||
],
|
||||
},
|
||||
description: `Starts a local server to serve your static dist/ directory. Check ${cyan(
|
||||
'https://docs.astro.build/en/reference/cli-reference/#astro-preview'
|
||||
)} for more information.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return await previewServer(settings, { flags, logging });
|
||||
const inlineConfig = flagsToAstroInlineConfig(flags);
|
||||
|
||||
return await previewServer(inlineConfig);
|
||||
}
|
||||
|
|
|
@ -1,18 +1,27 @@
|
|||
import fs from 'node:fs';
|
||||
import type yargs from 'yargs-parser';
|
||||
import type { LogOptions } from '../../core/logger/core.js';
|
||||
import { syncCli } from '../../core/sync/index.js';
|
||||
import { loadSettings } from '../load-settings.js';
|
||||
import { printHelp } from '../../core/messages.js';
|
||||
import { sync as _sync } from '../../core/sync/index.js';
|
||||
import { flagsToAstroInlineConfig } from '../flags.js';
|
||||
|
||||
interface SyncOptions {
|
||||
flags: yargs.Arguments;
|
||||
logging: LogOptions;
|
||||
}
|
||||
|
||||
export async function sync({ flags, logging }: SyncOptions) {
|
||||
const settings = await loadSettings({ cmd: 'sync', flags, logging });
|
||||
if (!settings) return;
|
||||
export async function sync({ flags }: SyncOptions) {
|
||||
if (flags?.help || flags?.h) {
|
||||
printHelp({
|
||||
commandName: 'astro sync',
|
||||
usage: '[...flags]',
|
||||
tables: {
|
||||
Flags: [['--help (-h)', 'See all available flags.']],
|
||||
},
|
||||
description: `Generates TypeScript types for all Astro modules.`,
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
const exitCode = await syncCli(settings, { logging, fs, flags });
|
||||
const inlineConfig = flagsToAstroInlineConfig(flags);
|
||||
|
||||
const exitCode = await _sync(inlineConfig);
|
||||
return exitCode;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* eslint-disable no-console */
|
||||
import { collectErrorMetadata } from '../core/errors/dev/index.js';
|
||||
import { isAstroConfigZodError } from '../core/errors/errors.js';
|
||||
import { createSafeError } from '../core/errors/index.js';
|
||||
import { debug } from '../core/logger/core.js';
|
||||
import { formatErrorMessage } from '../core/messages.js';
|
||||
|
@ -7,6 +8,9 @@ import { eventError, telemetry } from '../events/index.js';
|
|||
|
||||
/** Display error and exit */
|
||||
export async function throwAndExit(cmd: string, err: unknown) {
|
||||
// Suppress ZodErrors from AstroConfig as the pre-logged error is sufficient
|
||||
if (isAstroConfigZodError(err)) return;
|
||||
|
||||
let telemetryPromise: Promise<any>;
|
||||
let errorMessage: string;
|
||||
function exitWithErrorMessage() {
|
||||
|
|
|
@ -17,7 +17,7 @@ export function getViteConfig(inlineConfig: UserConfig) {
|
|||
fs,
|
||||
{ mergeConfig },
|
||||
{ nodeLogDestination },
|
||||
{ openConfig, createSettings },
|
||||
{ resolveConfig, createSettings },
|
||||
{ createVite },
|
||||
{ runHookConfigSetup, runHookConfigDone },
|
||||
{ astroContentListenPlugin },
|
||||
|
@ -34,7 +34,7 @@ export function getViteConfig(inlineConfig: UserConfig) {
|
|||
dest: nodeLogDestination,
|
||||
level: 'info',
|
||||
};
|
||||
const { astroConfig: config } = await openConfig({ cmd });
|
||||
const { astroConfig: config } = await resolveConfig({}, cmd);
|
||||
const settings = createSettings(config, inlineConfig.root);
|
||||
await runHookConfigSetup({ settings, command: cmd, logging });
|
||||
const viteConfig = await createVite(
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
import * as colors from 'kleur/colors';
|
||||
import fs from 'node:fs';
|
||||
import { performance } from 'node:perf_hooks';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import type * as vite from 'vite';
|
||||
import type yargs from 'yargs-parser';
|
||||
import type { AstroConfig, AstroSettings, ManifestData, RuntimeMode } from '../../@types/astro';
|
||||
import type {
|
||||
AstroConfig,
|
||||
AstroInlineConfig,
|
||||
AstroSettings,
|
||||
ManifestData,
|
||||
RuntimeMode,
|
||||
} from '../../@types/astro';
|
||||
import { injectImageEndpoint } from '../../assets/internal.js';
|
||||
import { telemetry } from '../../events/index.js';
|
||||
import { eventCliSession } from '../../events/session.js';
|
||||
import {
|
||||
runHookBuildDone,
|
||||
runHookBuildStart,
|
||||
|
@ -12,9 +20,11 @@ import {
|
|||
runHookConfigSetup,
|
||||
} from '../../integrations/index.js';
|
||||
import { isServerLikeOutput } from '../../prerender/utils.js';
|
||||
import { resolveConfig } from '../config/config.js';
|
||||
import { createNodeLogging } from '../config/logging.js';
|
||||
import { createSettings } from '../config/settings.js';
|
||||
import { createVite } from '../create-vite.js';
|
||||
import { debug, info, levels, timerMessage, warn, type LogOptions } from '../logger/core.js';
|
||||
import { printHelp } from '../messages.js';
|
||||
import { apply as applyPolyfill } from '../polyfill.js';
|
||||
import { RouteCache } from '../render/route-cache.js';
|
||||
import { createRouteManifest } from '../routing/index.js';
|
||||
|
@ -24,38 +34,38 @@ import type { StaticBuildOptions } from './types.js';
|
|||
import { getTimeStat } from './util.js';
|
||||
|
||||
export interface BuildOptions {
|
||||
mode?: RuntimeMode;
|
||||
logging: LogOptions;
|
||||
/**
|
||||
* Teardown the compiler WASM instance after build. This can improve performance when
|
||||
* building once, but may cause a performance hit if building multiple times in a row.
|
||||
*/
|
||||
teardownCompiler?: boolean;
|
||||
flags?: yargs.Arguments;
|
||||
}
|
||||
|
||||
/** `astro build` */
|
||||
export default async function build(settings: AstroSettings, options: BuildOptions): Promise<void> {
|
||||
export default async function build(
|
||||
inlineConfig: AstroInlineConfig,
|
||||
options: BuildOptions
|
||||
): Promise<void> {
|
||||
applyPolyfill();
|
||||
if (options.flags?.help || options.flags?.h) {
|
||||
printHelp({
|
||||
commandName: 'astro build',
|
||||
usage: '[...flags]',
|
||||
tables: {
|
||||
Flags: [
|
||||
['--drafts', `Include Markdown draft pages in the build.`],
|
||||
['--help (-h)', 'See all available flags.'],
|
||||
],
|
||||
},
|
||||
description: `Builds your site for deployment.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const logging = createNodeLogging(inlineConfig);
|
||||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig, 'build');
|
||||
telemetry.record(eventCliSession('build', userConfig));
|
||||
|
||||
const builder = new AstroBuilder(settings, options);
|
||||
const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const builder = new AstroBuilder(settings, {
|
||||
...options,
|
||||
logging,
|
||||
mode: inlineConfig.mode,
|
||||
});
|
||||
await builder.run();
|
||||
}
|
||||
|
||||
interface AstroBuilderOptions extends BuildOptions {
|
||||
logging: LogOptions;
|
||||
mode?: RuntimeMode;
|
||||
}
|
||||
|
||||
class AstroBuilder {
|
||||
private settings: AstroSettings;
|
||||
private logging: LogOptions;
|
||||
|
@ -66,7 +76,7 @@ class AstroBuilder {
|
|||
private timer: Record<string, number>;
|
||||
private teardownCompiler: boolean;
|
||||
|
||||
constructor(settings: AstroSettings, options: BuildOptions) {
|
||||
constructor(settings: AstroSettings, options: AstroBuilderOptions) {
|
||||
if (options.mode) {
|
||||
this.mode = options.mode;
|
||||
}
|
||||
|
@ -112,8 +122,8 @@ class AstroBuilder {
|
|||
);
|
||||
await runHookConfigDone({ settings: this.settings, logging });
|
||||
|
||||
const { sync } = await import('../sync/index.js');
|
||||
const syncRet = await sync(this.settings, { logging, fs });
|
||||
const { syncInternal } = await import('../sync/index.js');
|
||||
const syncRet = await syncInternal(this.settings, { logging, fs });
|
||||
if (syncRet !== 0) {
|
||||
return process.exit(syncRet);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,26 @@
|
|||
import type { Arguments as Flags } from 'yargs-parser';
|
||||
import type { AstroConfig, AstroUserConfig, CLIFlags } from '../../@types/astro';
|
||||
import type {
|
||||
AstroConfig,
|
||||
AstroInlineConfig,
|
||||
AstroInlineOnlyConfig,
|
||||
AstroUserConfig,
|
||||
CLIFlags,
|
||||
} from '../../@types/astro';
|
||||
|
||||
import * as colors from 'kleur/colors';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { ZodError } from 'zod';
|
||||
import { eventConfigError, telemetry } from '../../events/index.js';
|
||||
import { trackAstroConfigZodError } from '../errors/errors.js';
|
||||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
import { formatConfigErrorMessage } from '../messages.js';
|
||||
import { mergeConfig } from './merge.js';
|
||||
import { createRelativeSchema } from './schema.js';
|
||||
import { loadConfigWithVite } from './vite-load.js';
|
||||
|
||||
export const LEGACY_ASTRO_CONFIG_KEYS = new Set([
|
||||
const LEGACY_ASTRO_CONFIG_KEYS = new Set([
|
||||
'projectRoot',
|
||||
'src',
|
||||
'pages',
|
||||
|
@ -80,13 +90,29 @@ export async function validateConfig(
|
|||
const AstroConfigRelativeSchema = createRelativeSchema(cmd, root);
|
||||
|
||||
// First-Pass Validation
|
||||
const result = await AstroConfigRelativeSchema.parseAsync(userConfig);
|
||||
let result: AstroConfig;
|
||||
try {
|
||||
result = await AstroConfigRelativeSchema.parseAsync(userConfig);
|
||||
} catch (e) {
|
||||
// Improve config zod error messages
|
||||
if (e instanceof ZodError) {
|
||||
// Mark this error so the callee can decide to suppress Zod's error if needed.
|
||||
// We still want to throw the error to signal an error in validation.
|
||||
trackAstroConfigZodError(e);
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(formatConfigErrorMessage(e) + '\n');
|
||||
telemetry.record(eventConfigError({ cmd, err: e, isFatal: true }));
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
// If successful, return the result as a verified AstroConfig object.
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Convert the generic "yargs" flag object into our own, custom TypeScript object. */
|
||||
// NOTE: This function will be removed in a later PR. Use `flagsToAstroInlineConfig` instead.
|
||||
// All CLI related flow should be located in the `packages/astro/src/cli` directory.
|
||||
export function resolveFlags(flags: Partial<Flags>): CLIFlags {
|
||||
return {
|
||||
root: typeof flags.root === 'string' ? flags.root : undefined,
|
||||
|
@ -110,22 +136,6 @@ export function resolveRoot(cwd?: string | URL): string {
|
|||
return cwd ? path.resolve(cwd) : process.cwd();
|
||||
}
|
||||
|
||||
/** Merge CLI flags & user config object (CLI flags take priority) */
|
||||
function mergeCLIFlags(astroConfig: AstroUserConfig, flags: CLIFlags) {
|
||||
return mergeConfig(astroConfig, {
|
||||
site: flags.site,
|
||||
base: flags.base,
|
||||
markdown: {
|
||||
drafts: flags.drafts,
|
||||
},
|
||||
server: {
|
||||
port: flags.port,
|
||||
host: flags.host,
|
||||
open: flags.open,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function search(fsMod: typeof fs, root: string) {
|
||||
const paths = [
|
||||
'astro.config.mjs',
|
||||
|
@ -143,19 +153,9 @@ async function search(fsMod: typeof fs, root: string) {
|
|||
}
|
||||
}
|
||||
|
||||
interface LoadConfigOptions {
|
||||
cwd?: string;
|
||||
flags?: Flags;
|
||||
cmd: string;
|
||||
validate?: boolean;
|
||||
/** Invalidate when reloading a previously loaded config */
|
||||
isRestart?: boolean;
|
||||
fsMod?: typeof fs;
|
||||
}
|
||||
|
||||
interface ResolveConfigPathOptions {
|
||||
cwd?: string;
|
||||
flags?: Flags;
|
||||
root: string;
|
||||
configFile?: string;
|
||||
fs: typeof fs;
|
||||
}
|
||||
|
||||
|
@ -163,87 +163,85 @@ interface ResolveConfigPathOptions {
|
|||
* Resolve the file URL of the user's `astro.config.js|cjs|mjs|ts` file
|
||||
*/
|
||||
export async function resolveConfigPath(
|
||||
configOptions: ResolveConfigPathOptions
|
||||
options: ResolveConfigPathOptions
|
||||
): Promise<string | undefined> {
|
||||
const root = resolveRoot(configOptions.cwd);
|
||||
const flags = resolveFlags(configOptions.flags || {});
|
||||
|
||||
let userConfigPath: string | undefined;
|
||||
if (flags?.config) {
|
||||
userConfigPath = /^\.*\//.test(flags.config) ? flags.config : `./${flags.config}`;
|
||||
userConfigPath = fileURLToPath(new URL(userConfigPath, `file://${root}/`));
|
||||
if (!configOptions.fs.existsSync(userConfigPath)) {
|
||||
if (options.configFile) {
|
||||
userConfigPath = path.join(options.root, options.configFile);
|
||||
if (!options.fs.existsSync(userConfigPath)) {
|
||||
throw new AstroError({
|
||||
...AstroErrorData.ConfigNotFound,
|
||||
message: AstroErrorData.ConfigNotFound.message(flags.config),
|
||||
message: AstroErrorData.ConfigNotFound.message(options.configFile),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
userConfigPath = await search(configOptions.fs, root);
|
||||
userConfigPath = await search(options.fs, options.root);
|
||||
}
|
||||
|
||||
return userConfigPath;
|
||||
}
|
||||
|
||||
interface OpenConfigResult {
|
||||
userConfig: AstroUserConfig;
|
||||
astroConfig: AstroConfig;
|
||||
flags: CLIFlags;
|
||||
root: string;
|
||||
}
|
||||
|
||||
/** Load a configuration file, returning both the userConfig and astroConfig */
|
||||
export async function openConfig(configOptions: LoadConfigOptions): Promise<OpenConfigResult> {
|
||||
const root = resolveRoot(configOptions.cwd);
|
||||
const flags = resolveFlags(configOptions.flags || {});
|
||||
|
||||
const userConfig = await loadConfig(configOptions, root);
|
||||
const astroConfig = await resolveConfig(userConfig, root, flags, configOptions.cmd);
|
||||
|
||||
return {
|
||||
astroConfig,
|
||||
userConfig,
|
||||
flags,
|
||||
root,
|
||||
};
|
||||
}
|
||||
|
||||
async function loadConfig(
|
||||
configOptions: LoadConfigOptions,
|
||||
root: string
|
||||
root: string,
|
||||
configFile?: string | false,
|
||||
fsMod = fs
|
||||
): Promise<Record<string, any>> {
|
||||
const fsMod = configOptions.fsMod ?? fs;
|
||||
if (configFile === false) return {};
|
||||
|
||||
const configPath = await resolveConfigPath({
|
||||
cwd: configOptions.cwd,
|
||||
flags: configOptions.flags,
|
||||
root,
|
||||
configFile,
|
||||
fs: fsMod,
|
||||
});
|
||||
if (!configPath) return {};
|
||||
|
||||
// Create a vite server to load the config
|
||||
return await loadConfigWithVite({
|
||||
configPath,
|
||||
fs: fsMod,
|
||||
root,
|
||||
});
|
||||
try {
|
||||
return await loadConfigWithVite({
|
||||
root,
|
||||
configPath,
|
||||
fs: fsMod,
|
||||
});
|
||||
} catch (e) {
|
||||
const configPathText = configFile ? colors.bold(configFile) : 'your Astro config';
|
||||
// Config errors should bypass log level as it breaks startup
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`${colors.bold(colors.red('[astro]'))} Unable to load ${configPathText}\n`);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
function splitInlineConfig(inlineConfig: AstroInlineConfig): {
|
||||
inlineUserConfig: AstroUserConfig;
|
||||
inlineOnlyConfig: AstroInlineOnlyConfig;
|
||||
} {
|
||||
const { configFile, mode, logLevel, ...inlineUserConfig } = inlineConfig;
|
||||
return {
|
||||
inlineUserConfig,
|
||||
inlineOnlyConfig: {
|
||||
configFile,
|
||||
mode,
|
||||
logLevel,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
interface ResolveConfigResult {
|
||||
userConfig: AstroUserConfig;
|
||||
astroConfig: AstroConfig;
|
||||
}
|
||||
|
||||
/** Attempt to resolve an Astro configuration object. Normalize, validate, and return. */
|
||||
export async function resolveConfig(
|
||||
userConfig: AstroUserConfig,
|
||||
root: string,
|
||||
flags: CLIFlags = {},
|
||||
cmd: string
|
||||
): Promise<AstroConfig> {
|
||||
const mergedConfig = mergeCLIFlags(userConfig, flags);
|
||||
const validatedConfig = await validateConfig(mergedConfig, root, cmd);
|
||||
inlineConfig: AstroInlineConfig,
|
||||
command: string,
|
||||
fsMod = fs
|
||||
): Promise<ResolveConfigResult> {
|
||||
const root = resolveRoot(inlineConfig.root);
|
||||
const { inlineUserConfig, inlineOnlyConfig } = splitInlineConfig(inlineConfig);
|
||||
|
||||
return validatedConfig;
|
||||
}
|
||||
const userConfig = await loadConfig(root, inlineOnlyConfig.configFile, fsMod);
|
||||
const mergedConfig = mergeConfig(userConfig, inlineUserConfig);
|
||||
const astroConfig = await validateConfig(mergedConfig, root, command);
|
||||
|
||||
export function createDefaultDevConfig(
|
||||
userConfig: AstroUserConfig = {},
|
||||
root: string = process.cwd()
|
||||
) {
|
||||
return resolveConfig(userConfig, root, undefined, 'dev');
|
||||
return { userConfig, astroConfig };
|
||||
}
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
export {
|
||||
createDefaultDevConfig,
|
||||
openConfig,
|
||||
resolveConfigPath,
|
||||
resolveFlags,
|
||||
resolveRoot,
|
||||
validateConfig,
|
||||
} from './config.js';
|
||||
export { resolveConfig, resolveConfigPath, resolveFlags, resolveRoot } from './config.js';
|
||||
export { createNodeLogging } from './logging.js';
|
||||
export { mergeConfig } from './merge.js';
|
||||
export type { AstroConfigSchema } from './schema';
|
||||
export { createDefaultDevSettings, createSettings } from './settings.js';
|
||||
export { createSettings } from './settings.js';
|
||||
export { loadTSConfig, updateTSConfigForFramework } from './tsconfig.js';
|
||||
|
|
13
packages/astro/src/core/config/logging.ts
Normal file
13
packages/astro/src/core/config/logging.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
import type { AstroInlineConfig } from '../../@types/astro.js';
|
||||
import type { LogOptions } from '../logger/core.js';
|
||||
import { nodeLogDestination } from '../logger/node.js';
|
||||
|
||||
export function createNodeLogging(inlineConfig: AstroInlineConfig): LogOptions {
|
||||
// For internal testing, the inline config can pass the raw `logging` object directly
|
||||
if (inlineConfig.logging) return inlineConfig.logging;
|
||||
|
||||
return {
|
||||
dest: nodeLogDestination,
|
||||
level: inlineConfig.logLevel ?? 'info',
|
||||
};
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import yaml from 'js-yaml';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import type { AstroConfig, AstroSettings, AstroUserConfig } from '../../@types/astro';
|
||||
import type { AstroConfig, AstroSettings } from '../../@types/astro';
|
||||
import { getContentPaths } from '../../content/index.js';
|
||||
import jsxRenderer from '../../jsx/renderer.js';
|
||||
import { markdownContentEntryType } from '../../vite-plugin-markdown/content-entry-type.js';
|
||||
|
@ -9,7 +9,6 @@ import { getDefaultClientDirectives } from '../client-directive/index.js';
|
|||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
import { formatYAMLException, isYAMLException } from '../errors/utils.js';
|
||||
import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from './../constants.js';
|
||||
import { createDefaultDevConfig } from './config.js';
|
||||
import { AstroTimer } from './timer.js';
|
||||
import { loadTSConfig } from './tsconfig.js';
|
||||
|
||||
|
@ -119,14 +118,3 @@ export function createSettings(config: AstroConfig, cwd?: string): AstroSettings
|
|||
settings.watchFiles = watchFiles;
|
||||
return settings;
|
||||
}
|
||||
|
||||
export async function createDefaultDevSettings(
|
||||
userConfig: AstroUserConfig = {},
|
||||
root?: string | URL
|
||||
): Promise<AstroSettings> {
|
||||
if (root && typeof root !== 'string') {
|
||||
root = fileURLToPath(root);
|
||||
}
|
||||
const config = await createDefaultDevConfig(userConfig, root);
|
||||
return createBaseSettings(config);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import type * as http from 'node:http';
|
||||
import type { AddressInfo } from 'node:net';
|
||||
import type { AstroSettings, AstroUserConfig } from '../../@types/astro';
|
||||
import type { AstroInlineConfig, AstroSettings } from '../../@types/astro';
|
||||
|
||||
import nodeFs from 'node:fs';
|
||||
import * as vite from 'vite';
|
||||
|
@ -11,52 +11,36 @@ import {
|
|||
runHookServerDone,
|
||||
runHookServerStart,
|
||||
} from '../../integrations/index.js';
|
||||
import { createDefaultDevSettings, resolveRoot } from '../config/index.js';
|
||||
import { createVite } from '../create-vite.js';
|
||||
import type { LogOptions } from '../logger/core.js';
|
||||
import { nodeLogDestination } from '../logger/node.js';
|
||||
import { appendForwardSlash } from '../path.js';
|
||||
import { apply as applyPolyfill } from '../polyfill.js';
|
||||
|
||||
const defaultLogging: LogOptions = {
|
||||
dest: nodeLogDestination,
|
||||
level: 'error',
|
||||
};
|
||||
|
||||
export interface Container {
|
||||
fs: typeof nodeFs;
|
||||
logging: LogOptions;
|
||||
settings: AstroSettings;
|
||||
viteConfig: vite.InlineConfig;
|
||||
viteServer: vite.ViteDevServer;
|
||||
resolvedRoot: string;
|
||||
configFlag: string | undefined;
|
||||
configFlagPath: string | undefined;
|
||||
inlineConfig: AstroInlineConfig;
|
||||
restartInFlight: boolean; // gross
|
||||
handle: (req: http.IncomingMessage, res: http.ServerResponse) => void;
|
||||
close: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface CreateContainerParams {
|
||||
logging: LogOptions;
|
||||
settings: AstroSettings;
|
||||
inlineConfig?: AstroInlineConfig;
|
||||
isRestart?: boolean;
|
||||
logging?: LogOptions;
|
||||
userConfig?: AstroUserConfig;
|
||||
settings?: AstroSettings;
|
||||
fs?: typeof nodeFs;
|
||||
root?: string | URL;
|
||||
// The string passed to --config and the resolved path
|
||||
configFlag?: string;
|
||||
configFlagPath?: string;
|
||||
}
|
||||
|
||||
export async function createContainer(params: CreateContainerParams = {}): Promise<Container> {
|
||||
let {
|
||||
isRestart = false,
|
||||
logging = defaultLogging,
|
||||
settings = await createDefaultDevSettings(params.userConfig, params.root),
|
||||
fs = nodeFs,
|
||||
} = params;
|
||||
|
||||
export async function createContainer({
|
||||
isRestart = false,
|
||||
logging,
|
||||
inlineConfig,
|
||||
settings,
|
||||
fs = nodeFs,
|
||||
}: CreateContainerParams): Promise<Container> {
|
||||
// Initialize
|
||||
applyPolyfill();
|
||||
settings = await runHookConfigSetup({
|
||||
|
@ -94,14 +78,11 @@ export async function createContainer(params: CreateContainerParams = {}): Promi
|
|||
const viteServer = await vite.createServer(viteConfig);
|
||||
|
||||
const container: Container = {
|
||||
configFlag: params.configFlag,
|
||||
configFlagPath: params.configFlagPath,
|
||||
inlineConfig: inlineConfig ?? {},
|
||||
fs,
|
||||
logging,
|
||||
resolvedRoot: appendForwardSlash(resolveRoot(params.root)),
|
||||
restartInFlight: false,
|
||||
settings,
|
||||
viteConfig,
|
||||
viteServer,
|
||||
handle(req, res) {
|
||||
viteServer.middlewares.handle(req, res, Function.prototype);
|
||||
|
@ -143,18 +124,3 @@ export async function startContainer({
|
|||
export function isStarted(container: Container): boolean {
|
||||
return !!container.viteServer.httpServer?.listening;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only used in tests
|
||||
*/
|
||||
export async function runInContainer(
|
||||
params: CreateContainerParams,
|
||||
callback: (container: Container) => Promise<void> | void
|
||||
) {
|
||||
const container = await createContainer(params);
|
||||
try {
|
||||
await callback(container);
|
||||
} finally {
|
||||
await container.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,27 +1,16 @@
|
|||
import { cyan } from 'kleur/colors';
|
||||
import fs from 'node:fs';
|
||||
import type http from 'node:http';
|
||||
import type { AddressInfo } from 'node:net';
|
||||
import { performance } from 'perf_hooks';
|
||||
import type * as vite from 'vite';
|
||||
import type yargs from 'yargs-parser';
|
||||
import type { AstroSettings } from '../../@types/astro';
|
||||
import type { AstroInlineConfig } from '../../@types/astro';
|
||||
import { attachContentServerListeners } from '../../content/index.js';
|
||||
import { telemetry } from '../../events/index.js';
|
||||
import { info, warn, type LogOptions } from '../logger/core.js';
|
||||
import { info, warn } from '../logger/core.js';
|
||||
import * as msg from '../messages.js';
|
||||
import { printHelp } from '../messages.js';
|
||||
import { startContainer } from './container.js';
|
||||
import { createContainerWithAutomaticRestart } from './restart.js';
|
||||
|
||||
export interface DevOptions {
|
||||
configFlag: string | undefined;
|
||||
configFlagPath: string | undefined;
|
||||
flags?: yargs.Arguments;
|
||||
logging: LogOptions;
|
||||
handleConfigError: (error: Error) => void;
|
||||
isRestart?: boolean;
|
||||
}
|
||||
|
||||
export interface DevServer {
|
||||
address: AddressInfo;
|
||||
handle: (req: http.IncomingMessage, res: http.ServerResponse<http.IncomingMessage>) => void;
|
||||
|
@ -30,68 +19,34 @@ export interface DevServer {
|
|||
}
|
||||
|
||||
/** `astro dev` */
|
||||
export default async function dev(
|
||||
settings: AstroSettings,
|
||||
options: DevOptions
|
||||
): Promise<DevServer | undefined> {
|
||||
if (options.flags?.help || options.flags?.h) {
|
||||
printHelp({
|
||||
commandName: 'astro dev',
|
||||
usage: '[...flags]',
|
||||
tables: {
|
||||
Flags: [
|
||||
['--port', `Specify which port to run on. Defaults to 3000.`],
|
||||
['--host', `Listen on all addresses, including LAN and public addresses.`],
|
||||
['--host <custom-address>', `Expose on a network IP address at <custom-address>`],
|
||||
['--open', 'Automatically open the app in the browser on server start'],
|
||||
['--help (-h)', 'See all available flags.'],
|
||||
],
|
||||
},
|
||||
description: `Check ${cyan(
|
||||
'https://docs.astro.build/en/reference/cli-reference/#astro-dev'
|
||||
)} for more information.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
export default async function dev(inlineConfig: AstroInlineConfig): Promise<DevServer> {
|
||||
const devStart = performance.now();
|
||||
await telemetry.record([]);
|
||||
|
||||
// Create a container which sets up the Vite server.
|
||||
const restart = await createContainerWithAutomaticRestart({
|
||||
flags: options.flags ?? {},
|
||||
handleConfigError: options.handleConfigError,
|
||||
// eslint-disable-next-line no-console
|
||||
beforeRestart: () => console.clear(),
|
||||
params: {
|
||||
settings,
|
||||
root: options.flags?.root,
|
||||
logging: options.logging,
|
||||
isRestart: options.isRestart,
|
||||
},
|
||||
});
|
||||
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
||||
const logging = restart.container.logging;
|
||||
|
||||
// Start listening to the port
|
||||
const devServerAddressInfo = await startContainer(restart.container);
|
||||
|
||||
info(
|
||||
options.logging,
|
||||
logging,
|
||||
null,
|
||||
msg.serverStart({
|
||||
startupTime: performance.now() - devStart,
|
||||
resolvedUrls: restart.container.viteServer.resolvedUrls || { local: [], network: [] },
|
||||
host: settings.config.server.host,
|
||||
base: settings.config.base,
|
||||
isRestart: options.isRestart,
|
||||
host: restart.container.settings.config.server.host,
|
||||
base: restart.container.settings.config.base,
|
||||
})
|
||||
);
|
||||
|
||||
const currentVersion = process.env.PACKAGE_VERSION ?? '0.0.0';
|
||||
if (currentVersion.includes('-')) {
|
||||
warn(options.logging, null, msg.prerelease({ currentVersion }));
|
||||
warn(logging, null, msg.prerelease({ currentVersion }));
|
||||
}
|
||||
if (restart.container.viteConfig.server?.fs?.strict === false) {
|
||||
warn(options.logging, null, msg.fsStrictWarning());
|
||||
if (restart.container.viteServer.config.server?.fs?.strict === false) {
|
||||
warn(logging, null, msg.fsStrictWarning());
|
||||
}
|
||||
|
||||
await attachContentServerListeners(restart.container);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export { createContainer, isStarted, runInContainer, startContainer } from './container.js';
|
||||
export { createContainer, isStarted, startContainer } from './container.js';
|
||||
export { default } from './dev.js';
|
||||
export { createContainerWithAutomaticRestart } from './restart.js';
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import nodeFs from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import * as vite from 'vite';
|
||||
import type { AstroSettings } from '../../@types/astro';
|
||||
import { createSettings, openConfig } from '../config/index.js';
|
||||
import type { AstroInlineConfig, AstroSettings } from '../../@types/astro';
|
||||
import { eventCliSession, telemetry } from '../../events/index.js';
|
||||
import { createNodeLogging, createSettings, resolveConfig } from '../config/index.js';
|
||||
import { collectErrorMetadata } from '../errors/dev/utils.js';
|
||||
import { isAstroConfigZodError } from '../errors/errors.js';
|
||||
import { createSafeError } from '../errors/index.js';
|
||||
import { info } from '../logger/core.js';
|
||||
import type { Container, CreateContainerParams } from './container';
|
||||
import { info, error as logError } from '../logger/core.js';
|
||||
import { formatErrorMessage } from '../messages.js';
|
||||
import type { Container } from './container';
|
||||
import { createContainer, isStarted, startContainer } from './container.js';
|
||||
|
||||
async function createRestartedContainer(
|
||||
|
@ -11,15 +17,13 @@ async function createRestartedContainer(
|
|||
settings: AstroSettings,
|
||||
needsStart: boolean
|
||||
): Promise<Container> {
|
||||
const { logging, fs, resolvedRoot, configFlag, configFlagPath } = container;
|
||||
const { logging, fs, inlineConfig } = container;
|
||||
const newContainer = await createContainer({
|
||||
isRestart: true,
|
||||
logging,
|
||||
settings,
|
||||
inlineConfig,
|
||||
fs,
|
||||
root: resolvedRoot,
|
||||
configFlag,
|
||||
configFlagPath,
|
||||
});
|
||||
|
||||
if (needsStart) {
|
||||
|
@ -30,7 +34,7 @@ async function createRestartedContainer(
|
|||
}
|
||||
|
||||
export function shouldRestartContainer(
|
||||
{ settings, configFlag, configFlagPath, restartInFlight }: Container,
|
||||
{ settings, inlineConfig, restartInFlight }: Container,
|
||||
changedFile: string
|
||||
): boolean {
|
||||
if (restartInFlight) return false;
|
||||
|
@ -38,10 +42,8 @@ export function shouldRestartContainer(
|
|||
let shouldRestart = false;
|
||||
|
||||
// If the config file changed, reload the config and restart the server.
|
||||
if (configFlag) {
|
||||
if (!!configFlagPath) {
|
||||
shouldRestart = vite.normalizePath(configFlagPath) === vite.normalizePath(changedFile);
|
||||
}
|
||||
if (inlineConfig.configFile) {
|
||||
shouldRestart = vite.normalizePath(inlineConfig.configFile) === vite.normalizePath(changedFile);
|
||||
}
|
||||
// Otherwise, watch for any astro.config.* file changes in project root
|
||||
else {
|
||||
|
@ -60,39 +62,16 @@ export function shouldRestartContainer(
|
|||
return shouldRestart;
|
||||
}
|
||||
|
||||
interface RestartContainerParams {
|
||||
container: Container;
|
||||
flags: any;
|
||||
logMsg: string;
|
||||
handleConfigError: (err: Error) => Promise<void> | void;
|
||||
beforeRestart?: () => void;
|
||||
}
|
||||
|
||||
export async function restartContainer({
|
||||
container,
|
||||
flags,
|
||||
logMsg,
|
||||
handleConfigError,
|
||||
beforeRestart,
|
||||
}: RestartContainerParams): Promise<{ container: Container; error: Error | null }> {
|
||||
const { logging, close, resolvedRoot, settings: existingSettings } = container;
|
||||
export async function restartContainer(
|
||||
container: Container
|
||||
): Promise<{ container: Container; error: Error | null }> {
|
||||
const { logging, close, settings: existingSettings } = container;
|
||||
container.restartInFlight = true;
|
||||
|
||||
if (beforeRestart) {
|
||||
beforeRestart();
|
||||
}
|
||||
const needsStart = isStarted(container);
|
||||
try {
|
||||
const newConfig = await openConfig({
|
||||
cwd: resolvedRoot,
|
||||
flags,
|
||||
cmd: 'dev',
|
||||
isRestart: true,
|
||||
fsMod: container.fs,
|
||||
});
|
||||
info(logging, 'astro', logMsg + '\n');
|
||||
let astroConfig = newConfig.astroConfig;
|
||||
const settings = createSettings(astroConfig, resolvedRoot);
|
||||
const { astroConfig } = await resolveConfig(container.inlineConfig, 'dev', container.fs);
|
||||
const settings = createSettings(astroConfig, fileURLToPath(existingSettings.config.root));
|
||||
await close();
|
||||
return {
|
||||
container: await createRestartedContainer(container, settings, needsStart),
|
||||
|
@ -100,7 +79,18 @@ export async function restartContainer({
|
|||
};
|
||||
} catch (_err) {
|
||||
const error = createSafeError(_err);
|
||||
await handleConfigError(error);
|
||||
// Print all error messages except ZodErrors from AstroConfig as the pre-logged error is sufficient
|
||||
if (!isAstroConfigZodError(_err)) {
|
||||
logError(logging, 'config', formatErrorMessage(collectErrorMetadata(error)) + '\n');
|
||||
}
|
||||
// Inform connected clients of the config error
|
||||
container.viteServer.ws.send({
|
||||
type: 'error',
|
||||
err: {
|
||||
message: error.message,
|
||||
stack: error.stack || '',
|
||||
},
|
||||
});
|
||||
await close();
|
||||
info(logging, 'astro', 'Continuing with previous valid configuration\n');
|
||||
return {
|
||||
|
@ -111,10 +101,8 @@ export async function restartContainer({
|
|||
}
|
||||
|
||||
export interface CreateContainerWithAutomaticRestart {
|
||||
flags: any;
|
||||
params: CreateContainerParams;
|
||||
handleConfigError?: (error: Error) => void | Promise<void>;
|
||||
beforeRestart?: () => void;
|
||||
inlineConfig?: AstroInlineConfig;
|
||||
fs: typeof nodeFs;
|
||||
}
|
||||
|
||||
interface Restart {
|
||||
|
@ -123,12 +111,17 @@ interface Restart {
|
|||
}
|
||||
|
||||
export async function createContainerWithAutomaticRestart({
|
||||
flags,
|
||||
handleConfigError = () => {},
|
||||
beforeRestart,
|
||||
params,
|
||||
inlineConfig,
|
||||
fs,
|
||||
}: CreateContainerWithAutomaticRestart): Promise<Restart> {
|
||||
const initialContainer = await createContainer(params);
|
||||
const logging = createNodeLogging(inlineConfig ?? {});
|
||||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'dev', fs);
|
||||
telemetry.record(eventCliSession('dev', userConfig));
|
||||
|
||||
const settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const initialContainer = await createContainer({ settings, logging, inlineConfig, fs });
|
||||
|
||||
let resolveRestart: (value: Error | null) => void;
|
||||
let restartComplete = new Promise<Error | null>((resolve) => {
|
||||
resolveRestart = resolve;
|
||||
|
@ -142,24 +135,9 @@ export async function createContainerWithAutomaticRestart({
|
|||
};
|
||||
|
||||
async function handleServerRestart(logMsg: string) {
|
||||
info(logging, 'astro', logMsg + '\n');
|
||||
const container = restart.container;
|
||||
const { container: newContainer, error } = await restartContainer({
|
||||
beforeRestart,
|
||||
container,
|
||||
flags,
|
||||
logMsg,
|
||||
async handleConfigError(err) {
|
||||
// Send an error message to the client if one is connected.
|
||||
await handleConfigError(err);
|
||||
container.viteServer.ws.send({
|
||||
type: 'error',
|
||||
err: {
|
||||
message: err.message,
|
||||
stack: err.stack || '',
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
const { container: newContainer, error } = await restartContainer(container);
|
||||
restart.container = newContainer;
|
||||
// Add new watches because this is a new container with a new Vite server
|
||||
addWatches();
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import type { ZodError } from 'zod';
|
||||
import { codeFrame } from './printer.js';
|
||||
import { getErrorDataByTitle } from './utils.js';
|
||||
|
||||
|
@ -141,6 +142,23 @@ export class AggregateError extends AstroError {
|
|||
}
|
||||
}
|
||||
|
||||
const astroConfigZodErrors = new WeakSet<ZodError>();
|
||||
|
||||
/**
|
||||
* Check if an error is a ZodError from an AstroConfig validation.
|
||||
* Used to suppress formatting a ZodError if needed.
|
||||
*/
|
||||
export function isAstroConfigZodError(error: unknown): error is ZodError {
|
||||
return astroConfigZodErrors.has(error as ZodError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Track that a ZodError comes from an AstroConfig validation.
|
||||
*/
|
||||
export function trackAstroConfigZodError(error: ZodError): void {
|
||||
astroConfigZodErrors.add(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic object representing an error with all possible data
|
||||
* Compatible with both Astro's and Vite's errors
|
||||
|
|
|
@ -1,40 +1,24 @@
|
|||
import { cyan } from 'kleur/colors';
|
||||
import { createRequire } from 'module';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import type { Arguments } from 'yargs-parser';
|
||||
import type { AstroSettings, PreviewModule, PreviewServer } from '../../@types/astro';
|
||||
import { createRequire } from 'node:module';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import type { AstroInlineConfig, PreviewModule, PreviewServer } from '../../@types/astro';
|
||||
import { telemetry } from '../../events/index.js';
|
||||
import { eventCliSession } from '../../events/session.js';
|
||||
import { runHookConfigDone, runHookConfigSetup } from '../../integrations/index.js';
|
||||
import type { LogOptions } from '../logger/core';
|
||||
import { printHelp } from '../messages.js';
|
||||
import { resolveConfig } from '../config/config.js';
|
||||
import { createNodeLogging } from '../config/logging.js';
|
||||
import { createSettings } from '../config/settings.js';
|
||||
import createStaticPreviewServer from './static-preview-server.js';
|
||||
import { getResolvedHostForHttpServer } from './util.js';
|
||||
|
||||
interface PreviewOptions {
|
||||
logging: LogOptions;
|
||||
flags?: Arguments;
|
||||
}
|
||||
|
||||
/** The primary dev action */
|
||||
export default async function preview(
|
||||
_settings: AstroSettings,
|
||||
{ logging, flags }: PreviewOptions
|
||||
inlineConfig: AstroInlineConfig
|
||||
): Promise<PreviewServer | undefined> {
|
||||
if (flags?.help || flags?.h) {
|
||||
printHelp({
|
||||
commandName: 'astro preview',
|
||||
usage: '[...flags]',
|
||||
tables: {
|
||||
Flags: [
|
||||
['--open', 'Automatically open the app in the browser on server start'],
|
||||
['--help (-h)', 'See all available flags.'],
|
||||
],
|
||||
},
|
||||
description: `Starts a local server to serve your static dist/ directory. Check ${cyan(
|
||||
'https://docs.astro.build/en/reference/cli-reference/#astro-preview'
|
||||
)} for more information.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const logging = createNodeLogging(inlineConfig);
|
||||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'preview');
|
||||
telemetry.record(eventCliSession('preview', userConfig));
|
||||
|
||||
const _settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const settings = await runHookConfigSetup({
|
||||
settings: _settings,
|
||||
|
|
|
@ -1,48 +1,54 @@
|
|||
import { dim } from 'kleur/colors';
|
||||
import type fsMod from 'node:fs';
|
||||
import fsMod from 'node:fs';
|
||||
import { performance } from 'node:perf_hooks';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createServer, type HMRPayload } from 'vite';
|
||||
import type { Arguments } from 'yargs-parser';
|
||||
import type { AstroSettings } from '../../@types/astro';
|
||||
import type { AstroInlineConfig, AstroSettings } from '../../@types/astro';
|
||||
import { createContentTypesGenerator } from '../../content/index.js';
|
||||
import { globalContentConfigObserver } from '../../content/utils.js';
|
||||
import { telemetry } from '../../events/index.js';
|
||||
import { eventCliSession } from '../../events/session.js';
|
||||
import { runHookConfigSetup } from '../../integrations/index.js';
|
||||
import { setUpEnvTs } from '../../vite-plugin-inject-env-ts/index.js';
|
||||
import { getTimeStat } from '../build/util.js';
|
||||
import { resolveConfig } from '../config/config.js';
|
||||
import { createNodeLogging } from '../config/logging.js';
|
||||
import { createSettings } from '../config/settings.js';
|
||||
import { createVite } from '../create-vite.js';
|
||||
import { AstroError, AstroErrorData, createSafeError, isAstroError } from '../errors/index.js';
|
||||
import { info, type LogOptions } from '../logger/core.js';
|
||||
import { printHelp } from '../messages.js';
|
||||
|
||||
export type ProcessExit = 0 | 1;
|
||||
|
||||
export type SyncOptions = {
|
||||
logging: LogOptions;
|
||||
fs: typeof fsMod;
|
||||
/**
|
||||
* Only used for testing
|
||||
* @internal
|
||||
*/
|
||||
fs?: typeof fsMod;
|
||||
};
|
||||
|
||||
export async function syncCli(
|
||||
settings: AstroSettings,
|
||||
{ logging, fs, flags }: { logging: LogOptions; fs: typeof fsMod; flags?: Arguments }
|
||||
): Promise<ProcessExit> {
|
||||
if (flags?.help || flags?.h) {
|
||||
printHelp({
|
||||
commandName: 'astro sync',
|
||||
usage: '[...flags]',
|
||||
tables: {
|
||||
Flags: [['--help (-h)', 'See all available flags.']],
|
||||
},
|
||||
description: `Generates TypeScript types for all Astro modules.`,
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
export type SyncInternalOptions = SyncOptions & {
|
||||
logging: LogOptions;
|
||||
};
|
||||
|
||||
const resolvedSettings = await runHookConfigSetup({
|
||||
settings,
|
||||
logging,
|
||||
export async function sync(
|
||||
inlineConfig: AstroInlineConfig,
|
||||
options?: SyncOptions
|
||||
): Promise<ProcessExit> {
|
||||
const logging = createNodeLogging(inlineConfig);
|
||||
const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'sync');
|
||||
telemetry.record(eventCliSession('sync', userConfig));
|
||||
|
||||
const _settings = createSettings(astroConfig, fileURLToPath(astroConfig.root));
|
||||
|
||||
const settings = await runHookConfigSetup({
|
||||
settings: _settings,
|
||||
logging: logging,
|
||||
command: 'build',
|
||||
});
|
||||
return sync(resolvedSettings, { logging, fs });
|
||||
|
||||
return await syncInternal(settings, { logging, fs: options?.fs });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,15 +56,18 @@ export async function syncCli(
|
|||
*
|
||||
* A non-zero process signal is emitted in case there's an error while generating content collection types.
|
||||
*
|
||||
* This should only be used when the callee already has an `AstroSetting`, otherwise use `sync()` instead.
|
||||
* @internal
|
||||
*
|
||||
* @param {SyncOptions} options
|
||||
* @param {AstroSettings} settings Astro settings
|
||||
* @param {typeof fsMod} options.fs The file system
|
||||
* @param {LogOptions} options.logging Logging options
|
||||
* @return {Promise<ProcessExit>}
|
||||
*/
|
||||
export async function sync(
|
||||
export async function syncInternal(
|
||||
settings: AstroSettings,
|
||||
{ logging, fs }: SyncOptions
|
||||
{ logging, fs }: SyncInternalOptions
|
||||
): Promise<ProcessExit> {
|
||||
const timerStart = performance.now();
|
||||
// Needed to load content config
|
||||
|
@ -88,7 +97,7 @@ export async function sync(
|
|||
const contentTypesGenerator = await createContentTypesGenerator({
|
||||
contentConfigObserver: globalContentConfigObserver,
|
||||
logging,
|
||||
fs,
|
||||
fs: fs ?? fsMod,
|
||||
settings,
|
||||
viteServer: tempViteServer,
|
||||
});
|
||||
|
@ -124,7 +133,7 @@ export async function sync(
|
|||
}
|
||||
|
||||
info(logging, 'content', `Types generated ${dim(getTimeStat(timerStart, performance.now()))}`);
|
||||
await setUpEnvTs({ settings, logging, fs });
|
||||
await setUpEnvTs({ settings, logging, fs: fs ?? fsMod });
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ describe('Astro Markdown URL', () => {
|
|||
it('trailingSlash: always', async () => {
|
||||
let fixture = await loadFixture({
|
||||
root: './fixtures/astro-markdown-url/',
|
||||
outDir: new URL('./fixtures/astro-markdown-url/with-subpath-always/', import.meta.url),
|
||||
outDir: './with-subpath-always',
|
||||
base: '/my-cool-base',
|
||||
trailingSlash: 'always',
|
||||
});
|
||||
|
@ -24,7 +24,7 @@ describe('Astro Markdown URL', () => {
|
|||
it('trailingSlash: never', async () => {
|
||||
let fixture = await loadFixture({
|
||||
root: './fixtures/astro-markdown-url/',
|
||||
outDir: new URL('./fixtures/astro-markdown-url/with-subpath-never/', import.meta.url),
|
||||
outDir: './with-subpath-never',
|
||||
base: '/my-cool-base',
|
||||
trailingSlash: 'never',
|
||||
});
|
||||
|
@ -39,7 +39,7 @@ describe('Astro Markdown URL', () => {
|
|||
it('trailingSlash: ignore', async () => {
|
||||
let fixture = await loadFixture({
|
||||
root: './fixtures/astro-markdown-url/',
|
||||
outDir: new URL('./fixtures/astro-markdown-url/with-subpath-ignore/', import.meta.url),
|
||||
outDir: './with-subpath-ignore',
|
||||
base: '/my-cool-base',
|
||||
trailingSlash: 'ignore',
|
||||
});
|
||||
|
@ -58,7 +58,7 @@ describe('Astro Markdown URL', () => {
|
|||
it('trailingSlash: always', async () => {
|
||||
let fixture = await loadFixture({
|
||||
root: './fixtures/astro-markdown-url/',
|
||||
outDir: new URL('./fixtures/astro-markdown-url/without-subpath-always/', import.meta.url),
|
||||
outDir: './without-subpath-always',
|
||||
trailingSlash: 'always',
|
||||
});
|
||||
await fixture.build();
|
||||
|
@ -72,7 +72,7 @@ describe('Astro Markdown URL', () => {
|
|||
it('trailingSlash: never', async () => {
|
||||
let fixture = await loadFixture({
|
||||
root: './fixtures/astro-markdown-url/',
|
||||
outDir: new URL('./fixtures/astro-markdown-url/without-subpath-never/', import.meta.url),
|
||||
outDir: './without-subpath-never',
|
||||
trailingSlash: 'never',
|
||||
});
|
||||
await fixture.build();
|
||||
|
@ -86,7 +86,7 @@ describe('Astro Markdown URL', () => {
|
|||
it('trailingSlash: ignore', async () => {
|
||||
let fixture = await loadFixture({
|
||||
root: './fixtures/astro-markdown-url/',
|
||||
outDir: new URL('./fixtures/astro-markdown-url/without-subpath-ignore/', import.meta.url),
|
||||
outDir: './without-subpath-ignore',
|
||||
trailingSlash: 'ignore',
|
||||
});
|
||||
await fixture.build();
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('astro sync', () => {
|
|||
},
|
||||
},
|
||||
};
|
||||
await fixture.sync({ fs: fsMock });
|
||||
await fixture.sync({}, { fs: fsMock });
|
||||
|
||||
const expectedTypesFile = new URL('.astro/types.d.ts', fixture.config.root).href;
|
||||
expect(writtenFiles).to.haveOwnProperty(expectedTypesFile);
|
||||
|
@ -55,7 +55,7 @@ describe('astro sync', () => {
|
|||
},
|
||||
},
|
||||
};
|
||||
await fixture.sync({ fs: fsMock });
|
||||
await fixture.sync({}, { fs: fsMock });
|
||||
|
||||
expect(writtenFiles, 'Did not try to update env.d.ts file.').to.haveOwnProperty(typesEnvPath);
|
||||
expect(writtenFiles[typesEnvPath]).to.include(`/// <reference path="../.astro/types.d.ts" />`);
|
||||
|
@ -79,7 +79,7 @@ describe('astro sync', () => {
|
|||
},
|
||||
},
|
||||
};
|
||||
await fixture.sync({ fs: fsMock });
|
||||
await fixture.sync({}, { fs: fsMock });
|
||||
|
||||
expect(writtenFiles, 'Did not try to write env.d.ts file.').to.haveOwnProperty(typesEnvPath);
|
||||
expect(writtenFiles[typesEnvPath]).to.include(`/// <reference types="astro/client" />`);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { expect } from 'chai';
|
||||
import { loadFixture, silentLogging } from './test-utils.js';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
import testAdapter from './test-adapter.js';
|
||||
import * as cheerio from 'cheerio';
|
||||
|
||||
|
@ -108,8 +108,7 @@ describe('Astro.clientAddress', () => {
|
|||
let devServer;
|
||||
|
||||
before(async () => {
|
||||
// We expect an error, so silence the output
|
||||
devServer = await fixture.startDevServer({ logging: silentLogging });
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { expect } from 'chai';
|
||||
import { loadFixture, silentLogging } from './test-utils.js';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
describe('Development Routing', () => {
|
||||
describe('No site config', () => {
|
||||
|
@ -10,9 +10,7 @@ describe('Development Routing', () => {
|
|||
|
||||
before(async () => {
|
||||
fixture = await loadFixture({ root: './fixtures/without-site-config/' });
|
||||
devServer = await fixture.startDevServer({
|
||||
logging: silentLogging,
|
||||
});
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { expect } from 'chai';
|
||||
import { load as cheerioLoad } from 'cheerio';
|
||||
import { loadFixture, silentLogging } from './test-utils.js';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
describe('Dynamic endpoint collision', () => {
|
||||
describe('build', () => {
|
||||
|
@ -31,9 +31,7 @@ describe('Dynamic endpoint collision', () => {
|
|||
root: './fixtures/dynamic-endpoint-collision/',
|
||||
});
|
||||
|
||||
devServer = await fixture.startDevServer({
|
||||
logging: silentLogging,
|
||||
});
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { expect } from 'chai';
|
||||
import { loadFixture, silentLogging } from './test-utils.js';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
describe('Errors in JavaScript', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
|
@ -15,9 +15,7 @@ describe('Errors in JavaScript', () => {
|
|||
logLevel: 'silent',
|
||||
},
|
||||
});
|
||||
devServer = await fixture.startDevServer({
|
||||
logging: silentLogging,
|
||||
});
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { expect } from 'chai';
|
||||
import { loadFixture, silentLogging } from './test-utils.js';
|
||||
import { loadFixture } from './test-utils.js';
|
||||
|
||||
describe('Can handle errors that are not instanceof Error', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
|
@ -12,9 +12,7 @@ describe('Can handle errors that are not instanceof Error', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/error-non-error',
|
||||
});
|
||||
devServer = await fixture.startDevServer({
|
||||
logging: silentLogging,
|
||||
});
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
|
|
@ -13,7 +13,7 @@ describe('Preview Routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/with-subpath-no-trailing-slash/',
|
||||
base: '/blog',
|
||||
outDir: new URL('./fixtures/with-subpath-no-trailing-slash/dist-4000/', import.meta.url),
|
||||
outDir: './dist-4000',
|
||||
build: {
|
||||
format: 'directory',
|
||||
},
|
||||
|
@ -41,9 +41,10 @@ describe('Preview Routing', () => {
|
|||
expect(response.redirected).to.equal(false);
|
||||
});
|
||||
|
||||
it('404 when loading subpath root without trailing slash', async () => {
|
||||
it('200 when loading subpath root without trailing slash', async () => {
|
||||
const response = await fixture.fetch('/blog');
|
||||
expect(response.status).to.equal(404);
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.redirected).to.equal(false);
|
||||
});
|
||||
|
||||
it('404 when loading another page with subpath used', async () => {
|
||||
|
@ -72,7 +73,7 @@ describe('Preview Routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/with-subpath-no-trailing-slash/',
|
||||
base: '/blog',
|
||||
outDir: new URL('./fixtures/with-subpath-no-trailing-slash/dist-4001/', import.meta.url),
|
||||
outDir: './dist-4001',
|
||||
trailingSlash: 'always',
|
||||
server: {
|
||||
port: 4001,
|
||||
|
@ -132,7 +133,7 @@ describe('Preview Routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/with-subpath-no-trailing-slash/',
|
||||
base: '/blog',
|
||||
outDir: new URL('./fixtures/with-subpath-no-trailing-slash/dist-4002/', import.meta.url),
|
||||
outDir: './dist-4002',
|
||||
trailingSlash: 'ignore',
|
||||
server: {
|
||||
port: 4002,
|
||||
|
@ -194,7 +195,7 @@ describe('Preview Routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/with-subpath-no-trailing-slash/',
|
||||
base: '/blog',
|
||||
outDir: new URL('./fixtures/with-subpath-no-trailing-slash/dist-4003/', import.meta.url),
|
||||
outDir: './dist-4003',
|
||||
build: {
|
||||
format: 'file',
|
||||
},
|
||||
|
@ -222,9 +223,10 @@ describe('Preview Routing', () => {
|
|||
expect(response.redirected).to.equal(false);
|
||||
});
|
||||
|
||||
it('404 when loading subpath root without trailing slash', async () => {
|
||||
it('200 when loading subpath root without trailing slash', async () => {
|
||||
const response = await fixture.fetch('/blog');
|
||||
expect(response.status).to.equal(404);
|
||||
expect(response.status).to.equal(200);
|
||||
expect(response.redirected).to.equal(false);
|
||||
});
|
||||
|
||||
it('404 when loading another page with subpath used', async () => {
|
||||
|
@ -253,7 +255,7 @@ describe('Preview Routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/with-subpath-no-trailing-slash/',
|
||||
base: '/blog',
|
||||
outDir: new URL('./fixtures/with-subpath-no-trailing-slash/dist-4004/', import.meta.url),
|
||||
outDir: './dist-4004',
|
||||
build: {
|
||||
format: 'file',
|
||||
},
|
||||
|
@ -316,7 +318,7 @@ describe('Preview Routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/with-subpath-no-trailing-slash/',
|
||||
base: '/blog',
|
||||
outDir: new URL('./fixtures/with-subpath-no-trailing-slash/dist-4005/', import.meta.url),
|
||||
outDir: './dist-4005',
|
||||
build: {
|
||||
format: 'file',
|
||||
},
|
||||
|
@ -379,7 +381,7 @@ describe('Preview Routing', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/with-subpath-no-trailing-slash/',
|
||||
base: '/blog',
|
||||
outDir: new URL('./fixtures/with-subpath-no-trailing-slash/dist-4006/', import.meta.url),
|
||||
outDir: './dist-4006',
|
||||
build: {
|
||||
format: 'file',
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { expect } from 'chai';
|
||||
import { load as cheerioLoad } from 'cheerio';
|
||||
import { isWindows, loadFixture, silentLogging } from './test-utils.js';
|
||||
import { isWindows, loadFixture } from './test-utils.js';
|
||||
|
||||
let fixture;
|
||||
|
||||
|
@ -108,9 +108,7 @@ describe('React Components', () => {
|
|||
let devServer;
|
||||
|
||||
before(async () => {
|
||||
devServer = await fixture.startDevServer({
|
||||
logging: silentLogging,
|
||||
});
|
||||
devServer = await fixture.startDevServer();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { execa } from 'execa';
|
|||
import fastGlob from 'fast-glob';
|
||||
import fs from 'node:fs';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import { check } from '../dist/cli/check/index.js';
|
||||
|
@ -10,8 +11,7 @@ import build from '../dist/core/build/index.js';
|
|||
import { RESOLVED_SPLIT_MODULE_ID } from '../dist/core/build/plugins/plugin-ssr.js';
|
||||
import { getVirtualModulePageNameFromPath } from '../dist/core/build/plugins/util.js';
|
||||
import { makeSplitEntryPointFileName } from '../dist/core/build/static-build.js';
|
||||
import { openConfig } from '../dist/core/config/config.js';
|
||||
import { createSettings } from '../dist/core/config/index.js';
|
||||
import { mergeConfig, resolveConfig } from '../dist/core/config/index.js';
|
||||
import dev from '../dist/core/dev/index.js';
|
||||
import { nodeLogDestination } from '../dist/core/logger/node.js';
|
||||
import preview from '../dist/core/preview/index.js';
|
||||
|
@ -28,7 +28,7 @@ process.env.ASTRO_TELEMETRY_DISABLED = true;
|
|||
/**
|
||||
* @typedef {import('undici').Response} Response
|
||||
* @typedef {import('../src/core/dev/dev').DedvServer} DevServer
|
||||
* @typedef {import('../src/@types/astro').AstroConfig} AstroConfig
|
||||
* @typedef {import('../src/@types/astro').AstroInlineConfig & { root?: string | URL }} AstroInlineConfig
|
||||
* @typedef {import('../src/core/preview/index').PreviewServer} PreviewServer
|
||||
* @typedef {import('../src/core/app/index').App} App
|
||||
* @typedef {import('../src/cli/check/index').AstroChecker} AstroChecker
|
||||
|
@ -43,12 +43,13 @@ process.env.ASTRO_TELEMETRY_DISABLED = true;
|
|||
* @property {(path: string, updater: (content: string) => string) => Promise<void>} writeFile
|
||||
* @property {(path: string) => Promise<string[]>} readdir
|
||||
* @property {(pattern: string) => Promise<string[]>} glob
|
||||
* @property {() => Promise<DevServer>} startDevServer
|
||||
* @property {() => Promise<PreviewServer>} preview
|
||||
* @property {typeof dev} startDevServer
|
||||
* @property {typeof preview} preview
|
||||
* @property {() => Promise<void>} clean
|
||||
* @property {() => Promise<App>} loadTestAdapterApp
|
||||
* @property {() => Promise<void>} onNextChange
|
||||
* @property {(opts: CheckPayload) => Promise<AstroChecker>} check
|
||||
* @property {typeof check} check
|
||||
* @property {typeof sync} sync
|
||||
*
|
||||
* This function returns an instance of the Check
|
||||
*
|
||||
|
@ -82,7 +83,7 @@ export const silentLogging = {
|
|||
|
||||
/**
|
||||
* Load Astro fixture
|
||||
* @param {AstroConfig} inlineConfig Astro config partial (note: must specify `root`)
|
||||
* @param {AstroInlineConfig} inlineConfig Astro config partial (note: must specify `root`)
|
||||
* @returns {Promise<Fixture>} The fixture. Has the following properties:
|
||||
* .config - Returns the final config. Will be automatically passed to the methods below:
|
||||
*
|
||||
|
@ -103,50 +104,25 @@ export const silentLogging = {
|
|||
export async function loadFixture(inlineConfig) {
|
||||
if (!inlineConfig?.root) throw new Error("Must provide { root: './fixtures/...' }");
|
||||
|
||||
// load config
|
||||
let cwd = inlineConfig.root;
|
||||
delete inlineConfig.root;
|
||||
if (typeof cwd === 'string') {
|
||||
try {
|
||||
cwd = new URL(cwd.replace(/\/?$/, '/'));
|
||||
} catch (err1) {
|
||||
cwd = new URL(cwd.replace(/\/?$/, '/'), import.meta.url);
|
||||
}
|
||||
// Silent by default during tests to not pollute the console output
|
||||
inlineConfig.logLevel = 'silent';
|
||||
|
||||
let root = inlineConfig.root;
|
||||
// Handle URL, should already be absolute so just convert to path
|
||||
if (typeof root !== 'string') {
|
||||
root = fileURLToPath(root);
|
||||
}
|
||||
|
||||
/** @type {import('../src/core/logger/core').LogOptions} */
|
||||
const logging = defaultLogging;
|
||||
|
||||
// Handle "file:///C:/Users/fred", convert to "C:/Users/fred"
|
||||
else if (root.startsWith('file://')) {
|
||||
root = fileURLToPath(new URL(root));
|
||||
}
|
||||
// Handle "./fixtures/...", convert to absolute path
|
||||
else if (!path.isAbsolute(root)) {
|
||||
root = fileURLToPath(new URL(root, import.meta.url));
|
||||
}
|
||||
inlineConfig = { ...inlineConfig, root };
|
||||
// Load the config.
|
||||
let { astroConfig: config } = await openConfig({
|
||||
cwd: fileURLToPath(cwd),
|
||||
logging,
|
||||
cmd: 'dev',
|
||||
});
|
||||
config = merge(config, { ...inlineConfig, root: cwd });
|
||||
|
||||
// HACK: the inline config doesn't run through config validation where these normalizations usually occur
|
||||
if (typeof inlineConfig.site === 'string') {
|
||||
config.site = new URL(inlineConfig.site);
|
||||
}
|
||||
if (inlineConfig.base && !inlineConfig.base.endsWith('/')) {
|
||||
config.base = inlineConfig.base + '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* The dev/build/sync/check commands run integrations' `astro:config:setup` hook that could mutate
|
||||
* the `AstroSettings`. This function helps to create a fresh settings object that is used by the
|
||||
* command functions below to prevent tests from polluting each other.
|
||||
*/
|
||||
const getSettings = async () => {
|
||||
let settings = createSettings(config, fileURLToPath(cwd));
|
||||
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');
|
||||
settings.renderers.unshift(jsxRenderer);
|
||||
}
|
||||
return settings;
|
||||
};
|
||||
const { astroConfig: config } = await resolveConfig(inlineConfig, 'dev');
|
||||
|
||||
const resolveUrl = (url) =>
|
||||
`http://${config.server.host || 'localhost'}:${config.server.port}${url.replace(/^\/?/, '/')}`;
|
||||
|
@ -177,17 +153,19 @@ export async function loadFixture(inlineConfig) {
|
|||
let devServer;
|
||||
|
||||
return {
|
||||
build: async (opts = {}) => {
|
||||
build: async (extraInlineConfig = {}) => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
return build(await getSettings('build'), { logging, ...opts });
|
||||
return build(mergeConfig(inlineConfig, extraInlineConfig));
|
||||
},
|
||||
sync: async (extraInlineConfig = {}, opts) => {
|
||||
return sync(mergeConfig(inlineConfig, extraInlineConfig), opts);
|
||||
},
|
||||
sync: async (opts) => sync(await getSettings('build'), { logging, fs, ...opts }),
|
||||
check: async (opts) => {
|
||||
return await check(await getSettings('build'), { logging, ...opts });
|
||||
return await check(opts);
|
||||
},
|
||||
startDevServer: async (opts = {}) => {
|
||||
startDevServer: async (extraInlineConfig = {}) => {
|
||||
process.env.NODE_ENV = 'development';
|
||||
devServer = await dev(await getSettings('dev'), { logging, ...opts });
|
||||
devServer = await dev(mergeConfig(inlineConfig, extraInlineConfig));
|
||||
config.server.host = parseAddressToHost(devServer.address.address); // update host
|
||||
config.server.port = devServer.address.port; // update port
|
||||
return devServer;
|
||||
|
@ -207,9 +185,9 @@ export async function loadFixture(inlineConfig) {
|
|||
throw err;
|
||||
}
|
||||
},
|
||||
preview: async (opts = {}) => {
|
||||
preview: async (extraInlineConfig = {}) => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
const previewServer = await preview(await getSettings('build'), { logging, ...opts });
|
||||
const previewServer = await preview(mergeConfig(inlineConfig, extraInlineConfig));
|
||||
config.server.host = parseAddressToHost(previewServer.host); // update host
|
||||
config.server.port = previewServer.port; // update port
|
||||
return previewServer;
|
||||
|
@ -282,32 +260,6 @@ function parseAddressToHost(address) {
|
|||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic object merge utility. Returns new copy of merged Object.
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @returns {Object}
|
||||
*/
|
||||
function merge(a, b) {
|
||||
const allKeys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
||||
const c = {};
|
||||
for (const k of allKeys) {
|
||||
const needsObjectMerge =
|
||||
typeof a[k] === 'object' &&
|
||||
typeof b[k] === 'object' &&
|
||||
(Object.keys(a[k]).length || Object.keys(b[k]).length) &&
|
||||
!Array.isArray(a[k]) &&
|
||||
!Array.isArray(b[k]);
|
||||
if (needsObjectMerge) {
|
||||
c[k] = merge(a[k] || {}, b[k] || {});
|
||||
continue;
|
||||
}
|
||||
c[k] = a[k];
|
||||
if (b[k] !== undefined) c[k] = b[k];
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
const cliPath = fileURLToPath(new URL('../astro.js', import.meta.url));
|
||||
|
||||
/** Returns a process running the Astro CLI. */
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
import { expect } from 'chai';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { defaultLogging } from '../test-utils.js';
|
||||
import { openConfig } from '../../../dist/core/config/index.js';
|
||||
import { flagsToAstroInlineConfig } from '../../../dist/cli/flags.js';
|
||||
import { resolveConfig } 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: defaultLogging,
|
||||
});
|
||||
function resolveConfigWithFlags(flags) {
|
||||
return resolveConfig(
|
||||
flagsToAstroInlineConfig({
|
||||
root: cwd,
|
||||
...flags,
|
||||
}),
|
||||
'dev'
|
||||
);
|
||||
}
|
||||
|
||||
describe('host', () => {
|
||||
it('can be specified via --host flag', async () => {
|
||||
const projectRootURL = new URL('../../fixtures/astro-basic/', import.meta.url);
|
||||
const { astroConfig } = await openConfigWithFlags({
|
||||
const { astroConfig } = await resolveConfigWithFlags({
|
||||
root: fileURLToPath(projectRootURL),
|
||||
host: true,
|
||||
});
|
||||
|
@ -32,7 +33,7 @@ describe('config.server', () => {
|
|||
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({
|
||||
const { astroConfig } = await resolveConfigWithFlags({
|
||||
root: fileURLToPath(projectRootURL),
|
||||
config: configFileURL,
|
||||
});
|
||||
|
@ -44,7 +45,7 @@ describe('config.server', () => {
|
|||
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({
|
||||
const { astroConfig } = await resolveConfigWithFlags({
|
||||
root: fileURLToPath(projectRootURL),
|
||||
config: configFileURL,
|
||||
});
|
||||
|
@ -57,7 +58,7 @@ describe('config.server', () => {
|
|||
const projectRootURL = new URL('../../fixtures/astro-basic/', import.meta.url);
|
||||
const configFileURL = './does-not-exist.mjs';
|
||||
try {
|
||||
await openConfigWithFlags({
|
||||
await resolveConfigWithFlags({
|
||||
root: fileURLToPath(projectRootURL),
|
||||
config: configFileURL,
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ 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/index.js';
|
||||
import { validateConfig } from '../../../dist/core/config/config.js';
|
||||
|
||||
describe('Config Validation', () => {
|
||||
it('empty user config is valid', async () => {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { createSettings, openConfig } from '../../../dist/core/config/index.js';
|
||||
import { runInContainer } from '../../../dist/core/dev/index.js';
|
||||
import { createFs, defaultLogging } from '../test-utils.js';
|
||||
import { createFs, runInContainer } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/tailwindcss-ts/', import.meta.url);
|
||||
|
||||
|
@ -20,16 +18,7 @@ describe('Astro config formats', () => {
|
|||
root
|
||||
);
|
||||
|
||||
const { astroConfig } = await openConfig({
|
||||
cwd: root,
|
||||
flags: {},
|
||||
cmd: 'dev',
|
||||
logging: defaultLogging,
|
||||
fsMod: fs,
|
||||
});
|
||||
const settings = createSettings(astroConfig);
|
||||
|
||||
await runInContainer({ fs, root, settings }, () => {
|
||||
await runInContainer({ fs, inlineConfig: { root: fileURLToPath(root) } }, () => {
|
||||
expect(true).to.equal(
|
||||
true,
|
||||
'We were able to get into the container which means the config loaded.'
|
||||
|
|
|
@ -2,9 +2,8 @@ import { fileURLToPath } from 'node:url';
|
|||
import nodeFS from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import { runInContainer } from '../../../dist/core/dev/index.js';
|
||||
import { attachContentServerListeners } from '../../../dist/content/index.js';
|
||||
import { createFs, triggerFSEvent } from '../test-utils.js';
|
||||
import { createFs, runInContainer, triggerFSEvent } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
|
@ -53,7 +52,7 @@ describe('frontmatter', () => {
|
|||
root
|
||||
);
|
||||
|
||||
await runInContainer({ fs, root }, async (container) => {
|
||||
await runInContainer({ fs, inlineConfig: { root: fileURLToPath(root) } }, async (container) => {
|
||||
await attachContentServerListeners(container);
|
||||
|
||||
fs.writeFileFromRootSync(
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { expect } from 'chai';
|
||||
|
||||
import { runInContainer } from '../../../dist/core/dev/index.js';
|
||||
import { createFs, createRequestAndResponse } from '../test-utils.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
|
@ -19,8 +18,8 @@ describe('base configuration', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
base: '/docs',
|
||||
trailingSlash: 'never',
|
||||
},
|
||||
|
@ -48,8 +47,8 @@ describe('base configuration', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
base: '/docs',
|
||||
trailingSlash: 'never',
|
||||
},
|
||||
|
@ -79,8 +78,8 @@ describe('base configuration', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
base: '/docs',
|
||||
trailingSlash: 'never',
|
||||
},
|
||||
|
@ -108,8 +107,8 @@ describe('base configuration', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
base: '/docs',
|
||||
trailingSlash: 'never',
|
||||
},
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
import { expect } from 'chai';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { validateConfig } from '../../../dist/core/config/config.js';
|
||||
import { createSettings } from '../../../dist/core/config/index.js';
|
||||
import { sync as _sync } from '../../../dist/core/sync/index.js';
|
||||
import { createFsWithFallback, defaultLogging } from '../test-utils.js';
|
||||
import { createFsWithFallback } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/content-mixed-errors/', import.meta.url);
|
||||
const logging = defaultLogging;
|
||||
|
||||
async function sync({ fs, config = {} }) {
|
||||
const astroConfig = await validateConfig(config, fileURLToPath(root), 'prod');
|
||||
const settings = createSettings(astroConfig, fileURLToPath(root));
|
||||
|
||||
return _sync(settings, { logging, fs });
|
||||
return _sync({ ...config, root: fileURLToPath(root), logLevel: 'silent' }, { fs });
|
||||
}
|
||||
|
||||
describe('Content Collections - mixed content errors', () => {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import { expect } from 'chai';
|
||||
import * as cheerio from 'cheerio';
|
||||
import os from 'node:os';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import mdx from '../../../../integrations/mdx/dist/index.js';
|
||||
import { attachContentServerListeners } from '../../../dist/content/server-listeners.js';
|
||||
import { runInContainer } from '../../../dist/core/dev/index.js';
|
||||
import { createFsWithFallback, createRequestAndResponse } from '../test-utils.js';
|
||||
import { createFsWithFallback, createRequestAndResponse, runInContainer } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/content/', import.meta.url);
|
||||
|
||||
const describe = os.platform() === 'win32' ? global.describe.skip : global.describe;
|
||||
|
||||
/** @type {typeof runInContainer} */
|
||||
async function runInContainerWithContentListeners(params, callback) {
|
||||
return await runInContainer(params, async (container) => {
|
||||
await attachContentServerListeners(container);
|
||||
|
@ -56,9 +56,8 @@ describe('Content Collections - render()', () => {
|
|||
await runInContainerWithContentListeners(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
integrations: [mdx()],
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
vite: { server: { middlewareMode: true } },
|
||||
},
|
||||
},
|
||||
|
@ -129,9 +128,8 @@ description: Astro is launching this week!
|
|||
await runInContainerWithContentListeners(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
integrations: [mdx()],
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
vite: { server: { middlewareMode: true } },
|
||||
},
|
||||
},
|
||||
|
@ -200,9 +198,8 @@ description: Astro is launching this week!
|
|||
await runInContainerWithContentListeners(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
integrations: [mdx()],
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
vite: { server: { middlewareMode: true } },
|
||||
},
|
||||
},
|
||||
|
@ -270,9 +267,8 @@ description: Astro is launching this week!
|
|||
await runInContainerWithContentListeners(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
integrations: [mdx()],
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
vite: { server: { middlewareMode: true } },
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import { expect } from 'chai';
|
||||
import * as cheerio from 'cheerio';
|
||||
|
||||
import { runInContainer } from '../../../dist/core/dev/index.js';
|
||||
import { createFs, createRequestAndResponse, triggerFSEvent } from '../test-utils.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
createFs,
|
||||
createRequestAndResponse,
|
||||
triggerFSEvent,
|
||||
runInContainer,
|
||||
} from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
|
@ -25,7 +29,7 @@ describe('dev container', () => {
|
|||
root
|
||||
);
|
||||
|
||||
await runInContainer({ fs, root }, async (container) => {
|
||||
await runInContainer({ fs, inlineConfig: { root: fileURLToPath(root) } }, async (container) => {
|
||||
const { req, res, text } = createRequestAndResponse({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
|
@ -60,7 +64,7 @@ describe('dev container', () => {
|
|||
root
|
||||
);
|
||||
|
||||
await runInContainer({ fs, root }, async (container) => {
|
||||
await runInContainer({ fs, inlineConfig: { root: fileURLToPath(root) } }, async (container) => {
|
||||
let r = createRequestAndResponse({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
|
@ -119,8 +123,8 @@ describe('dev container', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
output: 'server',
|
||||
integrations: [
|
||||
{
|
||||
|
@ -170,8 +174,8 @@ describe('dev container', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
output: 'server',
|
||||
integrations: [
|
||||
{
|
||||
|
@ -223,8 +227,8 @@ describe('dev container', () => {
|
|||
it('items in public/ are not available from root when using a base', async () => {
|
||||
await runInContainer(
|
||||
{
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
base: '/sub/',
|
||||
},
|
||||
},
|
||||
|
@ -256,7 +260,7 @@ describe('dev container', () => {
|
|||
});
|
||||
|
||||
it('items in public/ are available from root when not using a base', async () => {
|
||||
await runInContainer({ root }, async (container) => {
|
||||
await runInContainer({ inlineConfig: { root: fileURLToPath(root) } }, async (container) => {
|
||||
// Try the root path
|
||||
let r = createRequestAndResponse({
|
||||
method: 'GET',
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { expect } from 'chai';
|
||||
import * as cheerio from 'cheerio';
|
||||
|
||||
import { runInContainer } from '../../../dist/core/dev/index.js';
|
||||
import { createFs, createRequestAndResponse } from '../test-utils.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
|
@ -65,8 +64,8 @@ describe('head injection', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
vite: { server: { middlewareMode: true } },
|
||||
},
|
||||
},
|
||||
|
@ -154,8 +153,8 @@ describe('head injection', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
vite: { server: { middlewareMode: true } },
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
import { expect } from 'chai';
|
||||
|
||||
import { runInContainer } from '../../../dist/core/dev/index.js';
|
||||
import { createFs, createRequestAndResponse, silentLogging } from '../test-utils.js';
|
||||
import svelte from '../../../../integrations/svelte/dist/index.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
describe('dev container', () => {
|
||||
describe('hydration', () => {
|
||||
it('should not crash when reassigning a hydrated component', async () => {
|
||||
const fs = createFs(
|
||||
{
|
||||
|
@ -31,10 +29,9 @@ describe('dev container', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
logging: silentLogging,
|
||||
userConfig: {
|
||||
integrations: [svelte()],
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
logLevel: 'silent',
|
||||
},
|
||||
},
|
||||
async (container) => {
|
||||
|
|
|
@ -2,18 +2,12 @@ import { expect } from 'chai';
|
|||
import * as cheerio from 'cheerio';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
import { createSettings, openConfig } from '../../../dist/core/config/index.js';
|
||||
import {
|
||||
createContainerWithAutomaticRestart,
|
||||
isStarted,
|
||||
startContainer,
|
||||
} from '../../../dist/core/dev/index.js';
|
||||
import {
|
||||
createFs,
|
||||
createRequestAndResponse,
|
||||
defaultLogging,
|
||||
triggerFSEvent,
|
||||
} from '../test-utils.js';
|
||||
import { createFs, createRequestAndResponse, triggerFSEvent } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
|
@ -36,8 +30,9 @@ describe('dev container restarts', () => {
|
|||
root
|
||||
);
|
||||
|
||||
let restart = await createContainerWithAutomaticRestart({
|
||||
params: { fs, root },
|
||||
const restart = await createContainerWithAutomaticRestart({
|
||||
fs,
|
||||
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
|
||||
});
|
||||
|
||||
try {
|
||||
|
@ -99,8 +94,9 @@ describe('dev container restarts', () => {
|
|||
root
|
||||
);
|
||||
|
||||
let restart = await createContainerWithAutomaticRestart({
|
||||
params: { fs, root },
|
||||
const restart = await createContainerWithAutomaticRestart({
|
||||
fs,
|
||||
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
|
||||
});
|
||||
await startContainer(restart.container);
|
||||
expect(isStarted(restart.container)).to.equal(true);
|
||||
|
@ -127,16 +123,9 @@ describe('dev container restarts', () => {
|
|||
troot
|
||||
);
|
||||
|
||||
const { astroConfig } = await openConfig({
|
||||
cwd: troot,
|
||||
flags: {},
|
||||
cmd: 'dev',
|
||||
logging: defaultLogging,
|
||||
});
|
||||
const settings = createSettings(astroConfig);
|
||||
|
||||
let restart = await createContainerWithAutomaticRestart({
|
||||
params: { fs, root, settings },
|
||||
const restart = await createContainerWithAutomaticRestart({
|
||||
fs,
|
||||
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
|
||||
});
|
||||
await startContainer(restart.container);
|
||||
expect(isStarted(restart.container)).to.equal(true);
|
||||
|
@ -161,16 +150,9 @@ describe('dev container restarts', () => {
|
|||
root
|
||||
);
|
||||
|
||||
const { astroConfig } = await openConfig({
|
||||
cwd: root,
|
||||
flags: {},
|
||||
cmd: 'dev',
|
||||
logging: defaultLogging,
|
||||
});
|
||||
const settings = createSettings(astroConfig, fileURLToPath(root));
|
||||
|
||||
let restart = await createContainerWithAutomaticRestart({
|
||||
params: { fs, root, settings },
|
||||
const restart = await createContainerWithAutomaticRestart({
|
||||
fs,
|
||||
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
|
||||
});
|
||||
await startContainer(restart.container);
|
||||
expect(isStarted(restart.container)).to.equal(true);
|
||||
|
@ -193,16 +175,9 @@ describe('dev container restarts', () => {
|
|||
root
|
||||
);
|
||||
|
||||
const { astroConfig } = await openConfig({
|
||||
cwd: root,
|
||||
flags: {},
|
||||
cmd: 'dev',
|
||||
logging: defaultLogging,
|
||||
});
|
||||
const settings = createSettings(astroConfig, fileURLToPath(root));
|
||||
|
||||
let restart = await createContainerWithAutomaticRestart({
|
||||
params: { fs, root, settings },
|
||||
const restart = await createContainerWithAutomaticRestart({
|
||||
fs,
|
||||
inlineConfig: { root: fileURLToPath(root), logLevel: 'silent' },
|
||||
});
|
||||
await startContainer(restart.container);
|
||||
expect(isStarted(restart.container)).to.equal(true);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { expect } from 'chai';
|
||||
import * as cheerio from 'cheerio';
|
||||
|
||||
import { runInContainer } from '../../../dist/core/dev/index.js';
|
||||
import { createFs, createRequestAndResponse, silentLogging } from '../test-utils.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import svelte from '../../../../integrations/svelte/dist/index.js';
|
||||
import { createFs, createRequestAndResponse, runInContainer } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
|
@ -31,9 +30,9 @@ describe('core/render components', () => {
|
|||
await runInContainer(
|
||||
{
|
||||
fs,
|
||||
root,
|
||||
logging: silentLogging,
|
||||
userConfig: {
|
||||
inlineConfig: {
|
||||
root: fileURLToPath(root),
|
||||
logLevel: 'silent',
|
||||
integrations: [svelte()],
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import { expect } from 'chai';
|
||||
|
||||
import { createFs } from '../test-utils.js';
|
||||
import { createRouteManifest } from '../../../dist/core/routing/manifest/create.js';
|
||||
import { createDefaultDevSettings } from '../../../dist/core/config/index.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { defaultLogging } from '../test-utils.js';
|
||||
import { createRouteManifest } from '../../../dist/core/routing/manifest/create.js';
|
||||
import { createBasicSettings, createFs, defaultLogging } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
|
@ -16,13 +14,11 @@ describe('routing - createRouteManifest', () => {
|
|||
},
|
||||
root
|
||||
);
|
||||
const settings = await createDefaultDevSettings(
|
||||
{
|
||||
base: '/search',
|
||||
trailingSlash: 'never',
|
||||
},
|
||||
root
|
||||
);
|
||||
const settings = await createBasicSettings({
|
||||
root: fileURLToPath(root),
|
||||
base: '/search',
|
||||
trailingSlash: 'never',
|
||||
});
|
||||
const manifest = createRouteManifest({
|
||||
cwd: fileURLToPath(root),
|
||||
settings,
|
||||
|
@ -41,17 +37,15 @@ describe('routing - createRouteManifest', () => {
|
|||
},
|
||||
root
|
||||
);
|
||||
const settings = await createDefaultDevSettings(
|
||||
{
|
||||
base: '/search',
|
||||
trailingSlash: 'never',
|
||||
redirects: {
|
||||
'/blog/[...slug]': '/',
|
||||
'/blog/contributing': '/another',
|
||||
},
|
||||
const settings = await createBasicSettings({
|
||||
root: fileURLToPath(root),
|
||||
base: '/search',
|
||||
trailingSlash: 'never',
|
||||
redirects: {
|
||||
'/blog/[...slug]': '/',
|
||||
'/blog/contributing': '/another',
|
||||
},
|
||||
root
|
||||
);
|
||||
});
|
||||
const manifest = createRouteManifest({
|
||||
cwd: fileURLToPath(root),
|
||||
settings,
|
||||
|
@ -70,15 +64,13 @@ describe('routing - createRouteManifest', () => {
|
|||
},
|
||||
root
|
||||
);
|
||||
const settings = await createDefaultDevSettings(
|
||||
{
|
||||
trailingSlash: 'never',
|
||||
redirects: {
|
||||
'/foo': '/bar',
|
||||
},
|
||||
const settings = await createBasicSettings({
|
||||
root: fileURLToPath(root),
|
||||
trailingSlash: 'never',
|
||||
redirects: {
|
||||
'/foo': '/bar',
|
||||
},
|
||||
root
|
||||
);
|
||||
});
|
||||
const manifest = createRouteManifest(
|
||||
{
|
||||
cwd: fileURLToPath(root),
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
// @ts-check
|
||||
import { createFs, createRequestAndResponse, defaultLogging } from '../test-utils.js';
|
||||
import {
|
||||
createBasicSettings,
|
||||
createFs,
|
||||
createRequestAndResponse,
|
||||
defaultLogging,
|
||||
} from '../test-utils.js';
|
||||
import { createRouteManifest, matchAllRoutes } from '../../../dist/core/routing/index.js';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createViteLoader } from '../../../dist/core/module-loader/vite.js';
|
||||
|
@ -127,16 +131,17 @@ describe('Route matching', () => {
|
|||
|
||||
before(async () => {
|
||||
const fs = createFs(fileSystem, root);
|
||||
settings = await createBasicSettings({
|
||||
root: fileURLToPath(root),
|
||||
trailingSlash: 'never',
|
||||
output: 'hybrid',
|
||||
adapter: testAdapter(),
|
||||
});
|
||||
container = await createContainer({
|
||||
fs,
|
||||
root,
|
||||
userConfig: {
|
||||
trailingSlash: 'never',
|
||||
output: 'hybrid',
|
||||
adapter: testAdapter(),
|
||||
},
|
||||
settings,
|
||||
logging: defaultLogging,
|
||||
});
|
||||
settings = container.settings;
|
||||
|
||||
const loader = createViteLoader(container.viteServer);
|
||||
const manifest = createDevelopmentManifest(container.settings);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { expect } from 'chai';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createContainer } from '../../../dist/core/dev/index.js';
|
||||
import { createViteLoader } from '../../../dist/core/module-loader/index.js';
|
||||
import { createBasicSettings, defaultLogging } from '../test-utils.js';
|
||||
|
||||
const root = new URL('../../fixtures/alias/', import.meta.url);
|
||||
|
||||
|
@ -9,7 +11,8 @@ describe('<Code />', () => {
|
|||
let container;
|
||||
let mod;
|
||||
before(async () => {
|
||||
container = await createContainer({ root });
|
||||
const settings = await createBasicSettings({ root: fileURLToPath(root) });
|
||||
container = await createContainer({ settings, logging: defaultLogging });
|
||||
const loader = createViteLoader(container.viteServer);
|
||||
mod = await loader.import('astro/components/Shiki.js');
|
||||
});
|
||||
|
|
|
@ -8,6 +8,9 @@ import { getDefaultClientDirectives } from '../../dist/core/client-directive/ind
|
|||
import { nodeLogDestination } from '../../dist/core/logger/node.js';
|
||||
import { createEnvironment } from '../../dist/core/render/index.js';
|
||||
import { RouteCache } from '../../dist/core/render/route-cache.js';
|
||||
import { resolveConfig } from '../../dist/core/config/index.js';
|
||||
import { createBaseSettings } from '../../dist/core/config/settings.js';
|
||||
import { createContainer } from '../../dist/core/dev/container.js';
|
||||
import { unixify } from './correct-path.js';
|
||||
|
||||
/** @type {import('../../src/core/logger/core').LogOptions} */
|
||||
|
@ -189,3 +192,42 @@ export function createBasicEnvironment(options = {}) {
|
|||
streaming: options.streaming ?? true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('../../src/@types/astro.js').AstroInlineConfig} inlineConfig
|
||||
* @returns {Promise<import('../../src/@types/astro.js').AstroSettings>}
|
||||
*/
|
||||
export async function createBasicSettings(inlineConfig = {}) {
|
||||
if (!inlineConfig.root) {
|
||||
inlineConfig.root = fileURLToPath(new URL('.', import.meta.url));
|
||||
}
|
||||
const { astroConfig } = await resolveConfig(inlineConfig, 'dev');
|
||||
return createBaseSettings(astroConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* fs?: typeof realFS,
|
||||
* inlineConfig?: import('../../src/@types/astro.js').AstroInlineConfig,
|
||||
* logging?: import('../../src/core/logger/core').LogOptions,
|
||||
* }} RunInContainerOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {RunInContainerOptions} options
|
||||
* @param {(container: import('../../src/core/dev/container.js').Container) => Promise<void> | void} callback
|
||||
*/
|
||||
export async function runInContainer(options = {}, callback) {
|
||||
const settings = await createBasicSettings(options.inlineConfig ?? {});
|
||||
const container = await createContainer({
|
||||
fs: options?.fs ?? realFS,
|
||||
settings,
|
||||
inlineConfig: options.inlineConfig ?? {},
|
||||
logging: defaultLogging,
|
||||
});
|
||||
try {
|
||||
await callback(container);
|
||||
} finally {
|
||||
await container.close();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { expect } from 'chai';
|
||||
|
||||
import { createDefaultDevSettings } from '../../../dist/core/config/index.js';
|
||||
import { createLoader } from '../../../dist/core/module-loader/index.js';
|
||||
import { createRouteManifest } from '../../../dist/core/routing/index.js';
|
||||
import { createComponent, render } from '../../../dist/runtime/server/index.js';
|
||||
|
@ -8,6 +7,7 @@ import { createController, handleRequest } from '../../../dist/vite-plugin-astro
|
|||
import {
|
||||
createAstroModule,
|
||||
createBasicEnvironment,
|
||||
createBasicSettings,
|
||||
createFs,
|
||||
createRequestAndResponse,
|
||||
defaultLogging,
|
||||
|
@ -15,7 +15,7 @@ import {
|
|||
|
||||
async function createDevEnvironment(overrides = {}) {
|
||||
const env = createBasicEnvironment();
|
||||
env.settings = await createDefaultDevSettings({}, '/');
|
||||
env.settings = await createBasicSettings({ root: '/' });
|
||||
env.settings.renderers = [];
|
||||
env.loader = createLoader();
|
||||
Object.assign(env, overrides);
|
||||
|
|
|
@ -23,7 +23,7 @@ describe('getStaticPaths', () => {
|
|||
const $ = cheerio.load(html);
|
||||
expect($('p').text()).to.equal('First mdx file');
|
||||
expect($('#one').text()).to.equal('hello', 'Frontmatter included');
|
||||
expect($('#url').text()).to.equal('/src/content/1.mdx', 'url is included');
|
||||
expect($('#url').text()).to.equal('src/content/1.mdx', 'url is included');
|
||||
expect($('#file').text()).to.contain(
|
||||
'fixtures/mdx-get-static-paths/src/content/1.mdx',
|
||||
'file is included'
|
||||
|
|
|
@ -96,7 +96,7 @@ describe('MDX plugins', () => {
|
|||
it('ignores string-based plugins in markdown config', async () => {
|
||||
const fixture = await buildFixture({
|
||||
markdown: {
|
||||
remarkPlugins: [['remark-toc']],
|
||||
remarkPlugins: [['remark-toc', {}]],
|
||||
},
|
||||
integrations: [mdx()],
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue