diff --git a/.changeset/many-pears-explode.md b/.changeset/many-pears-explode.md new file mode 100644 index 000000000000..1e2a30edc227 --- /dev/null +++ b/.changeset/many-pears-explode.md @@ -0,0 +1,35 @@ +--- +'astro': major +--- + +Export experimental `dev`, `build`, `preview`, and `sync` APIs from `astro`. These APIs allow you to run Astro's commands programmatically, and replaces the previous entry point that runs the Astro CLI. + +While these APIs are experimental, the inline config parameter is relatively stable without foreseeable changes. However, the returned results of these APIs are more likely to change in the future. + +```ts +import { dev, build, preview, sync, type AstroInlineConfig } from 'astro'; + +// Inline Astro config object. +// Provide a path to a configuration file to load or set options directly inline. +const inlineConfig: AstroInlineConfig = { + // Inline-specific options... + configFile: './astro.config.mjs', + logLevel: 'info', + // Standard Astro config options... + site: 'https://example.com', +}; + +// Start the Astro dev server +const devServer = await dev(inlineConfig); +await devServer.stop(); + +// Build your Astro project +await build(inlineConfig); + +// Preview your built project +const previewServer = await preview(inlineConfig); +await previewServer.stop(); + +// Generate types for your Astro project +await sync(inlineConfig); +``` diff --git a/packages/astro/index.d.ts b/packages/astro/index.d.ts new file mode 100644 index 000000000000..a9e679be17d9 --- /dev/null +++ b/packages/astro/index.d.ts @@ -0,0 +1,2 @@ +export type * from './dist/@types/astro.js'; +export * from './dist/core/index.js'; diff --git a/packages/astro/package.json b/packages/astro/package.json index 76e67fb92289..caf49c64ea13 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -12,7 +12,7 @@ }, "bugs": "https://github.com/withastro/astro/issues", "homepage": "https://astro.build", - "types": "./dist/@types/astro.d.ts", + "types": "./index.d.ts", "typesVersions": { "*": { "app": [ @@ -31,8 +31,8 @@ }, "exports": { ".": { - "types": "./dist/@types/astro.d.ts", - "default": "./astro.js" + "types": "./index.d.ts", + "default": "./dist/core/index.js" }, "./env": "./env.d.ts", "./types": "./types.d.ts", @@ -90,6 +90,7 @@ "tsconfigs", "dist", "astro.js", + "index.d.ts", "config.d.ts", "config.mjs", "zod.d.ts", diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 0449b89d47ad..3007a7de2c61 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -143,7 +143,7 @@ export interface CLIFlags { */ export interface AstroGlobal< Props extends Record = Record, - Self = AstroComponentFactory + Self = AstroComponentFactory, > extends AstroGlobalPartial, AstroSharedContext { /** @@ -1404,13 +1404,39 @@ export interface AstroConfig extends AstroConfigType { // TypeScript still confirms zod validation matches this type. integrations: AstroIntegration[]; } +/** + * An inline Astro config that takes highest priority when merging with the user config, + * and includes inline-specific options to configure how Astro runs. + */ export interface AstroInlineConfig extends AstroUserConfig, AstroInlineOnlyConfig {} export interface AstroInlineOnlyConfig { + /** + * A custom path to the Astro config file. If relative, it'll resolve based on the current working directory. + * Set to false to disable loading any config files. + * + * If this value is undefined or unset, Astro will search for an `astro.config.(js,mjs,ts)` file relative to + * the `root` and load the config file if found. + * + * The inline config passed in this object will take highest priority when merging with the loaded user config. + */ configFile?: string | false; + /** + * The mode used when building your site to generate either "development" or "production" code. + */ mode?: RuntimeMode; + /** + * The logging level to filter messages logged by Astro. + * - "debug": Log everything, including noisy debugging diagnostics. + * - "info": Log informational messages, warnings, and errors. + * - "warn": Log warnings and errors. + * - "error": Log errors only. + * - "silent": No logging. + * + * @default "info" + */ logLevel?: LoggerLevel; /** - * @internal for testing only + * @internal for testing only, use `logLevel` instead. */ logging?: LogOptions; } diff --git a/packages/astro/src/cli/build/index.ts b/packages/astro/src/cli/build/index.ts index 9e26108a2d13..dd44823d1427 100644 --- a/packages/astro/src/cli/build/index.ts +++ b/packages/astro/src/cli/build/index.ts @@ -25,7 +25,5 @@ export async function build({ flags }: BuildOptions) { const inlineConfig = flagsToAstroInlineConfig(flags); - await _build(inlineConfig, { - teardownCompiler: true, - }); + await _build(inlineConfig); } diff --git a/packages/astro/src/cli/check/index.ts b/packages/astro/src/cli/check/index.ts index 428027154a89..6ae11d576cef 100644 --- a/packages/astro/src/cli/check/index.ts +++ b/packages/astro/src/cli/check/index.ts @@ -27,7 +27,7 @@ export async function check(flags: Arguments) { // Run sync before check to make sure types are generated. // NOTE: In the future, `@astrojs/check` can expose a `before lint` hook so that this works during `astro check --watch` too. // For now, we run this once as usually `astro check --watch` is ran alongside `astro dev` which also calls `astro sync`. - const { sync } = await import('../../core/sync/index.js'); + const { default: sync } = await import('../../core/sync/index.js'); const inlineConfig = flagsToAstroInlineConfig(flags); const exitCode = await sync(inlineConfig); if (exitCode !== 0) { diff --git a/packages/astro/src/cli/sync/index.ts b/packages/astro/src/cli/sync/index.ts index 66a277e4635d..8650bf904655 100644 --- a/packages/astro/src/cli/sync/index.ts +++ b/packages/astro/src/cli/sync/index.ts @@ -1,6 +1,6 @@ import type yargs from 'yargs-parser'; import { printHelp } from '../../core/messages.js'; -import { sync as _sync } from '../../core/sync/index.js'; +import _sync from '../../core/sync/index.js'; import { flagsToAstroInlineConfig } from '../flags.js'; interface SyncOptions { diff --git a/packages/astro/src/core/README.md b/packages/astro/src/core/README.md index 74f55a0bb040..ef4dd8750ba7 100644 --- a/packages/astro/src/core/README.md +++ b/packages/astro/src/core/README.md @@ -1,6 +1,8 @@ # `core/` -Code that executes within the top-level Node context. Contains the main Astro logic for the `build` and `dev` commands, and also manages the Vite server and SSR. +Code that executes within the top-level Node context. Contains the main Astro logic for the `build`, `dev`, `preview`, and `sync` commands, and also manages the Vite server and SSR. + +The `core/index.ts` file is the main entry point for the `astro` package. [See CONTRIBUTING.md](../../../../CONTRIBUTING.md) for a code overview. diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 07b9b2f7ca17..894a5e6e203c 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -37,14 +37,22 @@ export interface BuildOptions { /** * 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. + * + * @internal only used for testing + * @default true */ teardownCompiler?: boolean; } -/** `astro build` */ +/** + * Builds your site for deployment. By default, this will generate static files and place them in a dist/ directory. + * If SSR is enabled, this will generate the necessary server files to serve your site. + * + * @experimental The JavaScript API is experimental + */ export default async function build( inlineConfig: AstroInlineConfig, - options: BuildOptions + options?: BuildOptions ): Promise { applyPolyfill(); const logging = createNodeLogging(inlineConfig); @@ -82,7 +90,7 @@ class AstroBuilder { } this.settings = settings; this.logging = options.logging; - this.teardownCompiler = options.teardownCompiler ?? false; + this.teardownCompiler = options.teardownCompiler ?? true; this.routeCache = new RouteCache(this.logging); this.origin = settings.config.site ? new URL(settings.config.site).origin diff --git a/packages/astro/src/core/dev/dev.ts b/packages/astro/src/core/dev/dev.ts index b14656d26bc6..115cbe825827 100644 --- a/packages/astro/src/core/dev/dev.ts +++ b/packages/astro/src/core/dev/dev.ts @@ -18,7 +18,12 @@ export interface DevServer { stop(): Promise; } -/** `astro dev` */ +/** + * Runs Astro’s development server. This is a local HTTP server that doesn’t bundle assets. + * It uses Hot Module Replacement (HMR) to update your browser as you save changes in your editor. + * + * @experimental The JavaScript API is experimental + */ export default async function dev(inlineConfig: AstroInlineConfig): Promise { const devStart = performance.now(); await telemetry.record([]); diff --git a/packages/astro/src/core/index.ts b/packages/astro/src/core/index.ts new file mode 100644 index 000000000000..31d868311455 --- /dev/null +++ b/packages/astro/src/core/index.ts @@ -0,0 +1,26 @@ +// This is the main entrypoint when importing the `astro` package. + +import type { AstroInlineConfig } from '../@types/astro.js'; +import { default as _build } from './build/index.js'; +import { default as _sync } from './sync/index.js'; + +export { default as dev } from './dev/index.js'; +export { default as preview } from './preview/index.js'; + +/** + * Builds your site for deployment. By default, this will generate static files and place them in a dist/ directory. + * If SSR is enabled, this will generate the necessary server files to serve your site. + * + * @experimental The JavaScript API is experimental + */ +// Wrap `_build` to prevent exposing the second internal options parameter +export const build = (inlineConfig: AstroInlineConfig) => _build(inlineConfig); + +/** + * Generates TypeScript types for all Astro modules. This sets up a `src/env.d.ts` file for type inferencing, + * and defines the `astro:content` module for the Content Collections API. + * + * @experimental The JavaScript API is experimental + */ +// Wrap `_sync` to prevent exposing the second internal options parameter +export const sync = (inlineConfig: AstroInlineConfig) => _sync(inlineConfig); diff --git a/packages/astro/src/core/preview/index.ts b/packages/astro/src/core/preview/index.ts index fdd5d6fe7c7b..d5b2b2db76bf 100644 --- a/packages/astro/src/core/preview/index.ts +++ b/packages/astro/src/core/preview/index.ts @@ -10,10 +10,13 @@ import { createSettings } from '../config/settings.js'; import createStaticPreviewServer from './static-preview-server.js'; import { getResolvedHostForHttpServer } from './util.js'; -/** The primary dev action */ -export default async function preview( - inlineConfig: AstroInlineConfig -): Promise { +/** + * Starts a local server to serve your static dist/ directory. This command is useful for previewing + * your build locally, before deploying it. It is not designed to be run in production. + * + * @experimental The JavaScript API is experimental + */ +export default async function preview(inlineConfig: AstroInlineConfig): Promise { const logging = createNodeLogging(inlineConfig); const { userConfig, astroConfig } = await resolveConfig(inlineConfig ?? {}, 'preview'); telemetry.record(eventCliSession('preview', userConfig)); diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts index be51f0039b02..e1d432465dda 100644 --- a/packages/astro/src/core/sync/index.ts +++ b/packages/astro/src/core/sync/index.ts @@ -22,8 +22,7 @@ export type ProcessExit = 0 | 1; export type SyncOptions = { /** - * Only used for testing - * @internal + * @internal only used for testing */ fs?: typeof fsMod; }; @@ -32,7 +31,13 @@ export type SyncInternalOptions = SyncOptions & { logging: LogOptions; }; -export async function sync( +/** + * Generates TypeScript types for all Astro modules. This sets up a `src/env.d.ts` file for type inferencing, + * and defines the `astro:content` module for the Content Collections API. + * + * @experimental The JavaScript API is experimental + */ +export default async function sync( inlineConfig: AstroInlineConfig, options?: SyncOptions ): Promise { @@ -48,7 +53,7 @@ export async function sync( command: 'build', }); - return await syncInternal(settings, { logging, fs: options?.fs }); + return await syncInternal(settings, { ...options, logging }); } /** diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index 0b336698f3db..fafd3046cf23 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -6,15 +6,14 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import stripAnsi from 'strip-ansi'; import { check } from '../dist/cli/check/index.js'; +import { dev, preview } from '../dist/core/index.js'; import build from '../dist/core/build/index.js'; +import sync from '../dist/core/sync/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 { 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'; -import { sync } from '../dist/core/sync/index.js'; // Disable telemetry when running tests process.env.ASTRO_TELEMETRY_DISABLED = true; @@ -148,7 +147,7 @@ export async function loadFixture(inlineConfig) { return { build: async (extraInlineConfig = {}) => { process.env.NODE_ENV = 'production'; - return build(mergeConfig(inlineConfig, extraInlineConfig)); + return build(mergeConfig(inlineConfig, extraInlineConfig), { teardownCompiler: false }); }, sync: async (extraInlineConfig = {}, opts) => { return sync(mergeConfig(inlineConfig, extraInlineConfig), opts); diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js index 34461534699d..a8e282a88163 100644 --- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js +++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { fileURLToPath } from 'node:url'; -import { sync as _sync } from '../../../dist/core/sync/index.js'; +import _sync from '../../../dist/core/sync/index.js'; import { createFsWithFallback } from '../test-utils.js'; const root = new URL('../../fixtures/content-mixed-errors/', import.meta.url); diff --git a/packages/astro/tsconfig.json b/packages/astro/tsconfig.json index 63854a31decc..d8bd9b19752b 100644 --- a/packages/astro/tsconfig.json +++ b/packages/astro/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../tsconfig.base.json", - "include": ["src", "index.d.ts"], + "include": ["src"], "compilerOptions": { "allowJs": true, "declarationDir": "./dist",