summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/witty-melons-heal.md5
-rw-r--r--packages/astro/package.json1
-rw-r--r--packages/astro/src/core/create-vite.ts151
-rw-r--r--pnpm-lock.yaml14
4 files changed, 51 insertions, 120 deletions
diff --git a/.changeset/witty-melons-heal.md b/.changeset/witty-melons-heal.md
new file mode 100644
index 000000000..27202d2c5
--- /dev/null
+++ b/.changeset/witty-melons-heal.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Improve Astro libraries config handling
diff --git a/packages/astro/package.json b/packages/astro/package.json
index 331ef6b27..6ebfb9bab 100644
--- a/packages/astro/package.json
+++ b/packages/astro/package.json
@@ -158,6 +158,7 @@
"unist-util-visit": "^4.1.0",
"vfile": "^5.3.2",
"vite": "~3.1.3",
+ "vitefu": "^0.1.0",
"yargs-parser": "^21.0.1",
"zod": "^3.17.3"
},
diff --git a/packages/astro/src/core/create-vite.ts b/packages/astro/src/core/create-vite.ts
index fc39e3bdb..63e780961 100644
--- a/packages/astro/src/core/create-vite.ts
+++ b/packages/astro/src/core/create-vite.ts
@@ -1,11 +1,9 @@
import type { AstroSettings } from '../@types/astro';
import type { LogOptions } from './logger/core';
-import fs from 'fs';
-import { createRequire } from 'module';
-import path from 'path';
import { fileURLToPath } from 'url';
import * as vite from 'vite';
+import { crawlFrameworkPkgs } from 'vitefu';
import astroPostprocessVitePlugin from '../vite-plugin-astro-postprocess/index.js';
import astroViteServerPlugin from '../vite-plugin-astro-server/index.js';
import astroVitePlugin from '../vite-plugin-astro/index.js';
@@ -58,7 +56,31 @@ export async function createVite(
commandConfig: vite.InlineConfig,
{ settings, logging, mode }: CreateViteOptions
): Promise<vite.InlineConfig> {
- const thirdPartyAstroPackages = await getAstroPackages(settings);
+ const astroPkgsConfig = await crawlFrameworkPkgs({
+ root: fileURLToPath(settings.config.root),
+ isBuild: mode === 'build',
+ isFrameworkPkgByJson(pkgJson) {
+ return (
+ // Attempt: package relies on `astro`. ✅ Definitely an Astro package
+ pkgJson.peerDependencies?.astro ||
+ pkgJson.dependencies?.astro ||
+ // Attempt: package is tagged with `astro` or `astro-component`. ✅ Likely a community package
+ pkgJson.keywords?.includes('astro') ||
+ pkgJson.keywords?.includes('astro-component') ||
+ // Attempt: package is named `astro-something` or `@scope/astro-something`. ✅ Likely a community package
+ /^(@[^\/]+\/)?astro\-/.test(pkgJson.name)
+ );
+ },
+ isFrameworkPkgByName(pkgName) {
+ const isNotAstroPkg = isCommonNotAstro(pkgName);
+ if (isNotAstroPkg) {
+ return false;
+ } else {
+ return undefined;
+ }
+ },
+ });
+
// Start with the Vite configuration that Astro core needs
const commonConfig: vite.InlineConfig = {
cacheDir: fileURLToPath(new URL('./node_modules/.vite/', settings.config.root)), // using local caches allows Astro to be used in monorepos, etc.
@@ -126,11 +148,14 @@ export async function createVite(
conditions: ['astro'],
},
ssr: {
- noExternal: [...getSsrNoExternalDeps(settings.config.root), ...thirdPartyAstroPackages],
+ noExternal: [
+ ...getSsrNoExternalDeps(settings.config.root),
+ ...astroPkgsConfig.ssr.noExternal,
+ ],
// shiki is imported by Code.astro, which is no-externalized (processed by Vite).
// However, shiki's deps are in CJS and trips up Vite's dev SSR transform, externalize
// shiki to load it with node instead.
- external: mode === 'dev' ? ['shiki'] : [],
+ external: [...(mode === 'dev' ? ['shiki'] : []), ...astroPkgsConfig.ssr.external],
},
};
@@ -174,120 +199,6 @@ function sortPlugins(pluginOptions: vite.PluginOption[]) {
pluginOptions.splice(jsxPluginIndex, 0, mdxPlugin);
}
-// Scans `projectRoot` for third-party Astro packages that could export an `.astro` file
-// `.astro` files need to be built by Vite, so these should use `noExternal`
-async function getAstroPackages(settings: AstroSettings): Promise<string[]> {
- const { astroPackages } = new DependencyWalker(settings.config.root);
- return astroPackages;
-}
-
-/**
- * Recursively walk a project’s dependency tree trying to find Astro packages.
- * - If the current node is an Astro package, we continue walking its child dependencies.
- * - If the current node is not an Astro package, we bail out of walking that branch.
- * This assumes it is unlikely for Astro packages to be dependencies of packages that aren’t
- * themselves also Astro packages.
- */
-class DependencyWalker {
- private readonly require: NodeRequire;
- private readonly astroDeps = new Set<string>();
- private readonly nonAstroDeps = new Set<string>();
-
- constructor(root: URL) {
- const pkgUrl = new URL('./package.json', root);
- this.require = createRequire(pkgUrl);
- const pkgPath = fileURLToPath(pkgUrl);
- if (!fs.existsSync(pkgPath)) return;
-
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
- const deps = [
- ...Object.keys(pkg.dependencies || {}),
- ...Object.keys(pkg.devDependencies || {}),
- ];
-
- this.scanDependencies(deps);
- }
-
- /** The dependencies we determined were likely to include `.astro` files. */
- public get astroPackages(): string[] {
- return Array.from(this.astroDeps);
- }
-
- private seen(dep: string): boolean {
- return this.astroDeps.has(dep) || this.nonAstroDeps.has(dep);
- }
-
- /** Try to load a directory’s `package.json` file from the filesystem. */
- private readPkgJSON(dir: string): PkgJSON | void {
- try {
- const filePath = path.join(dir, 'package.json');
- return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
- } catch (e) {}
- }
-
- /** Try to resolve a dependency’s `package.json` even if not a package export. */
- private resolvePkgJSON(dep: string): PkgJSON | void {
- try {
- const pkgJson: PkgJSON = this.require(dep + '/package.json');
- return pkgJson;
- } catch (e) {
- // Most likely error is that the dependency doesn’t include `package.json` in its package `exports`.
- try {
- // Walk up from default export until we find `package.json` with name === dep.
- let dir = path.dirname(this.require.resolve(dep));
- while (dir) {
- const pkgJSON = this.readPkgJSON(dir);
- if (pkgJSON && pkgJSON.name === dep) return pkgJSON;
-
- const parentDir = path.dirname(dir);
- if (parentDir === dir) break;
-
- dir = parentDir;
- }
- } catch {
- // Give up! Who knows where the `package.json` is…
- }
- }
- }
-
- private scanDependencies(deps: string[]): void {
- const newDeps: string[] = [];
- for (const dep of deps) {
- // Attempt: package is common and not Astro. ❌ Skip these for perf
- if (isCommonNotAstro(dep)) {
- this.nonAstroDeps.add(dep);
- continue;
- }
-
- const pkgJson = this.resolvePkgJSON(dep);
- if (!pkgJson) {
- this.nonAstroDeps.add(dep);
- continue;
- }
- const { dependencies = {}, peerDependencies = {}, keywords = [] } = pkgJson;
-
- if (
- // Attempt: package relies on `astro`. ✅ Definitely an Astro package
- peerDependencies.astro ||
- dependencies.astro ||
- // Attempt: package is tagged with `astro` or `astro-component`. ✅ Likely a community package
- keywords.includes('astro') ||
- keywords.includes('astro-component') ||
- // Attempt: package is named `astro-something` or `@scope/astro-something`. ✅ Likely a community package
- /^(@[^\/]+\/)?astro\-/.test(dep)
- ) {
- this.astroDeps.add(dep);
- // Collect any dependencies of this Astro package we haven’t seen yet.
- const unknownDependencies = Object.keys(dependencies).filter((d) => !this.seen(d));
- newDeps.push(...unknownDependencies);
- } else {
- this.nonAstroDeps.add(dep);
- }
- }
- if (newDeps.length) this.scanDependencies(newDeps);
- }
-}
-
const COMMON_DEPENDENCIES_NOT_ASTRO = [
'autoprefixer',
'react',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8a5924de7..4ef405d67 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -455,6 +455,7 @@ importers:
unist-util-visit: ^4.1.0
vfile: ^5.3.2
vite: ~3.1.3
+ vitefu: ^0.1.0
yargs-parser: ^21.0.1
zod: ^3.17.3
dependencies:
@@ -517,6 +518,7 @@ importers:
unist-util-visit: 4.1.1
vfile: 5.3.5
vite: 3.1.8_sass@1.55.0
+ vitefu: 0.1.0_vite@3.1.8
yargs-parser: 21.1.1
zod: 3.19.1
devDependencies:
@@ -18014,6 +18016,18 @@ packages:
fsevents: 2.3.2
dev: false
+ /vitefu/0.1.0_vite@3.1.8:
+ resolution: {integrity: sha512-5MQSHP9yr0HIve8q4XNb7QXfO1P4tzZDZP99qH0FM5ClcwYddeGXRDQ4TQYRUeXLjZ+vLecirHtGNpwFFUF7sw==}
+ peerDependencies:
+ vite: ^3.0.0
+ peerDependenciesMeta:
+ vite:
+ optional: true
+ dependencies:
+ import-meta-resolve: 2.1.0
+ vite: 3.1.8_sass@1.55.0
+ dev: false
+
/vitest/0.20.3:
resolution: {integrity: sha512-cXMjTbZxBBUUuIF3PUzEGPLJWtIMeURBDXVxckSHpk7xss4JxkiiWh5cnIlfGyfJne2Ii3QpbiRuFL5dMJtljw==}
engines: {node: '>=v14.16.0'}