summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Matthew Phillips <matthew@skypack.dev> 2022-11-15 10:02:23 -0500
committerGravatar GitHub <noreply@github.com> 2022-11-15 10:02:23 -0500
commit40226dd14d9f2d00d943f508dcfc76654c352938 (patch)
tree70630f9683f4721341a2230ff098cf39fd8ef77c
parent095de72a17ca0e7c17dd072ec2691139bfb42511 (diff)
downloadastro-40226dd14d9f2d00d943f508dcfc76654c352938.tar.gz
astro-40226dd14d9f2d00d943f508dcfc76654c352938.tar.zst
astro-40226dd14d9f2d00d943f508dcfc76654c352938.zip
Use Vite to load non JS astro configs (#5377)
* Use Vite to load non JS astro configs * Adding a changeset * Allow config to not exist * Use a file url * Use proload as a fallback * add missing peerdep * fix lint mistakes * Refactor the vite-load * First check if the file exists * Pass through fs * Update packages/astro/src/core/config/vite-load.ts Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com> * Also load astro.config.cjs * Do search before trying to load Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
-rw-r--r--.changeset/swift-bees-hope.md5
-rw-r--r--packages/astro/package.json4
-rw-r--r--packages/astro/src/cli/index.ts5
-rw-r--r--packages/astro/src/core/add/index.ts4
-rw-r--r--packages/astro/src/core/config/config.ts76
-rw-r--r--packages/astro/src/core/config/vite-load.ts138
-rw-r--r--packages/astro/test/fixtures/tailwindcss-ts/astro.config.ts7
-rw-r--r--packages/astro/test/fixtures/tailwindcss-ts/package.json11
-rw-r--r--packages/astro/test/fixtures/tailwindcss-ts/tailwind.config.cjs4
-rw-r--r--packages/astro/test/units/dev/restart.test.js38
-rw-r--r--packages/integrations/image/src/index.ts4
-rw-r--r--pnpm-lock.yaml12
12 files changed, 242 insertions, 66 deletions
diff --git a/.changeset/swift-bees-hope.md b/.changeset/swift-bees-hope.md
new file mode 100644
index 000000000..0bf096e3a
--- /dev/null
+++ b/.changeset/swift-bees-hope.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Uses vite to load astro.config.ts files
diff --git a/packages/astro/package.json b/packages/astro/package.json
index 279472c6e..13a82395d 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -111,11 +111,11 @@
"@babel/plugin-transform-react-jsx": "^7.17.12",
"@babel/traverse": "^7.18.2",
"@babel/types": "^7.18.4",
- "@proload/core": "^0.3.3",
- "@proload/plugin-tsm": "^0.2.1",
"@types/babel__core": "^7.1.19",
"@types/html-escaper": "^3.0.0",
"@types/yargs-parser": "^21.0.0",
+ "@proload/core": "^0.3.3",
+ "@proload/plugin-tsm": "^0.2.1",
"boxen": "^6.2.1",
"ci-info": "^3.3.1",
"common-ancestor-path": "^1.0.1",
diff --git a/packages/astro/src/cli/index.ts b/packages/astro/src/cli/index.ts
index ddd4acc83..33457a6a1 100644
--- a/packages/astro/src/cli/index.ts
+++ b/packages/astro/src/cli/index.ts
@@ -3,6 +3,7 @@ import * as colors from 'kleur/colors';
import type { Arguments as Flags } from 'yargs-parser';
import yargs from 'yargs-parser';
import { z } from 'zod';
+import fs from 'fs';
import {
createSettings,
openConfig,
@@ -88,7 +89,7 @@ async function handleConfigError(
e: any,
{ cwd, flags, logging }: { cwd?: string; flags?: Flags; logging: LogOptions }
) {
- const path = await resolveConfigPath({ cwd, flags });
+ const path = await resolveConfigPath({ cwd, flags, fs });
if (e instanceof Error) {
if (path) {
error(logging, 'astro', `Unable to load ${colors.bold(path)}\n`);
@@ -173,7 +174,7 @@ async function runCommand(cmd: string, flags: yargs.Arguments) {
const { default: devServer } = await import('../core/dev/index.js');
const configFlag = resolveFlags(flags).config;
- const configFlagPath = configFlag ? await resolveConfigPath({ cwd: root, flags }) : undefined;
+ const configFlagPath = configFlag ? await resolveConfigPath({ cwd: root, flags, fs }) : undefined;
await devServer(settings, {
configFlag,
diff --git a/packages/astro/src/core/add/index.ts b/packages/astro/src/core/add/index.ts
index 3c2d20a87..536cbe2f9 100644
--- a/packages/astro/src/core/add/index.ts
+++ b/packages/astro/src/core/add/index.ts
@@ -2,7 +2,7 @@ import type { AstroTelemetry } from '@astrojs/telemetry';
import boxen from 'boxen';
import { diffWords } from 'diff';
import { execa } from 'execa';
-import { existsSync, promises as fs } from 'fs';
+import fsMod, { existsSync, promises as fs } from 'fs';
import { bold, cyan, dim, green, magenta, red, yellow } from 'kleur/colors';
import ora from 'ora';
import path from 'path';
@@ -164,7 +164,7 @@ export default async function add(names: string[], { cwd, flags, logging, teleme
}
}
- const rawConfigPath = await resolveConfigPath({ cwd, flags });
+ const rawConfigPath = await resolveConfigPath({ cwd, flags, fs: fsMod });
let configURL = rawConfigPath ? pathToFileURL(rawConfigPath) : undefined;
if (configURL) {
diff --git a/packages/astro/src/core/config/config.ts b/packages/astro/src/core/config/config.ts
index 13c87fa9a..c17ec6a39 100644
--- a/packages/astro/src/core/config/config.ts
+++ b/packages/astro/src/core/config/config.ts
@@ -1,20 +1,16 @@
import type { Arguments as Flags } from 'yargs-parser';
import type { AstroConfig, AstroUserConfig, CLIFlags } from '../../@types/astro';
-import load, { ProloadError, resolve } from '@proload/core';
-import loadTypeScript from '@proload/plugin-tsm';
import fs from 'fs';
import * as colors from 'kleur/colors';
import path from 'path';
import { fileURLToPath, pathToFileURL } from 'url';
-import * as vite from 'vite';
import { mergeConfig as mergeViteConfig } from 'vite';
import { AstroError, AstroErrorData } from '../errors/index.js';
import { LogOptions } from '../logger/core.js';
import { arraify, isObject, isURL } from '../util.js';
import { createRelativeSchema } from './schema.js';
-
-load.use([loadTypeScript]);
+import { loadConfigWithVite } from './vite-load.js';
export const LEGACY_ASTRO_CONFIG_KEYS = new Set([
'projectRoot',
@@ -151,7 +147,7 @@ interface LoadConfigOptions {
* instead of the resolved config
*/
export async function resolveConfigPath(
- configOptions: Pick<LoadConfigOptions, 'cwd' | 'flags'>
+ configOptions: Pick<LoadConfigOptions, 'cwd' | 'flags'> & { fs: typeof fs }
): Promise<string | undefined> {
const root = resolveRoot(configOptions.cwd);
const flags = resolveFlags(configOptions.flags || {});
@@ -165,14 +161,14 @@ export async function resolveConfigPath(
// Resolve config file path using Proload
// If `userConfigPath` is `undefined`, Proload will search for `astro.config.[cm]?[jt]s`
try {
- const configPath = await resolve('astro', {
- mustExist: !!userConfigPath,
- cwd: root,
- filePath: userConfigPath,
+ const config = await loadConfigWithVite({
+ configPath: userConfigPath,
+ root,
+ fs: configOptions.fs
});
- return configPath;
+ return config.filePath;
} catch (e) {
- if (e instanceof ProloadError && flags.config) {
+ if (flags.config) {
throw new AstroError({
...AstroErrorData.ConfigNotFound,
message: AstroErrorData.ConfigNotFound.message(flags.config),
@@ -195,7 +191,7 @@ export async function openConfig(configOptions: LoadConfigOptions): Promise<Open
const flags = resolveFlags(configOptions.flags || {});
let userConfig: AstroUserConfig = {};
- const config = await tryLoadConfig(configOptions, flags, root);
+ const config = await tryLoadConfig(configOptions, root);
if (config) {
userConfig = config.value;
}
@@ -216,7 +212,6 @@ interface TryLoadConfigResult {
async function tryLoadConfig(
configOptions: LoadConfigOptions,
- flags: CLIFlags,
root: string
): Promise<TryLoadConfigResult | undefined> {
const fsMod = configOptions.fsMod ?? fs;
@@ -225,6 +220,7 @@ async function tryLoadConfig(
let configPath = await resolveConfigPath({
cwd: configOptions.cwd,
flags: configOptions.flags,
+ fs: fsMod
});
if (!configPath) return undefined;
if (configOptions.isRestart) {
@@ -246,52 +242,14 @@ async function tryLoadConfig(
};
configPath = tempConfigPath;
}
-
- const config = await load('astro', {
- mustExist: !!configPath,
- cwd: root,
- filePath: configPath,
+
+ // Create a vite server to load the config
+ const config = await loadConfigWithVite({
+ configPath,
+ fs: fsMod,
+ root
});
-
return config as TryLoadConfigResult;
- } catch (e) {
- if (e instanceof ProloadError && flags.config) {
- throw new AstroError({
- ...AstroErrorData.ConfigNotFound,
- message: AstroErrorData.ConfigNotFound.message(flags.config),
- });
- }
-
- const configPath = await resolveConfigPath(configOptions);
- if (!configPath) {
- throw e;
- }
-
- // Fallback to use Vite DevServer
- const viteServer = await vite.createServer({
- server: { middlewareMode: true, hmr: false },
- optimizeDeps: { entries: [] },
- clearScreen: false,
- appType: 'custom',
- // NOTE: Vite doesn't externalize linked packages by default. During testing locally,
- // these dependencies trip up Vite's dev SSR transform. In the future, we should
- // avoid `vite.createServer` and use `loadConfigFromFile` instead.
- ssr: {
- external: ['@astrojs/mdx', '@astrojs/react'],
- },
- });
- try {
- const mod = await viteServer.ssrLoadModule(configPath);
-
- if (mod?.default) {
- return {
- value: mod.default,
- filePath: configPath,
- };
- }
- } finally {
- await viteServer.close();
- }
} finally {
await finallyCleanup();
}
@@ -306,7 +264,7 @@ export async function loadConfig(configOptions: LoadConfigOptions): Promise<Astr
const flags = resolveFlags(configOptions.flags || {});
let userConfig: AstroUserConfig = {};
- const config = await tryLoadConfig(configOptions, flags, root);
+ const config = await tryLoadConfig(configOptions, root);
if (config) {
userConfig = config.value;
}
diff --git a/packages/astro/src/core/config/vite-load.ts b/packages/astro/src/core/config/vite-load.ts
new file mode 100644
index 000000000..a701ce7c7
--- /dev/null
+++ b/packages/astro/src/core/config/vite-load.ts
@@ -0,0 +1,138 @@
+import * as vite from 'vite';
+import npath from 'path';
+import { pathToFileURL } from 'url';
+import type fsType from 'fs';
+import { AstroError, AstroErrorData } from '../errors/index.js';
+
+// Fallback for legacy
+import load from '@proload/core';
+import loadTypeScript from '@proload/plugin-tsm';
+
+load.use([loadTypeScript]);
+
+export interface ViteLoader {
+ root: string;
+ viteServer: vite.ViteDevServer;
+}
+
+async function createViteLoader(root: string): Promise<ViteLoader> {
+ const viteServer = await vite.createServer({
+ server: { middlewareMode: true, hmr: false },
+ optimizeDeps: { entries: [] },
+ clearScreen: false,
+ appType: 'custom',
+ ssr: {
+ // NOTE: Vite doesn't externalize linked packages by default. During testing locally,
+ // these dependencies trip up Vite's dev SSR transform. In the future, we should
+ // avoid `vite.createServer` and use `loadConfigFromFile` instead.
+ external: ['@astrojs/tailwind', '@astrojs/mdx', '@astrojs/react']
+ }
+ });
+
+ return {
+ root,
+ viteServer,
+ };
+}
+
+async function stat(fs: typeof fsType, configPath: string, mustExist: boolean): Promise<boolean> {
+ try {
+ await fs.promises.stat(configPath);
+ return true;
+ } catch {
+ if(mustExist) {
+ throw new AstroError({
+ ...AstroErrorData.ConfigNotFound,
+ message: AstroErrorData.ConfigNotFound.message(configPath),
+ });
+ }
+ return false;
+ }
+}
+
+async function search(fs: typeof fsType, root: string) {
+ const paths = [
+ 'astro.config.mjs',
+ 'astro.config.js',
+ 'astro.config.ts',
+ 'astro.config.mts',
+ 'astro.config.cjs',
+ 'astro.config.cjs'
+ ].map(path => npath.join(root, path));
+
+ for(const file of paths) {
+ // First verify the file event exists
+ const exists = await stat(fs, file, false);
+ if(exists) {
+ return file;
+ }
+ }
+}
+
+interface LoadConfigWithViteOptions {
+ root: string;
+ configPath: string | undefined;
+ fs: typeof fsType;
+}
+
+export async function loadConfigWithVite({ configPath, fs, root }: LoadConfigWithViteOptions): Promise<{
+ value: Record<string, any>;
+ filePath?: string;
+}> {
+ let file: string;
+ if(configPath) {
+ // Go ahead and check if the file exists and throw if not.
+ await stat(fs, configPath, true);
+ file = configPath;
+ } else {
+ const found = await search(fs, root);
+ if(!found) {
+ // No config file found, return an empty config that will be populated with defaults
+ return {
+ value: {},
+ filePath: undefined
+ };
+ } else {
+ file = found;
+ }
+ }
+
+
+ // Try loading with Node import()
+ if(/\.[cm]?js$/.test(file)) {
+ const config = await import(pathToFileURL(file).toString());
+ return {
+ value: config.default ?? {},
+ filePath: file
+ };
+ }
+
+ // Try Loading with Vite
+ let loader: ViteLoader | undefined;
+ try {
+ loader = await createViteLoader(root);
+ const mod = await loader.viteServer.ssrLoadModule(file);
+ return {
+ value: mod.default ?? {},
+ filePath: file
+ }
+ } catch {
+
+ // Try loading with Proload
+ // TODO deprecate - this is only for legacy compatibility
+ const res = await load('astro', {
+ mustExist: true,
+ cwd: root,
+ filePath: file,
+ });
+ return {
+ value: res?.value ?? {},
+ filePath: file
+ };
+
+ } finally {
+ if(loader) {
+ await loader.viteServer.close();
+ }
+ }
+}
diff --git a/packages/astro/test/fixtures/tailwindcss-ts/astro.config.ts b/packages/astro/test/fixtures/tailwindcss-ts/astro.config.ts
new file mode 100644
index 000000000..0a5f36a87
--- /dev/null
+++ b/packages/astro/test/fixtures/tailwindcss-ts/astro.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from 'astro/config';
+import tailwind from "@astrojs/tailwind";
+
+// https://astro.build/config
+export default defineConfig({
+ integrations: [tailwind()]
+}); \ No newline at end of file
diff --git a/packages/astro/test/fixtures/tailwindcss-ts/package.json b/packages/astro/test/fixtures/tailwindcss-ts/package.json
new file mode 100644
index 000000000..78ead447f
--- /dev/null
+++ b/packages/astro/test/fixtures/tailwindcss-ts/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "@test/tailwindcss-ts",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/tailwind": "workspace:*",
+ "astro": "workspace:*",
+ "tailwindcss": "^3.2.4",
+ "postcss": ">=8.3.3 <9.0.0"
+ }
+}
diff --git a/packages/astro/test/fixtures/tailwindcss-ts/tailwind.config.cjs b/packages/astro/test/fixtures/tailwindcss-ts/tailwind.config.cjs
new file mode 100644
index 000000000..f4033a222
--- /dev/null
+++ b/packages/astro/test/fixtures/tailwindcss-ts/tailwind.config.cjs
@@ -0,0 +1,4 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+ content: ['src/**/*.{astro,tsx}']
+}; \ No newline at end of file
diff --git a/packages/astro/test/units/dev/restart.test.js b/packages/astro/test/units/dev/restart.test.js
index 2e78f789d..96c60c608 100644
--- a/packages/astro/test/units/dev/restart.test.js
+++ b/packages/astro/test/units/dev/restart.test.js
@@ -7,6 +7,8 @@ import {
startContainer,
} from '../../../dist/core/dev/index.js';
import { createFs, createRequestAndResponse, triggerFSEvent } from '../test-utils.js';
+import { createSettings, openConfig } from '../../../dist/core/config/index.js';
+import { defaultLogging } from '../../test-utils.js';
const root = new URL('../../fixtures/alias/', import.meta.url);
@@ -109,4 +111,40 @@ describe('dev container restarts', () => {
await restart.container.close();
}
});
+
+ it('Is able to restart project using Tailwind + astro.config.ts', async () => {
+ const troot = new URL('../../fixtures/tailwindcss-ts/', import.meta.url);
+ const fs = createFs(
+ {
+ '/src/pages/index.astro': ``,
+ '/astro.config.ts': ``,
+ },
+ troot
+ );
+
+ const { astroConfig } = await openConfig({
+ cwd: troot,
+ flags: {},
+ cmd: 'dev',
+ logging: defaultLogging,
+ });
+ const settings = createSettings(astroConfig);
+
+ let restart = await createContainerWithAutomaticRestart({
+ params: { fs, root, settings },
+ });
+ await startContainer(restart.container);
+ expect(isStarted(restart.container)).to.equal(true);
+
+ try {
+ // Trigger a change
+ let restartComplete = restart.restarted();
+ triggerFSEvent(restart.container, fs, '/astro.config.ts', 'change');
+ await restartComplete;
+
+ expect(isStarted(restart.container)).to.equal(true);
+ } finally {
+ await restart.container.close();
+ }
+ });
});
diff --git a/packages/integrations/image/src/index.ts b/packages/integrations/image/src/index.ts
index ebb91e17b..43f64935b 100644
--- a/packages/integrations/image/src/index.ts
+++ b/packages/integrations/image/src/index.ts
@@ -82,7 +82,9 @@ export default function integration(options: IntegrationOptions = {}): AstroInte
'astro:config:setup': async ({ command, config, updateConfig, injectRoute }) => {
needsBuildConfig = !config.build?.server;
_config = config;
- updateConfig({ vite: getViteConfiguration(command === 'dev') });
+ updateConfig({
+ vite: getViteConfiguration(command === 'dev'),
+ });
if (command === 'dev' || config.output === 'server') {
injectRoute({
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b4f3a0ac2..c6797e1e8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -2390,6 +2390,18 @@ importers:
postcss: 8.4.19
tailwindcss: 3.2.4_postcss@8.4.19
+ packages/astro/test/fixtures/tailwindcss-ts:
+ specifiers:
+ '@astrojs/tailwind': workspace:*
+ astro: workspace:*
+ postcss: '>=8.3.3 <9.0.0'
+ tailwindcss: ^3.2.4
+ dependencies:
+ '@astrojs/tailwind': link:../../../../integrations/tailwind
+ astro: link:../../..
+ postcss: 8.4.19
+ tailwindcss: 3.2.4_postcss@8.4.19
+
packages/astro/test/fixtures/third-party-astro:
specifiers:
astro: workspace:*