UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

62 lines (61 loc) • 2.23 kB
import { generateCspDigest } from "../core/encryption.js"; import { createComponent, render, spreadAttributes, unescapeHTML } from "../runtime/server/index.js"; export function createSvgComponent({ meta, attributes, children, styles }) { const hasStyles = styles.length > 0; const Component = createComponent({ async factory(result, props) { const normalizedProps = normalizeProps(attributes, props); // When CSP is enabled, hash each SVG <style> so the browser allows them. // The styles stay inside the <svg> where they belong — we only need the // hashes registered before the CSP meta tag is emitted in the <head>. // propagation: 'self' ensures init() runs during bufferHeadContent(). if (hasStyles && result.cspDestination) { for (const style of styles) { const hash = await generateCspDigest(style, result.cspAlgorithm); result._metadata.extraStyleHashes.push(hash); } } return render`<svg${spreadAttributes(normalizedProps)}>${unescapeHTML(children)}</svg>`; }, propagation: hasStyles ? "self" : "none" }); if (import.meta.env.DEV) { // Prevent revealing that this is a component makeNonEnumerable(Component); // Maintaining the current `console.log` output for SVG imports Object.defineProperty(Component, Symbol.for("nodejs.util.inspect.custom"), { value: (_, opts, inspect) => inspect(meta, opts) }); } Object.defineProperty(Component, "toJSON", { value: () => meta, enumerable: false }); // Attaching the metadata to the component to maintain current functionality return Object.assign(Component, meta); } /** * 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 = {}; export function dropAttributes(attributes) { for (const attr of ATTRS_TO_DROP) { delete attributes[attr]; } return attributes; } function normalizeProps(attributes, props) { return dropAttributes({ ...DEFAULT_ATTRS, ...attributes, ...props }); } function makeNonEnumerable(object) { for (const property in object) { Object.defineProperty(object, property, { enumerable: false }); } }