0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2024-12-23 21:53:55 -05:00
astro/packages/integrations/image/src/index.ts
Matthew Phillips 40226dd14d
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>
2022-11-15 10:02:23 -05:00

181 lines
5.6 KiB
TypeScript

import type { AstroConfig, AstroIntegration } from 'astro';
import { ssgBuild } from './build/ssg.js';
import type { ImageService, SSRImageService, TransformOptions } from './loaders/index.js';
import type { LoggerLevel } from './utils/logger.js';
import { joinPaths, prependForwardSlash, propsToFilename } from './utils/paths.js';
import { copyWasmFiles } from './vendor/squoosh/copy-wasm.js';
import { createPlugin } from './vite-plugin-astro-image.js';
export { getImage } from './lib/get-image.js';
export { getPicture } from './lib/get-picture.js';
const PKG_NAME = '@astrojs/image';
const ROUTE_PATTERN = '/_image';
const UNSUPPORTED_ADAPTERS = new Set([
'@astrojs/cloudflare',
'@astrojs/deno',
'@astrojs/netlify/edge-functions',
'@astrojs/vercel/edge',
]);
interface BuildConfig {
client: URL;
server: URL;
}
interface ImageIntegration {
loader?: ImageService;
defaultLoader: SSRImageService;
addStaticImage?: (transform: TransformOptions) => string;
}
declare global {
// eslint-disable-next-line no-var
var astroImage: ImageIntegration;
}
export interface IntegrationOptions {
/**
* Entry point for the @type {HostedImageService} or @type {LocalImageService} to be used.
*/
serviceEntryPoint?: '@astrojs/image/squoosh' | '@astrojs/image/sharp' | string;
logLevel?: LoggerLevel;
cacheDir?: false | string;
}
export default function integration(options: IntegrationOptions = {}): AstroIntegration {
const resolvedOptions = {
serviceEntryPoint: '@astrojs/image/squoosh',
logLevel: 'info' as LoggerLevel,
cacheDir: './node_modules/.astro/image',
...options,
};
let _config: AstroConfig;
let _buildConfig: BuildConfig;
let needsBuildConfig = false;
// During SSG builds, this is used to track all transformed images required.
const staticImages = new Map<string, Map<string, TransformOptions>>();
function getViteConfiguration(isDev: boolean) {
return {
plugins: [createPlugin(_config, resolvedOptions)],
build: {
rollupOptions: {
external: ['sharp'],
},
},
ssr: {
noExternal: ['@astrojs/image', resolvedOptions.serviceEntryPoint],
// Externalize CJS dependencies used by `serviceEntryPoint`. Vite dev mode has trouble
// loading these modules with `ssrLoadModule`, but works in build.
external: isDev ? ['http-cache-semantics', 'image-size', 'mime'] : [],
},
assetsInclude: ['**/*.wasm'],
};
}
return {
name: PKG_NAME,
hooks: {
'astro:config:setup': async ({ command, config, updateConfig, injectRoute }) => {
needsBuildConfig = !config.build?.server;
_config = config;
updateConfig({
vite: getViteConfiguration(command === 'dev'),
});
if (command === 'dev' || config.output === 'server') {
injectRoute({
pattern: ROUTE_PATTERN,
entryPoint: '@astrojs/image/endpoint',
});
}
const { default: defaultLoader } = await import(
resolvedOptions.serviceEntryPoint === '@astrojs/image/sharp'
? './loaders/sharp.js'
: './loaders/squoosh.js'
);
globalThis.astroImage = {
defaultLoader,
};
},
'astro:config:done': ({ config }) => {
_config = config;
_buildConfig = config.build;
},
'astro:build:start': ({ buildConfig }) => {
const adapterName = _config.adapter?.name;
if (adapterName && UNSUPPORTED_ADAPTERS.has(adapterName)) {
throw new Error(
`@astrojs/image is not supported with the ${adapterName} adapter. Please choose a Node.js compatible adapter.`
);
}
// Backwards compat
if (needsBuildConfig) {
_buildConfig = buildConfig;
}
},
'astro:build:setup': async () => {
// Used to cache all images rendered to HTML
// Added to globalThis to share the same map in Node and Vite
function addStaticImage(transform: TransformOptions) {
const srcTranforms = staticImages.has(transform.src)
? staticImages.get(transform.src)!
: new Map<string, TransformOptions>();
const filename = propsToFilename(transform);
srcTranforms.set(filename, transform);
staticImages.set(transform.src, srcTranforms);
// Prepend the Astro config's base path, if it was used.
// Doing this here makes sure that base is ignored when building
// staticImages to /dist, but the rendered HTML will include the
// base prefix for `src`.
return prependForwardSlash(joinPaths(_config.base, 'assets', filename));
}
// Helpers for building static images should only be available for SSG
if (_config.output === 'static') {
globalThis.astroImage.addStaticImage = addStaticImage;
}
},
'astro:build:generated': async ({ dir }) => {
// for SSG builds, build all requested image transforms to dist
const loader = globalThis?.astroImage?.loader;
if (resolvedOptions.serviceEntryPoint === '@astrojs/image/squoosh') {
// For the Squoosh service, copy all wasm files to dist/chunks.
// Because the default loader is dynamically imported (above),
// Vite will bundle squoosh to dist/chunks and expect to find the wasm files there
await copyWasmFiles(new URL('./chunks', dir));
}
if (loader && 'transform' in loader && staticImages.size > 0) {
const cacheDir = !!resolvedOptions.cacheDir
? new URL(resolvedOptions.cacheDir, _config.root)
: undefined;
await ssgBuild({
loader,
staticImages,
config: _config,
outDir: dir,
logLevel: resolvedOptions.logLevel,
cacheDir,
});
}
},
'astro:build:ssr': async () => {
if (resolvedOptions.serviceEntryPoint === '@astrojs/image/squoosh') {
await copyWasmFiles(new URL('./chunks/', _buildConfig.server));
}
},
},
};
}