summaryrefslogtreecommitdiff
path: root/packages/integrations/image/src/vite-plugin-astro-image.ts
blob: aefc910bb0546b171a752a0a381486ceba0039e5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import type { AstroConfig } from 'astro';
import { pathToFileURL } from 'node:url';
import type { PluginContext } from 'rollup';
import slash from 'slash';
import type { Plugin, ResolvedConfig } from 'vite';
import type { IntegrationOptions } from './index.js';
import type { InputFormat } from './loaders/index.js';
import { metadata } from './utils/metadata.js';

export interface ImageMetadata {
	src: string;
	width: number;
	height: number;
	format: InputFormat;
}

export function createPlugin(config: AstroConfig, options: Required<IntegrationOptions>): Plugin {
	const filter = (id: string) =>
		/^(?!\/_image?).*.(heic|heif|avif|jpeg|jpg|png|tiff|webp|gif)$/.test(id);

	const virtualModuleId = 'virtual:image-loader';

	let resolvedConfig: ResolvedConfig;
	let loaderModuleId: string;

	async function resolveLoader(context: PluginContext) {
		if (!loaderModuleId) {
			const module = await context.resolve(options.serviceEntryPoint);
			if (!module) {
				throw new Error(`"${options.serviceEntryPoint}" could not be found`);
			}
			loaderModuleId = module.id;
		}

		return loaderModuleId;
	}

	return {
		name: '@astrojs/image',
		enforce: 'pre',
		configResolved(viteConfig) {
			resolvedConfig = viteConfig;
		},
		async resolveId(id) {
			// The virtual model redirects imports to the ImageService being used
			// This ensures the module is available in `astro dev` and is included
			// in the SSR server bundle.
			if (id === virtualModuleId) {
				return await resolveLoader(this);
			}
		},
		async load(id) {
			// only claim image ESM imports
			if (!filter(id)) {
				return null;
			}

			const meta = await metadata(id);

			const fileUrl = pathToFileURL(id);
			const src = resolvedConfig.isProduction
				? fileUrl.pathname.replace(config.srcDir.pathname, '/')
				: id;

			const output = {
				...meta,
				src: slash(src), // Windows compat
			};

			return `export default ${JSON.stringify(output)}`;
		},
	};
}