Skip to content

Commit 98144fb

Browse files
ematipicomatthewp
andauthored
refactor: optimise dependencies (#14951)
Co-authored-by: matthewp <361671+matthewp@users.noreply.github.com>
1 parent 71d2382 commit 98144fb

16 files changed

Lines changed: 455 additions & 139 deletions

File tree

packages/astro/src/core/constants.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,14 @@ export const SUPPORTED_MARKDOWN_FILE_EXTENSIONS = [
104104

105105
// The folder name where to find the middleware
106106
export const MIDDLEWARE_PATH_SEGMENT_NAME = 'middleware';
107+
108+
// The environments used inside Astro
109+
export const ASTRO_VITE_ENVIRONMENT_NAMES = {
110+
server: 'ssr',
111+
client: 'client',
112+
astro: 'astro',
113+
prerender: 'prerender',
114+
} as const;
115+
116+
export type AstroEnvironmentNames =
117+
(typeof ASTRO_VITE_ENVIRONMENT_NAMES)[keyof typeof ASTRO_VITE_ENVIRONMENT_NAMES];

packages/astro/src/core/create-vite.ts

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import nodeFs from 'node:fs';
22
import { fileURLToPath } from 'node:url';
3-
import { convertPathToPattern } from 'tinyglobby';
43
import * as vite from 'vite';
54
import { crawlFrameworkPkgs } from 'vitefu';
65
import { vitePluginActions } from '../actions/vite-plugin-actions.js';
@@ -50,6 +49,7 @@ import { joinPaths } from './path.js';
5049
import { vitePluginServerIslands } from './server-islands/vite-plugin-server-islands.js';
5150
import { vitePluginSessionDriver } from './session/vite-plugin.js';
5251
import { isObject } from './util.js';
52+
import { vitePluginEnvironment } from '../vite-plugin-environment/index.js';
5353

5454
type CreateViteOptions = {
5555
settings: AstroSettings;
@@ -67,30 +67,6 @@ type CreateViteOptions = {
6767
}
6868
);
6969

70-
const ALWAYS_NOEXTERNAL = [
71-
// This is only because Vite's native ESM doesn't resolve "exports" correctly.
72-
'astro',
73-
// Vite fails on nested `.astro` imports without bundling
74-
'astro/components',
75-
// Handle recommended nanostores. Only @nanostores/preact is required from our testing!
76-
// Full explanation and related bug report: https://github.com/withastro/astro/pull/3667
77-
'@nanostores/preact',
78-
// fontsource packages are CSS that need to be processed
79-
'@fontsource/*',
80-
];
81-
82-
// These specifiers are usually dependencies written in CJS, but loaded through Vite's transform
83-
// pipeline, which Vite doesn't support in development time. This hardcoded list temporarily
84-
// fixes things until Vite can properly handle them, or when they support ESM.
85-
const ONLY_DEV_EXTERNAL = [
86-
// Imported by `@astrojs/prism` which exposes `<Prism/>` that is processed by Vite
87-
'prismjs/components/index.js',
88-
// Imported by `astro/assets` -> `packages/astro/src/core/logger/core.ts`
89-
'string-width',
90-
// Imported by `astro:transitions` -> packages/astro/src/runtime/server/transition.ts
91-
'cssesc',
92-
];
93-
9470
/** Return a base vite config as a common starting point for all Vite commands. */
9571
export async function createVite(
9672
commandConfig: vite.InlineConfig,
@@ -128,7 +104,6 @@ export async function createVite(
128104
},
129105
});
130106

131-
const srcDirPattern = convertPathToPattern(fileURLToPath(settings.config.srcDir));
132107
const envLoader = createEnvLoader({
133108
mode,
134109
config: settings.config,
@@ -143,16 +118,12 @@ export async function createVite(
143118
clearScreen: false, // we want to control the output, not Vite
144119
customLogger: createViteLogger(logger, settings.config.vite.logLevel),
145120
appType: 'custom',
146-
optimizeDeps: {
147-
// Scan for component code within `srcDir`
148-
entries: [`${srcDirPattern}**/*.{jsx,tsx,vue,svelte,html,astro}`],
149-
exclude: ['astro', 'node-fetch'],
150-
},
151121
plugins: [
152122
serializedManifestPlugin({ settings, command, sync }),
153123
vitePluginRenderers({ settings }),
154124
await astroPluginRoutes({ routesList, settings, logger, fsMod: fs }),
155125
astroVirtualManifestPlugin(),
126+
vitePluginEnvironment({ settings, astroPkgsConfig, command }),
156127
pluginPage({ routesList }),
157128
pluginPages({ routesList }),
158129
configAliasVitePlugin({ settings }),
@@ -233,12 +204,6 @@ export async function createVite(
233204
replacement: 'astro/components',
234205
},
235206
],
236-
// Astro imports in third-party packages should use the same version as root
237-
dedupe: ['astro'],
238-
},
239-
ssr: {
240-
noExternal: [...ALWAYS_NOEXTERNAL, ...astroPkgsConfig.ssr.noExternal],
241-
external: [...(command === 'dev' ? ONLY_DEV_EXTERNAL : []), ...astroPkgsConfig.ssr.external],
242207
},
243208
build: { assetsDir: settings.config.build.assets },
244209
environments: {

packages/astro/src/types/public/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type {
3535
export type { AstroIntegrationLogger } from '../../core/logger/core.js';
3636
export { AstroSession } from '../../core/session.js';
3737
export type { ToolbarServerHelpers } from '../../runtime/client/dev-toolbar/helpers.js';
38+
export type { AstroEnvironmentNames } from '../../core/constants.js';
3839
export type * from './common.js';
3940
export type * from './config.js';
4041
export type * from './content.js';
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type * as vite from 'vite';
2+
import type { AstroSettings } from '../types/astro.js';
3+
import type { CrawlFrameworkPkgsResult } from 'vitefu';
4+
import type { EnvironmentOptions } from 'vite';
5+
import { ASTRO_VITE_ENVIRONMENT_NAMES } from '../core/constants.js';
6+
import { convertPathToPattern } from 'tinyglobby';
7+
import { fileURLToPath } from 'node:url';
8+
9+
// These specifiers are usually dependencies written in CJS, but loaded through Vite's transform
10+
// pipeline, which Vite doesn't support in development time. This hardcoded list temporarily
11+
// fixes things until Vite can properly handle them, or when they support ESM.
12+
const ONLY_DEV_EXTERNAL = [
13+
// Imported by `@astrojs/prism` which exposes `<Prism/>` that is processed by Vite
14+
'prismjs/components/index.js',
15+
// Imported by `astro/assets` -> `packages/astro/src/core/logger/core.ts`
16+
'string-width',
17+
// Imported by `astro:transitions` -> packages/astro/src/runtime/server/transition.ts
18+
'cssesc',
19+
];
20+
21+
const ALWAYS_NOEXTERNAL = [
22+
// This is only because Vite's native ESM doesn't resolve "exports" correctly.
23+
'astro',
24+
// Vite fails on nested `.astro` imports without bundling
25+
'astro/components',
26+
// Handle recommended nanostores. Only @nanostores/preact is required from our testing!
27+
// Full explanation and related bug report: https://github.com/withastro/astro/pull/3667
28+
'@nanostores/preact',
29+
// fontsource packages are CSS that need to be processed
30+
'@fontsource/*',
31+
];
32+
33+
interface Payload {
34+
command: 'dev' | 'build';
35+
settings: AstroSettings;
36+
astroPkgsConfig: CrawlFrameworkPkgsResult;
37+
}
38+
/**
39+
* This plugin is responsible of setting up the environments of the vite server, such as
40+
* dependencies, SSR, etc.
41+
*
42+
*/
43+
export function vitePluginEnvironment({
44+
command,
45+
settings,
46+
astroPkgsConfig,
47+
}: Payload): vite.Plugin {
48+
const srcDirPattern = convertPathToPattern(fileURLToPath(settings.config.srcDir));
49+
50+
return {
51+
name: 'astro:environment',
52+
configEnvironment(environmentName, _options): EnvironmentOptions {
53+
const finalEnvironmentOptions: EnvironmentOptions = {
54+
resolve: {
55+
// Astro imports in third-party packages should use the same version as root
56+
dedupe: ['astro'],
57+
},
58+
};
59+
if (
60+
environmentName === ASTRO_VITE_ENVIRONMENT_NAMES.server ||
61+
environmentName === ASTRO_VITE_ENVIRONMENT_NAMES.astro ||
62+
environmentName === ASTRO_VITE_ENVIRONMENT_NAMES.prerender ||
63+
environmentName === ASTRO_VITE_ENVIRONMENT_NAMES.client
64+
) {
65+
if (_options.resolve?.noExternal !== true) {
66+
finalEnvironmentOptions.resolve!.noExternal = [
67+
...ALWAYS_NOEXTERNAL,
68+
...astroPkgsConfig.ssr.noExternal,
69+
];
70+
finalEnvironmentOptions.resolve!.external = [
71+
...(command === 'dev' ? ONLY_DEV_EXTERNAL : []),
72+
...astroPkgsConfig.ssr.external,
73+
];
74+
}
75+
76+
if (_options.optimizeDeps?.noDiscovery === false) {
77+
finalEnvironmentOptions.optimizeDeps = {
78+
entries: [`${srcDirPattern}**/*.{jsx,tsx,vue,svelte,html,astro}`],
79+
exclude: ['astro', 'node-fetch'],
80+
};
81+
}
82+
}
83+
84+
if (environmentName === ASTRO_VITE_ENVIRONMENT_NAMES.client) {
85+
finalEnvironmentOptions.optimizeDeps = {
86+
// Astro files can't be rendered on the client
87+
entries: [`${srcDirPattern}**/*.{jsx,tsx,vue,svelte,html}`],
88+
};
89+
}
90+
91+
return finalEnvironmentOptions;
92+
},
93+
};
94+
}

packages/integrations/cloudflare/src/index.ts

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import type {
1717
IntegrationResolvedRoute,
1818
} from 'astro';
1919
import type { PluginOption } from 'vite';
20-
import { defaultClientConditions } from 'vite';
2120
import { cloudflareModuleLoader } from './utils/cloudflare-module-loader.js';
2221
import { createRoutesFile, getParts } from './utils/generate-routes-json.js';
2322
import { type ImageService, setImageConfig } from './utils/image-config.js';
@@ -211,20 +210,13 @@ export default function createIntegration(args?: Options): AstroIntegration {
211210
},
212211
session,
213212
vite: {
214-
ssr: {
215-
optimizeDeps: {
216-
// Disabled to prevent "prebundle" errors on first dev
217-
// This can be removed when the issue is resolved with Cloudflare
218-
noDiscovery: true,
219-
},
220-
},
221213
plugins: [
222214
cfVitePlugin(cfPluginConfig),
223215
// https://developers.cloudflare.com/pages/functions/module-support/
224216
// Allows imports of '.wasm', '.bin', and '.txt' file types
225217
cloudflareModulePlugin,
226218
{
227-
name: 'vite:cf-imports',
219+
name: '@astrojs/cloudflare:cf-imports',
228220
enforce: 'pre',
229221
resolveId(source) {
230222
if (source.startsWith('cloudflare:')) {
@@ -233,9 +225,21 @@ export default function createIntegration(args?: Options): AstroIntegration {
233225
return null;
234226
},
235227
},
228+
{
229+
name: '@astrojs/cloudflare:environment',
230+
configEnvironment(environmentName, _options) {
231+
if (environmentName === 'ssr' && _options.optimizeDeps?.noDiscovery === false) {
232+
return {
233+
optimizeDeps: {
234+
exclude: ['unstorage/drivers/cloudflare-kv-binding'],
235+
},
236+
};
237+
}
238+
},
239+
},
236240
{
237241
enforce: 'post',
238-
name: 'vite:cf-externals',
242+
name: '@astrojs/cloudflare:cf-externals',
239243
applyToEnvironment: (environment) => environment.name === 'ssr',
240244
config(conf) {
241245
if (conf.ssr) {
@@ -256,7 +260,6 @@ export default function createIntegration(args?: Options): AstroIntegration {
256260
addWatchFile(new URL('./wrangler.toml', config.root));
257261
addWatchFile(new URL('./wrangler.json', config.root));
258262
addWatchFile(new URL('./wrangler.jsonc', config.root));
259-
260263
},
261264
'astro:routes:resolved': ({ routes }) => {
262265
_routes = routes;
@@ -314,49 +317,28 @@ export default function createIntegration(args?: Options): AstroIntegration {
314317
const parsed = parse(data);
315318
Object.assign(process.env, parsed);
316319
} catch {
317-
logger.error(`Unable to parse .dev.vars, variables will not be available to your application.`);
320+
logger.error(
321+
`Unable to parse .dev.vars, variables will not be available to your application.`,
322+
);
318323
}
319324
}
320325
},
321326
'astro:build:setup': ({ vite, target }) => {
322327
if (target === 'server') {
323328
vite.resolve ||= {};
324329
vite.resolve.alias ||= {};
325-
326-
const aliases = [
327-
{
328-
find: 'react-dom/server',
329-
replacement: 'react-dom/server.browser',
330-
},
331-
];
332-
333-
if (Array.isArray(vite.resolve.alias)) {
334-
vite.resolve.alias = [...vite.resolve.alias, ...aliases];
335-
} else {
336-
for (const alias of aliases) {
337-
(vite.resolve.alias as Record<string, string>)[alias.find] = alias.replacement;
338-
}
339-
}
340-
341-
// Support `workerd` and `worker` conditions for the ssr environment
342-
// (previously supported in esbuild instead: https://github.com/withastro/astro/pull/7092)
343330
vite.ssr ||= {};
344-
vite.ssr.resolve ||= {};
345-
vite.ssr.resolve.conditions ||= [...defaultClientConditions];
346-
vite.ssr.resolve.conditions.push('workerd', 'worker');
347-
348-
vite.ssr.target = 'webworker';
349331
vite.ssr.noExternal = true;
350332

351333
vite.build ||= {};
352334
vite.build.rollupOptions ||= {};
353335
vite.build.rollupOptions.output ||= {};
336+
vite.build.rollupOptions.external = ['sharp'];
337+
354338
// @ts-expect-error
355339
vite.build.rollupOptions.output.banner ||=
356340
'globalThis.process ??= {}; globalThis.process.env ??= {};';
357341

358-
vite.build.rollupOptions.external = ['sharp'];
359-
360342
// Cloudflare env is only available per request. This isn't feasible for code that access env vars
361343
// in a global way, so we shim their access as `process.env.*`. This is not the recommended way for users to access environment variables. But we'll add this for compatibility for chosen variables. Mainly to support `@astrojs/db`
362344
vite.define = {

packages/integrations/cloudflare/test/external-image-service.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ describe('ExternalImageService', () => {
1616
});
1717
await fixture.build();
1818
});
19-
19+
2020
after(async () => {
2121
// await fixture.clean();
22-
})
22+
});
2323

2424
it('has correct image service', async () => {
25-
const files = await glob('**/index.mjs', {
25+
const files = await glob('**/image-service*', {
2626
cwd: fileURLToPath(new URL('dist/_worker.js', root)),
2727
filesOnly: true,
2828
absolute: true,

packages/integrations/cloudflare/test/fixtures/vite-plugin/astro.config.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// @ts-check
22
import cloudflare from '@astrojs/cloudflare';
33
import { defineConfig, envField, fontProviders } from 'astro/config';
4-
54
import mdx from '@astrojs/mdx';
65
import { fileURLToPath } from 'node:url';
76
import react from '@astrojs/react';
7+
import vue from "@astrojs/vue"
88

99
export default defineConfig({
1010
adapter: cloudflare({
@@ -32,7 +32,7 @@ export default defineConfig({
3232
"fr": "en"
3333
}
3434
},
35-
integrations: [mdx(), react()],
35+
integrations: [mdx(), react(), vue()],
3636
env: {
3737
schema: {
3838
FOO: envField.string({ context: 'server', access: 'public' }),

packages/integrations/cloudflare/test/fixtures/vite-plugin/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
"@astrojs/cloudflare": "workspace:*",
1111
"@astrojs/mdx": "^4.3.5",
1212
"@astrojs/react": "workspace:*",
13+
"@astrojs/vue": "workspace:*",
14+
"vue": "^3.5.25",
15+
"@vitejs/plugin-vue": "^6.0.2",
1316
"@types/react": "^18.3.24",
1417
"@types/react-dom": "^18.3.7",
1518
"astro": "workspace:*",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<template>
2+
<p>Hello from vue component</p>
3+
</template>

packages/integrations/cloudflare/test/fixtures/vite-plugin/src/pages/index.astro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export const prerender = false;
33
44
import { getCollection, getEntry, render } from 'astro:content';
55
import Hello from '../components/Hello.tsx';
6+
import HelloVue from '../components/Hello.vue';
67
import Island from '../components/Island.astro';
78
import IslandProps from '../components/IslandProps.astro';
89
import { Font } from 'astro:assets';
@@ -45,6 +46,7 @@ const surnamne = Astro.url.searchParams.get('surname');
4546
<p>Running on: {workerRuntime ?? 'unknown runtime'}</p>
4647
<div id="framework">
4748
<Hello client:load />
49+
<HelloVue client:load />
4850
</div>
4951
<Island server:defer/>
5052
<IslandProps server:defer name="Aria" surname={surnamne}/>

0 commit comments

Comments
 (0)