diff --git a/.changeset/healthy-meals-wink.md b/.changeset/healthy-meals-wink.md
new file mode 100644
index 000000000..4cc5e10b1
--- /dev/null
+++ b/.changeset/healthy-meals-wink.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Expose `getViteConfig` from `astro/config` to unblock usage with Vitest
diff --git a/examples/with-vitest/.gitignore b/examples/with-vitest/.gitignore
new file mode 100644
index 000000000..02f6e50b4
--- /dev/null
+++ b/examples/with-vitest/.gitignore
@@ -0,0 +1,19 @@
+# build output
+dist/
+
+# dependencies
+node_modules/
+
+# logs
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+
+# environment variables
+.env
+.env.production
+
+# macOS-specific files
+.DS_Store
diff --git a/examples/with-vitest/.npmrc b/examples/with-vitest/.npmrc
new file mode 100644
index 000000000..ef83021af
--- /dev/null
+++ b/examples/with-vitest/.npmrc
@@ -0,0 +1,2 @@
+# Expose Astro dependencies for `pnpm` users
+shamefully-hoist=true
diff --git a/examples/with-vitest/.stackblitzrc b/examples/with-vitest/.stackblitzrc
new file mode 100644
index 000000000..43798ecff
--- /dev/null
+++ b/examples/with-vitest/.stackblitzrc
@@ -0,0 +1,6 @@
+{
+ "startCommand": "npm start",
+ "env": {
+ "ENABLE_CJS_IMPORTS": true
+ }
+}
\ No newline at end of file
diff --git a/examples/with-vitest/README.md b/examples/with-vitest/README.md
new file mode 100644
index 000000000..8f1f2e6a0
--- /dev/null
+++ b/examples/with-vitest/README.md
@@ -0,0 +1,9 @@
+# Astro + [Vitest](https://vitest.dev/) Example
+
+```
+npm init astro -- --template with-vitest
+```
+
+[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/with-vitest)
+
+This example showcases Astro working with [Vitest](https://vitest.dev/).
diff --git a/examples/with-vitest/astro.config.ts b/examples/with-vitest/astro.config.ts
new file mode 100644
index 000000000..ad7965b1a
--- /dev/null
+++ b/examples/with-vitest/astro.config.ts
@@ -0,0 +1,4 @@
+import { defineConfig } from 'astro/config';
+
+// https://astro.build/config
+export default defineConfig();
diff --git a/examples/with-vitest/package.json b/examples/with-vitest/package.json
new file mode 100644
index 000000000..c445064c9
--- /dev/null
+++ b/examples/with-vitest/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@example/with-vitest",
+ "version": "0.0.1",
+ "type": "module",
+ "private": true,
+ "scripts": {
+ "dev": "astro dev",
+ "start": "astro dev",
+ "build": "astro build",
+ "preview": "astro preview",
+ "test": "vitest"
+ },
+ "devDependencies": {
+ "astro": "^1.0.0-rc.4",
+ "vitest": "^0.20.3"
+ }
+}
diff --git a/examples/with-vitest/public/favicon.ico b/examples/with-vitest/public/favicon.ico
new file mode 100644
index 000000000..578ad458b
Binary files /dev/null and b/examples/with-vitest/public/favicon.ico differ
diff --git a/examples/with-vitest/sandbox.config.json b/examples/with-vitest/sandbox.config.json
new file mode 100644
index 000000000..9178af77d
--- /dev/null
+++ b/examples/with-vitest/sandbox.config.json
@@ -0,0 +1,11 @@
+{
+ "infiniteLoopProtection": true,
+ "hardReloadOnChange": false,
+ "view": "browser",
+ "template": "node",
+ "container": {
+ "port": 3000,
+ "startScript": "start",
+ "node": "14"
+ }
+}
diff --git a/examples/with-vitest/src/pages/index.astro b/examples/with-vitest/src/pages/index.astro
new file mode 100644
index 000000000..4389d5d25
--- /dev/null
+++ b/examples/with-vitest/src/pages/index.astro
@@ -0,0 +1,13 @@
+---
+---
+
+
+
+
+
+ Astro
+
+
+ Astro
+
+
diff --git a/examples/with-vitest/test/basic.test.ts b/examples/with-vitest/test/basic.test.ts
new file mode 100644
index 000000000..0f6d96168
--- /dev/null
+++ b/examples/with-vitest/test/basic.test.ts
@@ -0,0 +1,21 @@
+import { assert, expect, test } from 'vitest'
+
+// Edit an assertion and save to see HMR in action
+
+test('Math.sqrt()', () => {
+ expect(Math.sqrt(4)).toBe(2)
+ expect(Math.sqrt(144)).toBe(12)
+ expect(Math.sqrt(2)).toBe(Math.SQRT2)
+})
+
+test('JSON', () => {
+ const input = {
+ foo: 'hello',
+ bar: 'world',
+ }
+
+ const output = JSON.stringify(input)
+
+ expect(output).eq('{"foo":"hello","bar":"world"}')
+ assert.deepEqual(JSON.parse(output), input, 'matches original')
+})
diff --git a/examples/with-vitest/tsconfig.json b/examples/with-vitest/tsconfig.json
new file mode 100644
index 000000000..be8e3ea96
--- /dev/null
+++ b/examples/with-vitest/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ // Preact specific settings
+ "jsx": "react-jsx",
+ "jsxImportSource": "preact",
+ // Enable top-level await, and other modern ESM features.
+ "target": "ESNext",
+ "module": "ESNext",
+ // Enable node-style module resolution, for things like npm package imports.
+ "moduleResolution": "node",
+ // Enable JSON imports.
+ "resolveJsonModule": true,
+ // Enable stricter transpilation for better output.
+ "isolatedModules": true,
+ // Add type definitions for our Astro runtime.
+ "types": ["astro/client"]
+ }
+}
diff --git a/examples/with-vitest/vitest.config.ts b/examples/with-vitest/vitest.config.ts
new file mode 100644
index 000000000..a34f19bb1
--- /dev/null
+++ b/examples/with-vitest/vitest.config.ts
@@ -0,0 +1,9 @@
+///
+import { getViteConfig } from 'astro/config';
+
+export default getViteConfig({
+ test: {
+ /* for example, use global to avoid globals imports (describe, test, expect): */
+ // globals: true,
+ },
+});
diff --git a/packages/astro/config.d.ts b/packages/astro/config.d.ts
index b63f18336..b43ea268d 100644
--- a/packages/astro/config.d.ts
+++ b/packages/astro/config.d.ts
@@ -1,3 +1,4 @@
+type ViteUserConfig = import('vite').UserConfig;
type AstroUserConfig = import('./dist/types/@types/astro').AstroUserConfig;
/**
@@ -5,3 +6,8 @@ type AstroUserConfig = import('./dist/types/@types/astro').AstroUserConfig;
* https://astro.build/config
*/
export function defineConfig(config: AstroUserConfig): AstroUserConfig;
+
+/**
+ * Use Astro to generate a fully resolved Vite config
+ */
+export function getViteConfig(config: ViteUserConfig): ViteUserConfig;
diff --git a/packages/astro/config.mjs b/packages/astro/config.mjs
index cf19c5aa4..47193dbff 100644
--- a/packages/astro/config.mjs
+++ b/packages/astro/config.mjs
@@ -1,3 +1,43 @@
export function defineConfig(config) {
return config;
}
+
+export function getViteConfig(inlineConfig) {
+ // Return an async Vite config getter which exposes a resolved `mode` and `command`
+ return async ({ mode, command }) => {
+ // Vite `command` is `serve | build`, but Astro uses `dev | build`
+ const cmd = command === 'serve' ? 'dev' : command;
+
+ // Use dynamic import to avoid pulling in deps unless used
+ const [
+ { mergeConfig },
+ { nodeLogDestination },
+ { openConfig },
+ { createVite },
+ { runHookConfigSetup, runHookConfigDone },
+ ] = await Promise.all([
+ import('vite'),
+ import('./dist/core/logger/node.js'),
+ import('./dist/core/config.js'),
+ import('./dist/core/create-vite.js'),
+ import('./dist/integrations/index.js'),
+ ]);
+ const logging = {
+ dest: nodeLogDestination,
+ level: 'info',
+ };
+ const { astroConfig: config } = await openConfig({
+ cmd,
+ logging,
+ });
+ await runHookConfigSetup({ config, command: cmd });
+ const viteConfig = await createVite(
+ {
+ mode,
+ },
+ { astroConfig: config, logging: logging, mode }
+ );
+ await runHookConfigDone({ config });
+ return mergeConfig(viteConfig, inlineConfig);
+ }
+}
diff --git a/packages/astro/package.json b/packages/astro/package.json
index d5ee1cbcf..9a47eebee 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -32,7 +32,6 @@
"./jsx/*": "./dist/jsx/*",
"./jsx-runtime": "./dist/jsx-runtime/index.js",
"./config": "./config.mjs",
- "./internal": "./internal.js",
"./app": "./dist/core/app/index.js",
"./app/node": "./dist/core/app/node.js",
"./client/*": "./dist/runtime/client/*",
diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts
index 631c1dc2b..d197fb409 100644
--- a/packages/astro/src/core/create-vite.ts
+++ b/packages/astro/src/core/create-vite.ts
@@ -24,7 +24,7 @@ export type ViteConfigWithSSR = vite.InlineConfig & { ssr?: vite.SSROptions };
interface CreateViteOptions {
astroConfig: AstroConfig;
logging: LogOptions;
- mode: 'dev' | 'build';
+ mode: 'dev' | 'build' | string;
}
const ALWAYS_NOEXTERNAL = new Set([
@@ -74,7 +74,7 @@ export async function createVite(
astroScriptsPlugin({ config: astroConfig }),
// The server plugin is for dev only and having it run during the build causes
// the build to run very slow as the filewatcher is triggered often.
- mode === 'dev' && astroViteServerPlugin({ config: astroConfig, logging }),
+ mode !== 'build' && astroViteServerPlugin({ config: astroConfig, logging }),
envVitePlugin({ config: astroConfig }),
markdownVitePlugin({ config: astroConfig, logging }),
htmlVitePlugin(),
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 121e6c5eb..ff53ebbca 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -374,6 +374,14 @@ importers:
vite-plugin-pwa: 0.11.11
workbox-window: 6.5.4
+ examples/with-vitest:
+ specifiers:
+ astro: ^1.0.0-rc.4
+ vitest: ^0.20.3
+ devDependencies:
+ astro: link:../../packages/astro
+ vitest: 0.20.3
+
packages/astro:
specifiers:
'@astrojs/compiler': ^0.22.1
@@ -8364,6 +8372,12 @@ packages:
'@types/chai': 4.3.1
dev: true
+ /@types/chai-subset/1.3.3:
+ resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==}
+ dependencies:
+ '@types/chai': 4.3.1
+ dev: true
+
/@types/chai/4.3.1:
resolution: {integrity: sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ==}
dev: true
@@ -15820,6 +15834,16 @@ packages:
globalyzer: 0.1.0
globrex: 0.1.2
+ /tinypool/0.2.4:
+ resolution: {integrity: sha512-Vs3rhkUH6Qq1t5bqtb816oT+HeJTXfwt2cbPH17sWHIYKTotQIFPk3tf2fgqRrVyMDVOc1EnPgzIxfIulXVzwQ==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
+ /tinyspy/1.0.0:
+ resolution: {integrity: sha512-FI5B2QdODQYDRjfuLF+OrJ8bjWRMCXokQPcwKm0W3IzcbUmBNv536cQc7eXGoAuXphZwgx1DFbqImwzz08Fnhw==}
+ engines: {node: '>=14.0.0'}
+ dev: true
+
/tmp/0.0.33:
resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==}
engines: {node: '>=0.6.0'}
@@ -16491,7 +16515,6 @@ packages:
rollup: 2.77.2
optionalDependencies:
fsevents: 2.3.2
- dev: false
/vite/3.0.4_sass@1.54.3:
resolution: {integrity: sha512-NU304nqnBeOx2MkQnskBQxVsa0pRAH5FphokTGmyy8M3oxbvw7qAXts2GORxs+h/2vKsD+osMhZ7An6yK6F1dA==}
@@ -16521,6 +16544,48 @@ packages:
fsevents: 2.3.2
dev: false
+ /vitest/0.20.3:
+ resolution: {integrity: sha512-cXMjTbZxBBUUuIF3PUzEGPLJWtIMeURBDXVxckSHpk7xss4JxkiiWh5cnIlfGyfJne2Ii3QpbiRuFL5dMJtljw==}
+ engines: {node: '>=v14.16.0'}
+ hasBin: true
+ peerDependencies:
+ '@edge-runtime/vm': '*'
+ '@vitest/browser': '*'
+ '@vitest/ui': '*'
+ c8: '*'
+ happy-dom: '*'
+ jsdom: '*'
+ peerDependenciesMeta:
+ '@edge-runtime/vm':
+ optional: true
+ '@vitest/browser':
+ optional: true
+ '@vitest/ui':
+ optional: true
+ c8:
+ optional: true
+ happy-dom:
+ optional: true
+ jsdom:
+ optional: true
+ dependencies:
+ '@types/chai': 4.3.1
+ '@types/chai-subset': 1.3.3
+ '@types/node': 18.6.4
+ chai: 4.3.6
+ debug: 4.3.4
+ local-pkg: 0.4.2
+ tinypool: 0.2.4
+ tinyspy: 1.0.0
+ vite: 3.0.4
+ transitivePeerDependencies:
+ - less
+ - sass
+ - stylus
+ - supports-color
+ - terser
+ dev: true
+
/vm2/3.9.10:
resolution: {integrity: sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ==}
engines: {node: '>=6.0'}