From ad55d4e517837cd935aed46c2ff06992fb81cfc9 Mon Sep 17 00:00:00 2001 From: Michael Stramel Date: Fri, 4 Oct 2024 23:11:07 -0500 Subject: [PATCH] add behind an experiment flag --- packages/astro/client.d.ts | 6 +++-- packages/astro/src/@types/astro.ts | 17 +++++++++++++ packages/astro/src/assets/runtime.ts | 25 +++++++++---------- packages/astro/src/assets/utils/svg.ts | 8 +++--- .../astro/src/assets/vite-plugin-assets.ts | 5 ++-- packages/astro/src/core/config/schema.ts | 11 ++++++++ 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/packages/astro/client.d.ts b/packages/astro/client.d.ts index bc79287ee000a..18b43264096a5 100644 --- a/packages/astro/client.d.ts +++ b/packages/astro/client.d.ts @@ -2,6 +2,8 @@ /// /// +import type { SvgRenderMode } from './dist/assets/utils/svg'; + // eslint-disable-next-line @typescript-eslint/no-namespace declare namespace App { // eslint-disable-next-line @typescript-eslint/no-empty-interface @@ -123,9 +125,9 @@ declare module '*.svg' { */ size?: number | string; /** - * Bypasses automatic sprite optimization by directly inlinining the SVG + * Override the default rendering mode for SVGs */ - inline?: boolean + mode?: SvgRenderMode } & astroHTML.JSX.SVGAttributes const Component: ((_props: Props) => any) & ImageMetadata; diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 4cba5610b0c18..5fe67a5f6e189 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -57,6 +57,7 @@ import type { REDIRECT_STATUS_CODES, SUPPORTED_MARKDOWN_FILE_EXTENSIONS, } from './../core/constants.js'; +import type { SvgRenderMode } from '../assets/utils/svg.js'; export type { AstroIntegrationLogger, ToolbarServerHelpers }; @@ -2348,6 +2349,22 @@ export interface AstroUserConfig { * For a complete overview and the full API reference, see [the Content Layer API RFC](https://github.com/withastro/roadmap/blob/content-layer/proposals/0050-content-layer.md) and [share your feedback](https://github.com/withastro/roadmap/pull/982). */ contentLayer?: boolean; + + /** + * @docs + * @name experimental.svg + * @type {object} + * @default `undefined` + */ + svg?: { + /** + * @docs + * @name experimental.svg.mode + * @type {string} + * @default 'inline' + */ + mode?: SvgRenderMode; + }; }; } diff --git a/packages/astro/src/assets/runtime.ts b/packages/astro/src/assets/runtime.ts index c19483f2c277a..c1ec69f4b79a4 100644 --- a/packages/astro/src/assets/runtime.ts +++ b/packages/astro/src/assets/runtime.ts @@ -21,24 +21,23 @@ export function createSvgComponent({ meta, attributes, children }: SvgComponentP const id = `a:${ids++}`; const rendered = new WeakSet(); const Component = createComponent((result, props) => { - const { title: titleProp, viewBox, ...normalizedProps } = normalizeProps(attributes, props); + const { title: titleProp, viewBox, mode, ...normalizedProps } = normalizeProps(attributes, props); const title = titleProp ? unescapeHTML(`${titleProp}`) : ''; - // Bypasses automatic sprite optimization and directly inline the SVG - if (normalizedProps['inline']) { - delete normalizedProps.inline; - return render`${title}${unescapeHTML(children)}` - } + if (mode === 'sprite') { + // On the first render, include the symbol definition + let symbol: any = ''; + if (!rendered.has(result.response)) { + // We only need the viewBox on the symbol definition, we can drop it everywhere else + symbol = unescapeHTML(`${children}`); + rendered.add(result.response); + } - // On the first render, include the symbol definition - let symbol: any = ''; - if (!rendered.has(result.response)) { - // We only need the viewBox on the symbol definition, we can drop it everywhere else - symbol = unescapeHTML(`${children}`); - rendered.add(result.response); + return render`${title}${symbol}`; } - return render`${title}${symbol}`; + // Default to inline mode + return render`${title}${unescapeHTML(children)}` }); makeNonEnumerable(Component); diff --git a/packages/astro/src/assets/utils/svg.ts b/packages/astro/src/assets/utils/svg.ts index 7674606b9ea43..c55831504fb7a 100644 --- a/packages/astro/src/assets/utils/svg.ts +++ b/packages/astro/src/assets/utils/svg.ts @@ -4,7 +4,7 @@ import type { SvgComponentProps } from '../runtime.js'; type SvgAttributes = Record; /** - * Some attributes required for `image/svg+xml` are irrelevant when inlined ina `text/html` document. We can save a few bytes by dropping them. + * Some attributes required for `image/svg+xml` are irrelevant when inlined in a `text/html` document. We can save a few bytes by dropping them. */ const ATTRS_TO_DROP = ['xmlns', 'xmlns:xlink', 'version']; const DEFAULT_ATTRS: SvgAttributes = { role: 'img' }; @@ -40,12 +40,14 @@ function parseSvg(contents: string) { return { attributes, body }; } -export function makeSvgComponent(meta: ImageMetadata, contents: Buffer | string) { +export type SvgRenderMode = 'inline' | 'sprite'; + +export function makeSvgComponent(meta: ImageMetadata, contents: Buffer | string, options?: { mode?: SvgRenderMode }) { const file = typeof contents === 'string' ? contents : contents.toString('utf-8'); const { attributes, body: children } = parseSvg(file); const props: SvgComponentProps = { meta, - attributes: dropAttributes(attributes), + attributes: dropAttributes({ mode: options?.mode, ...attributes }), children, }; diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index d16b829c87de7..0afcf7d4f4af3 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -230,9 +230,10 @@ export default function assets({ }); } - if (/\.svg$/.test(id)) { + if (settings.config.experimental.svg && /\.svg$/.test(id)) { const { contents, ...metadata } = imageMetadata; - return makeSvgComponent(metadata, contents!); + // We know that the contents are present, as we only emit this property for SVG files + return makeSvgComponent(metadata, contents!, { mode: settings.config.experimental.svg.mode }); } // We can only reliably determine if an image is used on the server, as we need to track its usage throughout the entire build. diff --git a/packages/astro/src/core/config/schema.ts b/packages/astro/src/core/config/schema.ts index ad10f725ac6c4..d85c593d4f943 100644 --- a/packages/astro/src/core/config/schema.ts +++ b/packages/astro/src/core/config/schema.ts @@ -93,6 +93,9 @@ export const ASTRO_CONFIG_DEFAULTS = { validateSecrets: false, }, contentLayer: false, + svg: { + mode: 'inline', + }, }, } satisfies AstroUserConfig & { server: { open: boolean } }; @@ -544,6 +547,14 @@ export const AstroConfigSchema = z.object({ .optional() .default(ASTRO_CONFIG_DEFAULTS.experimental.contentIntellisense), contentLayer: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.contentLayer), + svg: z + .object({ + mode: z + .union([z.literal('inline'), z.literal('sprite')]) + .optional() + .default(ASTRO_CONFIG_DEFAULTS.experimental.svg.mode), + }) + .optional(), }) .strict( `Invalid or outdated experimental feature.\nCheck for incorrect spelling or outdated Astro version.\nSee https://docs.astro.build/en/reference/configuration-reference/#experimental-flags for a list of all current experiments.`,