ronantakizawa/codereview-bench huggingface dataset:
Repository: withastro/astro
File: packages/astro/src/core/sync/index.ts (TypeScript)

Code before:
 * @return {Promise<ProcessExit>}
 */
async function syncContentCollections(
	settings: AstroSettings,
	{
		mode,
		logger,
		fs,
		routesList,
		manifest,
	}: Required<Pick<SyncOptions, 'mode' | 'logger' | 'fs' | 'routesList' | 'manifest'>>,
): Promise<void> {
	// Needed to load content config
	const tempViteServer = await createServer(
		await createVite(
			{
				server: { middlewareMode: true, hmr: false, watch: null, ws: false },
				optimizeDeps: { noDiscovery: true },
				ssr: { external: [] },
				logLevel: 'silent',
			},
			{
				settings: {
					...settings,
					// Prevent mutation by vite plugins during sync
					buildOutput: undefined,
					injectedCsp: {
						fontResources: new Set(),
						styleHashes: [],
					},
				},
				logger,
				mode,
				command: 'build',
				fs,
				sync: true,
				routesList,
				manifest,
			},
		),
	);

	// Patch `hot.send` to bubble up error events
	// `hot.on('error')` does not fire for some reason
	const hotSend = tempViteServer.hot.send;
	tempViteServer.hot.send = (payload: HMRPayload) => {
		if (payload.type === 'error') {
			throw payload.err;
		}
		return hotSend(payload);
	};

Review comment (suggestion):
@ematipico can you update the suggestion to explain what's going on with the build output?
```suggestion
					// TODO:
```

Code after:
 * @return {Promise<ProcessExit>}
 */
async function syncContentCollections(
	settings: AstroSettings,
	{
		mode,
		logger,
		fs,
		routesList,
		manifest,
	}: Required<Pick<SyncOptions, 'mode' | 'logger' | 'fs' | 'routesList' | 'manifest'>>,
): Promise<void> {
	// Needed to load content config
	const tempViteServer = await createServer(
		await createVite(
			{
				server: { middlewareMode: true, hmr: false, watch: null, ws: false },
				optimizeDeps: { noDiscovery: true },
				ssr: { external: [] },
				logLevel: 'silent',
			},
			{
				// Prevent mutation of specific properties by vite plugins during sync
				settings: {
					...settings,
					// Prevent mutation by vite plugins during sync
					buildOutput: undefined,
					// Sync causes font resources and style hashes to be duplicated
					injectedCsp: {
						fontResources: new Set(),
						styleHashes: [],
					},
				},
				logger,
				mode,
				command: 'build',
				fs,
				sync: true,
				routesList,
				manifest,
			},
		),
	);

	// Patch `hot.send` to bubble up error events
	// `hot.on('error')` does not fire for some reason
	const hotSend = tempViteServer.hot.send;
	tempViteServer.hot.send = (payload: HMRPayload) => {
		if (payload.type === 'error') {
			throw payload.err;
		}
Repository: withastro/astro
File: packages/astro/src/core/sync/index.ts (TypeScript)

Code before:
	settings: AstroSettings,
	{
		mode,
		logger,
		fs,
		routesList,
		manifest,
	}: Required<Pick<SyncOptions, 'mode' | 'logger' | 'fs' | 'routesList' | 'manifest'>>,
): Promise<void> {
	// Needed to load content config
	const tempViteServer = await createServer(
		await createVite(
			{
				server: { middlewareMode: true, hmr: false, watch: null, ws: false },
				optimizeDeps: { noDiscovery: true },
				ssr: { external: [] },
				logLevel: 'silent',
			},
			{
				settings: {
					...settings,
					// Prevent mutation by vite plugins during sync
					buildOutput: undefined,
					injectedCsp: {
						fontResources: new Set(),
						styleHashes: [],
					},
				},
				logger,
				mode,
				command: 'build',
				fs,
				sync: true,
				routesList,
				manifest,
			},
		),
	);

	// Patch `hot.send` to bubble up error events
	// `hot.on('error')` does not fire for some reason
	const hotSend = tempViteServer.hot.send;
	tempViteServer.hot.send = (payload: HMRPayload) => {
		if (payload.type === 'error') {
			throw payload.err;
		}
		return hotSend(payload);
	};

	try {
		const contentTypesGenerator = await createContentTypesGenerator({

Review comment (refactor):
```suggestion
					// Sync causes font resources and style hashes to be duplicated
					injectedCsp: {
```

Code after:
	settings: AstroSettings,
	{
		mode,
		logger,
		fs,
		routesList,
		manifest,
	}: Required<Pick<SyncOptions, 'mode' | 'logger' | 'fs' | 'routesList' | 'manifest'>>,
): Promise<void> {
	// Needed to load content config
	const tempViteServer = await createServer(
		await createVite(
			{
				server: { middlewareMode: true, hmr: false, watch: null, ws: false },
				optimizeDeps: { noDiscovery: true },
				ssr: { external: [] },
				logLevel: 'silent',
			},
			{
				// Prevent mutation of specific properties by vite plugins during sync
				settings: {
					...settings,
					// Prevent mutation by vite plugins during sync
					buildOutput: undefined,
					// Sync causes font resources and style hashes to be duplicated
					injectedCsp: {
						fontResources: new Set(),
						styleHashes: [],
					},
				},
				logger,
				mode,
				command: 'build',
				fs,
				sync: true,
				routesList,
				manifest,
			},
		),
	);

	// Patch `hot.send` to bubble up error events
	// `hot.on('error')` does not fire for some reason
	const hotSend = tempViteServer.hot.send;
	tempViteServer.hot.send = (payload: HMRPayload) => {
		if (payload.type === 'error') {
			throw payload.err;
		}
		return hotSend(payload);
	};
Repository: withastro/astro
File: packages/astro/src/core/sync/index.ts (TypeScript)

Code before:
 */
async function syncContentCollections(
	settings: AstroSettings,
	{
		mode,
		logger,
		fs,
		routesList,
		manifest,
	}: Required<Pick<SyncOptions, 'mode' | 'logger' | 'fs' | 'routesList' | 'manifest'>>,
): Promise<void> {
	// Needed to load content config
	const tempViteServer = await createServer(
		await createVite(
			{
				server: { middlewareMode: true, hmr: false, watch: null, ws: false },
				optimizeDeps: { noDiscovery: true },
				ssr: { external: [] },
				logLevel: 'silent',
			},
			{ settings, logger, mode, command: 'build', fs, sync: true, routesList, manifest },
		),
	);

	// Patch `hot.send` to bubble up error events
	// `hot.on('error')` does not fire for some reason
	const hotSend = tempViteServer.hot.send;
	tempViteServer.hot.send = (payload: HMRPayload) => {
		if (payload.type === 'error') {
			throw payload.err;
		}
		return hotSend(payload);
	};

	try {
		const contentTypesGenerator = await createContentTypesGenerator({
			contentConfigObserver: globalContentConfigObserver,
			logger: logger,
			fs,
			settings,
			viteServer: tempViteServer,
		});
		const typesResult = await contentTypesGenerator.init();

		const contentConfig = globalContentConfigObserver.get();
		if (contentConfig.status === 'error') {
			throw contentConfig.error;
		}

		if (typesResult.typesGenerated === false) {
			switch (typesResult.reason) {

Review comment (refactor):
Do you think we can achieve this using TypeScript? I know it has the type [`Readonly`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype)

Code after:
 */
async function syncContentCollections(
	settings: AstroSettings,
	{
		mode,
		logger,
		fs,
		routesList,
		manifest,
	}: Required<Pick<SyncOptions, 'mode' | 'logger' | 'fs' | 'routesList' | 'manifest'>>,
): Promise<void> {
	// Needed to load content config
	const tempViteServer = await createServer(
		await createVite(
			{
				server: { middlewareMode: true, hmr: false, watch: null, ws: false },
				optimizeDeps: { noDiscovery: true },
				ssr: { external: [] },
				logLevel: 'silent',
			},
			{
				// Prevent mutation of specific properties by vite plugins during sync
				settings: {
					...settings,
					// Prevent mutation by vite plugins during sync
					buildOutput: undefined,
					// Sync causes font resources and style hashes to be duplicated
					injectedCsp: {
						fontResources: new Set(),
						styleHashes: [],
					},
				},
				logger,
				mode,
				command: 'build',
				fs,
				sync: true,
				routesList,
				manifest,
			},
		),
	);

	// Patch `hot.send` to bubble up error events
	// `hot.on('error')` does not fire for some reason
	const hotSend = tempViteServer.hot.send;
	tempViteServer.hot.send = (payload: HMRPayload) => {
		if (payload.type === 'error') {
			throw payload.err;
		}
		return hotSend(payload);
Repository: withastro/astro
File: packages/astro/src/assets/fonts/constants.ts (TypeScript)

Code before:
import type { Defaults, FontType } from './types.js';

export const LOCAL_PROVIDER_NAME = 'local';

export const DEFAULTS: Defaults = {
	weights: ['400'],
	styles: ['normal', 'italic'],
	subsets: ['cyrillic-ext', 'cyrillic', 'greek-ext', 'greek', 'vietnamese', 'latin-ext', 'latin'],
	// Technically serif is the browser default but most websites these days use sans-serif
	fallbacks: ['sans-serif'],
	optimizedFallbacks: true,
};

/** Used to serialize data, to be used by public APIs */
export const VIRTUAL_MODULE_ID = 'virtual:astro:assets/fonts/internal';
export const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;

export const ASSETS_DIR = 'fonts';
export const CACHE_DIR = './fonts/';

export const FONT_TYPES = ['woff2', 'woff', 'otf', 'ttf', 'eot'] as const;

export const FONT_FORMATS: Array<{ type: FontType; format: string }> = [
	{ type: 'woff2', format: 'woff2' },
	{ type: 'woff', format: 'woff' },
	{ type: 'otf', format: 'opentype' },
	{ type: 'ttf', format: 'truetype' },
	{ type: 'eot', format: 'embedded-opentype' },
];

export const GENERIC_FALLBACK_NAMES = [
	'serif',
	'sans-serif',

Review comment (suggestion):
Oh, I’m seeing now we _already_ didn’t include some important subsets. I think this makes sense then, as much as I dislike encoding this bias.

I’d say we should _require_ setting `subsets`, but given not all providers support it, that would end up being pretty confusing I think.

Code after:
import type { Defaults, FontType } from './types.js';

export const LOCAL_PROVIDER_NAME = 'local';

export const DEFAULTS: Defaults = {
	weights: ['400'],
	styles: ['normal', 'italic'],
	subsets: ['latin'],
	// Technically serif is the browser default but most websites these days use sans-serif
	fallbacks: ['sans-serif'],
	optimizedFallbacks: true,
};

/** Used to serialize data, to be used by public APIs */
export const VIRTUAL_MODULE_ID = 'virtual:astro:assets/fonts/internal';
export const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;

export const ASSETS_DIR = 'fonts';
export const CACHE_DIR = './fonts/';

export const FONT_TYPES = ['woff2', 'woff', 'otf', 'ttf', 'eot'] as const;

export const FONT_FORMATS: Array<{ type: FontType; format: string }> = [
	{ type: 'woff2', format: 'woff2' },
	{ type: 'woff', format: 'woff' },
	{ type: 'otf', format: 'opentype' },
	{ type: 'ttf', format: 'truetype' },
	{ type: 'eot', format: 'embedded-opentype' },
];

export const GENERIC_FALLBACK_NAMES = [
	'serif',
	'sans-serif',
Repository: withastro/astro
File: packages/astro/src/core/build/generate.ts (TypeScript)

Code before:
				// paths to ensure routing priority rules are enforced in the final build.
				const matchedRoute = matchRoute(decodeURI(staticPath), options.routesList);

				if (!matchedRoute) {
					// No route matched this path, so we can skip it.
					return false;
				}

				if (matchedRoute === route) {
					// Current route is higher-priority. Include it for building.
					return true;
				}

				// Current route is lower-priority than matchedRoute.
				// Path will be skipped due to collision.
				if (config.prerenderConflictBehavior === 'error') {
					throw new AstroError({
						...AstroErrorData.PrerenderRouteConflict,
						message: AstroErrorData.PrerenderRouteConflict.message(
							matchedRoute.route,
							route.route,
							normalized,
						),
						hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route),
					});
				} else if (config.prerenderConflictBehavior === 'warning') {
					const msg = AstroErrorData.PrerenderRouteConflict.message(
						matchedRoute.route,
						route.route,
						normalized,
					);
					logger.warn('build', msg);
				}

				return false;
			});

		// Add each path to the builtPaths set, to avoid building it again later.
		for (const staticPath of paths) {
			builtPaths.add(removeTrailingForwardSlash(staticPath));
		}
	}

	return paths;
}

function getInvalidRouteSegmentError(
	e: TypeError,
	route: RouteData,
	staticPath: GetStaticPathsItem,
): AstroError {

Review comment (suggestion):
```suggestion
				} else if (config.prerenderConflictBehavior === 'warn') {
```

Code after:
				// paths to ensure routing priority rules are enforced in the final build.
				const matchedRoute = matchRoute(decodeURI(staticPath), options.routesList);

				if (!matchedRoute) {
					// No route matched this path, so we can skip it.
					return false;
				}

				if (matchedRoute === route) {
					// Current route is higher-priority. Include it for building.
					return true;
				}

				// Current route is lower-priority than matchedRoute.
				// Path will be skipped due to collision.
				if (config.prerenderConflictBehavior === 'error') {
					throw new AstroError({
						...AstroErrorData.PrerenderRouteConflict,
						message: AstroErrorData.PrerenderRouteConflict.message(
							matchedRoute.route,
							route.route,
							normalized,
						),
						hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route),
					});
				} else if (config.prerenderConflictBehavior === 'warn') {
					const msg = AstroErrorData.PrerenderRouteConflict.message(
						matchedRoute.route,
						route.route,
						normalized,
					);
					logger.warn('build', msg);
				}

				return false;
			});

		// Add each path to the builtPaths set, to avoid building it again later.
		for (const staticPath of paths) {
			builtPaths.add(removeTrailingForwardSlash(staticPath));
		}
	}

	return paths;
}

function getInvalidRouteSegmentError(
	e: TypeError,
	route: RouteData,
	staticPath: GetStaticPathsItem,
): AstroError {
Repository: withastro/astro
File: packages/astro/src/core/config/schemas/base.ts (TypeScript)

Code before:
	},
	devToolbar: {
		enabled: true,
	},
	compressHTML: true,
	server: {
		host: false,
		port: 4321,
		open: false,
		allowedHosts: [],
	},
	integrations: [],
	markdown: markdownConfigDefaults,
	vite: {},
	legacy: {},
	redirects: {},
	security: {
		checkOrigin: true,
		allowedDomains: [],
	},
	env: {
		schema: {},
		validateSecrets: false,
	},
	session: undefined,
	prerenderConflictBehavior: 'warning',
	experimental: {
		clientPrerender: false,
		contentIntellisense: false,
		csp: false,
		chromeDevtoolsWorkspace: false,
		svgo: false,
	},
} satisfies AstroUserConfig & { server: { open: boolean } };

const highlighterTypesSchema = z
	.union([z.literal('shiki'), z.literal('prism')])
	.default(syntaxHighlightDefaults.type);

export const AstroConfigSchema = z.object({
	root: z
		.string()
		.optional()
		.default(ASTRO_CONFIG_DEFAULTS.root)
		.transform((val) => new URL(val)),
	srcDir: z
		.string()
		.optional()
		.default(ASTRO_CONFIG_DEFAULTS.srcDir)
		.transform((val) => new URL(val)),
	publicDir: z

Review comment (suggestion):
```suggestion
	prerenderConflictBehavior: 'warn',
```

Code after:
	},
	devToolbar: {
		enabled: true,
	},
	compressHTML: true,
	server: {
		host: false,
		port: 4321,
		open: false,
		allowedHosts: [],
	},
	integrations: [],
	markdown: markdownConfigDefaults,
	vite: {},
	legacy: {},
	redirects: {},
	security: {
		checkOrigin: true,
		allowedDomains: [],
	},
	env: {
		schema: {},
		validateSecrets: false,
	},
	session: undefined,
	prerenderConflictBehavior: 'warn',
	experimental: {
		clientPrerender: false,
		contentIntellisense: false,
		csp: false,
		chromeDevtoolsWorkspace: false,
		svgo: false,
	},
} satisfies AstroUserConfig & { server: { open: boolean } };

const highlighterTypesSchema = z
	.union([z.literal('shiki'), z.literal('prism')])
	.default(syntaxHighlightDefaults.type);

export const AstroConfigSchema = z.object({
	root: z
		.string()
		.optional()
		.default(ASTRO_CONFIG_DEFAULTS.root)
		.transform((val) => new URL(val)),
	srcDir: z
		.string()
		.optional()
		.default(ASTRO_CONFIG_DEFAULTS.srcDir)
		.transform((val) => new URL(val)),
	publicDir: z
Repository: withastro/astro
File: packages/astro/src/core/build/plugins/plugin-noop.ts (TypeScript)

Code before:
import type * as vite from 'vite';

export const NOOP_MODULE_ID = 'virtual:astro:noop';
const RESOLVED_NOOP_MODULE_ID = '\0' + NOOP_MODULE_ID;

// An empty module that does nothing. This can be used as a placeholder
// when you just need a module to be in the graph.
// We use this for the client build when there are no client modules,
// because the publicDir copying happens in the client build.
export function pluginNoop(): vite.Plugin {
	return {
		name: 'plugin-noop',
		resolveId(id) {
			if(id === NOOP_MODULE_ID) {
				return RESOLVED_NOOP_MODULE_ID;
			}
		},
		load(id) {
			if(id === RESOLVED_NOOP_MODULE_ID) {
				return '';
			}
		}
	}
}


Review comment (bug):
I'll be honest, even after reading the comment, I don't understand why we need a noop plugin. The comment doesn't explain what's the correlation between a "plugin that does nothing" and "placeholder [..] module to be in the graph". If there are some vitejs resources, let's use them.

The comment from line 8 to line 9 should belong to `packages/astro/src/core/build/static-build.ts`, where you use `NOOP_MODULE_ID`, so we explain why we do that. Also, I fail to understand the connection between `publicDir` and this plugin.

Code after:
import type * as vite from 'vite';

export const NOOP_MODULE_ID = 'virtual:astro:noop';
const RESOLVED_NOOP_MODULE_ID = '\0' + NOOP_MODULE_ID;

// An empty module that does nothing. This can be used as a placeholder
// when you just need a module to be in the graph.
// We use this for the client build when there are no client modules,
// because the publicDir copying happens in the client build.
export function pluginNoop(): vite.Plugin {
	return {
		name: 'plugin-noop',
		resolveId(id) {
			if(id === NOOP_MODULE_ID) {
				return RESOLVED_NOOP_MODULE_ID;
			}
		},
		load(id) {
			if(id === RESOLVED_NOOP_MODULE_ID) {
				return '';
			}
		},
		generateBundle(_options, bundle) {
			// Delete this bundle so that its not written out to disk.
			for(const [name, chunk] of Object.entries(bundle)) {
				if(chunk.type === 'asset') continue;
				if(chunk.facadeModuleId === RESOLVED_NOOP_MODULE_ID) {
					delete bundle[name];
				}
			}
		}
	}
}
Repository: withastro/astro
File: packages/astro/src/core/csp/runtime.ts (TypeScript)

Code before:
import type { CspDirective } from './config.js';
import type { SSRManifestCSP } from '../app/types.js';

/**
 * `existingDirective` is something like `img-src 'self'`. Same as `newDirective.
 *
 * Returns `undefined` if no directive has been deduped
 * @param existingDirective
 * @param newDirective
 */
export function deduplicateDirectiveValues(
	existingDirective: CspDirective,
	newDirective: CspDirective,
): CspDirective | undefined {
	const [directiveName, ...existingValues] = existingDirective.split(' ');
	const [newDirectiveName, ...newValues] = newDirective.split(' ');
	if (directiveName !== newDirectiveName) {
		return undefined;
	}
	const finalDirectives = Array.from(new Set([...existingValues, ...newValues]));

	return `${directiveName} ${finalDirectives.join(' ')}` as CspDirective;
}

export function pushDirective(
	directives: SSRManifestCSP['directives'],
	newDirective: CspDirective,
): SSRManifestCSP['directives'] {
	const finalDirectives: SSRManifestCSP['directives'] = [];
	let deduplicated = false;

Review comment (suggestion):
```suggestion
 * `existingDirective` is something like `img-src 'self'`. Same as `newDirective`.
```

Code after:
import type { CspDirective } from './config.js';
import type { SSRManifestCSP } from '../app/types.js';

/**
 * `existingDirective` is something like `img-src 'self'`. Same as `newDirective`.
 *
 * Returns `undefined` if no directive has been deduped
 * @param existingDirective
 * @param newDirective
 */
export function deduplicateDirectiveValues(
	existingDirective: CspDirective,
	newDirective: CspDirective,
): CspDirective | undefined {
	const [directiveName, ...existingValues] = existingDirective
		// split spaces
		.split(/\s+/)
		// Avoid duplicated spaces
		.filter(Boolean);
	const [newDirectiveName, ...newValues] = newDirective
		// split spaces
		.split(/\s+/)
		// Avoid duplicated spaces
		.filter(Boolean);
	if (directiveName !== newDirectiveName) {
		return undefined;
	}
	const finalDirectives = Array.from(new Set([...existingValues, ...newValues]));

	return `${directiveName} ${finalDirectives.join(' ')}` as CspDirective;
Repository: withastro/astro
File: packages/astro/src/actions/vite-plugin-actions.ts (TypeScript)

Code before:
	ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID,
	OPTIONS_VIRTUAL_MODULE_ID,
	RESOLVED_NOOP_ENTRYPOINT_VIRTUAL_MODULE_ID,
	RESOLVED_OPTIONS_VIRTUAL_MODULE_ID,
	RESOLVED_RUNTIME_VIRTUAL_MODULE_ID,
	RESOLVED_VIRTUAL_MODULE_ID,
	RUNTIME_VIRTUAL_MODULE_ID,
	VIRTUAL_MODULE_ID,
} from './consts.js';
import { isActionsFilePresent } from './utils.js';
import { ASTRO_VITE_ENVIRONMENT_NAMES } from '../core/constants.js';

/**
 * This plugin is used to retrieve the final entry point of the bundled actions.ts file
 * @param opts
 * @param internals
 */
export function vitePluginActionsBuild(
	opts: StaticBuildOptions,
	internals: BuildInternals,
): VitePlugin {
	return {
		name: '@astro/plugin-actions-build',

		applyToEnvironment(environment) {
			return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.server;
		},

		writeBundle(_, bundle) {
			for (const [chunkName, chunk] of Object.entries(bundle)) {
				if (
					chunk.type !== 'asset' &&
					chunk.facadeModuleId === ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID
				) {
					const outputDirectory = getServerOutputDirectory(opts.settings);
					internals.astroActionsEntryPoint = new URL(chunkName, outputDirectory);
				}
			}
		},
	};
}

export function vitePluginActions({
	fs,
	settings,
}: {
	fs: typeof fsMod;
	settings: AstroSettings;
}): VitePlugin {
	let resolvedActionsId: string;


Review comment (suggestion):
in my opinion this should be named `ssr` not `server` to match the Vite-defined name. Soft opinion, open to discussion.

Code after:
	ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID,
	OPTIONS_VIRTUAL_MODULE_ID,
	RESOLVED_NOOP_ENTRYPOINT_VIRTUAL_MODULE_ID,
	RESOLVED_OPTIONS_VIRTUAL_MODULE_ID,
	RESOLVED_RUNTIME_VIRTUAL_MODULE_ID,
	RESOLVED_VIRTUAL_MODULE_ID,
	RUNTIME_VIRTUAL_MODULE_ID,
	VIRTUAL_MODULE_ID,
} from './consts.js';
import { isActionsFilePresent } from './utils.js';
import { ASTRO_VITE_ENVIRONMENT_NAMES } from '../core/constants.js';

/**
 * This plugin is used to retrieve the final entry point of the bundled actions.ts file
 * @param opts
 * @param internals
 */
export function vitePluginActionsBuild(
	opts: StaticBuildOptions,
	internals: BuildInternals,
): VitePlugin {
	return {
		name: '@astro/plugin-actions-build',

		applyToEnvironment(environment) {
			return environment.name === ASTRO_VITE_ENVIRONMENT_NAMES.ssr;
		},

		writeBundle(_, bundle) {
			for (const [chunkName, chunk] of Object.entries(bundle)) {
				if (
					chunk.type !== 'asset' &&
					chunk.facadeModuleId === ACTIONS_RESOLVED_ENTRYPOINT_VIRTUAL_MODULE_ID
				) {
					const outputDirectory = getServerOutputDirectory(opts.settings);
					internals.astroActionsEntryPoint = new URL(chunkName, outputDirectory);
				}
			}
		},
	};
}

export function vitePluginActions({
	fs,
	settings,
}: {
	fs: typeof fsMod;
	settings: AstroSettings;
}): VitePlugin {
	let resolvedActionsId: string;