This commit is contained in:
2026-07-14 16:11:39 +03:00
parent 1280eb6fcd
commit ab7042001a
126 changed files with 5670 additions and 4120 deletions

View File

@@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js'
export const nextAppTurbopackAdapter: ModeAdapter<'next@app/turbopack'> = {
mode: 'next@app/turbopack',
contractVersion: 5,
async generate(context) {
const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, {
rootViewBox: true,

View File

@@ -151,7 +151,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact
}
function manifestTypes(config: ResolvedSpriteConfig): string {
return [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n')
return [
header(config.generatedNotice),
'',
'export type SpriteManifestColor = {',
' variable: `--icon-color-${number}`',
' fallback: string',
'}',
'export type SpriteManifestIcon = {',
' name: string',
' id: string',
' viewBox: string | null',
' colors: readonly SpriteManifestColor[]',
'}',
'export type SpriteManifest = {',
' schemaVersion: 1',
" generator: '@gromlab/svg-sprites'",
' name: string',
' description?: string',
' componentName: string',
" mode: 'next@app/turbopack'",
" target: 'next@app/turbopack'",
" format: 'stack' | 'symbol'",
' iconCount: number',
' spriteUrl: string',
' icons: readonly SpriteManifestIcon[]',
'}',
'',
'export declare const spriteManifest: SpriteManifest',
'export default spriteManifest',
'',
].join('\n')
}
export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] {

View File

@@ -5,7 +5,6 @@ import { generateOutputFiles } from './output.js'
export const nextAppWebpackAdapter: ModeAdapter<'next@app/webpack'> = {
mode: 'next@app/webpack',
contractVersion: 5,
async generate(context) {
const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true })
const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack')

View File

@@ -70,7 +70,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact
return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n')
}
const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n')
const manifestTypes = (config: ResolvedSpriteConfig) => [
header(config.generatedNotice),
'',
'export type SpriteManifestColor = {',
' variable: `--icon-color-${number}`',
' fallback: string',
'}',
'export type SpriteManifestIcon = {',
' name: string',
' id: string',
' viewBox: string | null',
' colors: readonly SpriteManifestColor[]',
'}',
'export type SpriteManifest = {',
' schemaVersion: 1',
" generator: '@gromlab/svg-sprites'",
' name: string',
' description?: string',
' componentName: string',
" mode: 'next@app/webpack'",
" target: 'next@app/webpack'",
" format: 'stack' | 'symbol'",
' iconCount: number',
' spriteUrl: string',
' icons: readonly SpriteManifestIcon[]',
'}',
'',
'export declare const spriteManifest: SpriteManifest',
'export default spriteManifest',
'',
].join('\n')
export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] {
return [

View File

@@ -5,7 +5,6 @@ import { generateOutputFiles } from './output.js'
export const nextPagesTurbopackAdapter: ModeAdapter<'next@pages/turbopack'> = {
mode: 'next@pages/turbopack',
contractVersion: 5,
async generate(context) {
const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true })
const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack')

View File

@@ -70,7 +70,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact
return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n')
}
const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n')
const manifestTypes = (config: ResolvedSpriteConfig) => [
header(config.generatedNotice),
'',
'export type SpriteManifestColor = {',
' variable: `--icon-color-${number}`',
' fallback: string',
'}',
'export type SpriteManifestIcon = {',
' name: string',
' id: string',
' viewBox: string | null',
' colors: readonly SpriteManifestColor[]',
'}',
'export type SpriteManifest = {',
' schemaVersion: 1',
" generator: '@gromlab/svg-sprites'",
' name: string',
' description?: string',
' componentName: string',
" mode: 'next@pages/turbopack'",
" target: 'next@pages/turbopack'",
" format: 'stack' | 'symbol'",
' iconCount: number',
' spriteUrl: string',
' icons: readonly SpriteManifestIcon[]',
'}',
'',
'export declare const spriteManifest: SpriteManifest',
'export default spriteManifest',
'',
].join('\n')
export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] {
return [

View File

@@ -5,7 +5,6 @@ import { generateOutputFiles } from './output.js'
export const nextPagesWebpackAdapter: ModeAdapter<'next@pages/webpack'> = {
mode: 'next@pages/webpack',
contractVersion: 5,
async generate(context) {
const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, { rootViewBox: true })
const artifact = createCompiledSpriteArtifact(bytes, context.prepared.iconNames, 'stack')

View File

@@ -70,7 +70,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact
return [header(config.generatedNotice), "const spriteUrl = new URL('./sprite.svg', import.meta.url).href", '', `export const spriteManifest = ${source}`, '', 'export default spriteManifest', ''].join('\n')
}
const manifestTypes = (config: ResolvedSpriteConfig) => [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n')
const manifestTypes = (config: ResolvedSpriteConfig) => [
header(config.generatedNotice),
'',
'export type SpriteManifestColor = {',
' variable: `--icon-color-${number}`',
' fallback: string',
'}',
'export type SpriteManifestIcon = {',
' name: string',
' id: string',
' viewBox: string | null',
' colors: readonly SpriteManifestColor[]',
'}',
'export type SpriteManifest = {',
' schemaVersion: 1',
" generator: '@gromlab/svg-sprites'",
' name: string',
' description?: string',
' componentName: string',
" mode: 'next@pages/webpack'",
" target: 'next@pages/webpack'",
" format: 'stack' | 'symbol'",
' iconCount: number',
' spriteUrl: string',
' icons: readonly SpriteManifestIcon[]',
'}',
'',
'export declare const spriteManifest: SpriteManifest',
'export default spriteManifest',
'',
].join('\n')
export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] {
return [

View File

@@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js'
export const reactViteAdapter: ModeAdapter<'react@vite'> = {
mode: 'react@vite',
contractVersion: 5,
async generate(context) {
const bytes = await compileSpriteContent(
context.prepared.folder,

View File

@@ -241,7 +241,30 @@ function generateManifest(config: ResolvedSpriteConfig, artifact: CompiledSprite
function generateManifestDeclarations(config: ResolvedSpriteConfig): string {
return [
blockHeader(config.generatedNotice),
"import type { SpriteManifest } from '@gromlab/svg-sprites/react'",
'',
'export type SpriteManifestColor = {',
' variable: `--icon-color-${number}`',
' fallback: string',
'}',
'export type SpriteManifestIcon = {',
' name: string',
' id: string',
' viewBox: string | null',
' colors: readonly SpriteManifestColor[]',
'}',
'export type SpriteManifest = {',
' schemaVersion: 1',
" generator: '@gromlab/svg-sprites'",
' name: string',
' description?: string',
' componentName: string',
" mode: 'react@vite'",
" target: 'vite'",
" format: 'stack' | 'symbol'",
' iconCount: number',
' spriteUrl: string',
' icons: readonly SpriteManifestIcon[]',
'}',
'',
'export declare const spriteManifest: SpriteManifest',
'export default spriteManifest',

View File

@@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js'
export const reactWebpackAdapter: ModeAdapter<'react@webpack'> = {
mode: 'react@webpack',
contractVersion: 5,
async generate(context) {
const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, {
rootViewBox: false,

View File

@@ -168,7 +168,37 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact
}
function manifestTypes(config: ResolvedSpriteConfig): string {
return [header(config.generatedNotice), "import type { SpriteManifest } from '@gromlab/svg-sprites/react'", '', 'export declare const spriteManifest: SpriteManifest', 'export default spriteManifest', ''].join('\n')
return [
header(config.generatedNotice),
'',
'export type SpriteManifestColor = {',
' variable: `--icon-color-${number}`',
' fallback: string',
'}',
'export type SpriteManifestIcon = {',
' name: string',
' id: string',
' viewBox: string | null',
' colors: readonly SpriteManifestColor[]',
'}',
'export type SpriteManifest = {',
' schemaVersion: 1',
" generator: '@gromlab/svg-sprites'",
' name: string',
' description?: string',
' componentName: string',
" mode: 'react@webpack'",
" target: 'webpack'",
" format: 'stack' | 'symbol'",
' iconCount: number',
' spriteUrl: string',
' icons: readonly SpriteManifestIcon[]',
'}',
'',
'export declare const spriteManifest: SpriteManifest',
'export default spriteManifest',
'',
].join('\n')
}
export function generateOutputFiles(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): GeneratedFile[] {

View File

@@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js'
export const standaloneViteAdapter: ModeAdapter<'standalone@vite'> = {
mode: 'standalone@vite',
contractVersion: 1,
async generate(context) {
const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, {
rootViewBox: false,

View File

@@ -68,9 +68,10 @@ function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSprite
].join('\n')
}
function index(config: ResolvedSpriteConfig): string {
function index(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string {
const prefix = camel(config.name)
const pascalName = pascal(config.name)
const iconTagName = `${config.name}-icon`
return [
header(config.generatedNotice),
"import spriteUrl from './sprite.svg?no-inline'",
@@ -78,9 +79,92 @@ function index(config: ResolvedSpriteConfig): string {
'',
`export { ${prefix}IconIds, ${prefix}IconNames }`,
`export const ${prefix}SpriteUrl = spriteUrl`,
`export const ${prefix}IconTagName = ${JSON.stringify(iconTagName)}`,
`const ${prefix}IconViewBoxes = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.viewBox])), null, 2)}`,
`const ${prefix}IconElementMarker = Symbol.for('@gromlab/svg-sprites/icon-element')`,
`const ${prefix}IconElementSignature = ${prefix}IconTagName + '|' + ${prefix}SpriteUrl`,
`function has${pascalName}Icon(icon) {`,
` return Object.prototype.hasOwnProperty.call(${prefix}IconIds, icon)`,
'}',
`export function get${pascalName}IconHref(icon) {`,
` return ${prefix}SpriteUrl + '#' + ${prefix}IconIds[icon]`,
'}',
`export function define${pascalName}IconElement() {`,
` if (typeof globalThis.customElements === 'undefined' || typeof globalThis.HTMLElement === 'undefined') return`,
` const existing = globalThis.customElements.get(${prefix}IconTagName)`,
' if (existing) {',
` if (existing[${prefix}IconElementMarker] === ${prefix}IconElementSignature) return`,
` throw new Error('Cannot define <' + ${prefix}IconTagName + '>: this custom element name is already registered.')`,
' }',
` class ${pascalName}IconElement extends globalThis.HTMLElement {`,
" static observedAttributes = ['icon']",
' constructor() {',
' super()',
' const ownerDocument = this.ownerDocument',
" const shadowRoot = this.attachShadow({ mode: 'open' })",
" const style = ownerDocument.createElement('style')",
" style.textContent = ':host{display:inline-block;width:1em;height:1em;vertical-align:-0.125em}:host([hidden]){display:none}svg{display:block;width:100%;height:100%}svg[hidden]{display:none}'",
" this._svg = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'svg')",
" this._svg.setAttribute('part', 'svg')",
" this._svg.setAttribute('aria-hidden', 'true')",
" this._svg.setAttribute('focusable', 'false')",
" this._svg.setAttribute('hidden', '')",
" this._use = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'use')",
" this._use.setAttribute('part', 'use')",
' this._svg.append(this._use)',
' shadowRoot.append(style, this._svg)',
' }',
' connectedCallback() {',
" if (Object.prototype.hasOwnProperty.call(this, 'icon')) {",
' const icon = this.icon',
' delete this.icon',
' this.icon = icon',
' }',
' this._renderIcon()',
' }',
' attributeChangedCallback() {',
' this._renderIcon()',
' }',
' get icon() {',
" const icon = this.getAttribute('icon')",
` return icon !== null && has${pascalName}Icon(icon) ? icon : null`,
' }',
' set icon(icon) {',
' if (icon === null || icon === undefined) {',
" this.removeAttribute('icon')",
' return',
' }',
` if (!has${pascalName}Icon(icon)) throw new TypeError('Unknown ${config.name} icon: ' + icon)`,
" this.setAttribute('icon', icon)",
' }',
' _renderIcon() {',
" const icon = this.getAttribute('icon')",
' if (icon === null) {',
' this._clearIcon()',
' return',
' }',
` if (!has${pascalName}Icon(icon)) {`,
' this._clearIcon()',
` if (this._invalidIcon !== icon) console.error('<' + ${prefix}IconTagName + '>: unknown icon "' + icon + '".')`,
' this._invalidIcon = icon',
' return',
' }',
' this._invalidIcon = undefined',
` const viewBox = ${prefix}IconViewBoxes[icon]`,
" if (viewBox === null) this._svg.removeAttribute('viewBox')",
" else this._svg.setAttribute('viewBox', viewBox)",
` this._use.setAttribute('href', get${pascalName}IconHref(icon))`,
" this._svg.removeAttribute('hidden')",
' }',
' _clearIcon() {',
" this._use.removeAttribute('href')",
" this._svg.removeAttribute('viewBox')",
" this._svg.setAttribute('hidden', '')",
' }',
' }',
` Object.defineProperty(${pascalName}IconElement, ${prefix}IconElementMarker, { value: ${prefix}IconElementSignature })`,
` globalThis.customElements.define(${prefix}IconTagName, ${pascalName}IconElement)`,
'}',
'',
].join('\n')
}
@@ -88,6 +172,7 @@ function index(config: ResolvedSpriteConfig): string {
function indexDeclarations(config: ResolvedSpriteConfig): string {
const prefix = camel(config.name)
const pascalName = pascal(config.name)
const iconTagName = `${config.name}-icon`
return [
header(config.generatedNotice),
`import type { ${pascalName}IconName } from './icon-data.js'`,
@@ -95,7 +180,17 @@ function indexDeclarations(config: ResolvedSpriteConfig): string {
`export { ${prefix}IconIds, ${prefix}IconNames } from './icon-data.js'`,
`export type { ${pascalName}IconName } from './icon-data.js'`,
`export declare const ${prefix}SpriteUrl: string`,
`export declare const ${prefix}IconTagName: ${JSON.stringify(iconTagName)}`,
`export declare function get${pascalName}IconHref(icon: ${pascalName}IconName): string`,
`export interface ${pascalName}IconElement extends HTMLElement {`,
` icon: ${pascalName}IconName | null`,
'}',
`export declare function define${pascalName}IconElement(): void`,
'declare global {',
' interface HTMLElementTagNameMap {',
` ${JSON.stringify(iconTagName)}: ${pascalName}IconElement`,
' }',
'}',
'',
].join('\n')
}
@@ -133,11 +228,33 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact
function manifestTypes(config: ResolvedSpriteConfig): string {
return [
header(config.generatedNotice),
"import type { StandaloneSpriteManifest, StandaloneSpriteManifestData } from '@gromlab/svg-sprites'",
'',
"export declare const spriteManifestData: StandaloneSpriteManifestData<'standalone@vite'>",
"export declare function createSpriteManifest(spriteUrl: string): StandaloneSpriteManifest<'standalone@vite'>",
"export declare const spriteManifest: StandaloneSpriteManifest<'standalone@vite'>",
'export type SpriteManifestColor = {',
' variable: `--icon-color-${number}`',
' fallback: string',
'}',
'export type SpriteManifestIcon = {',
' name: string',
' id: string',
' viewBox: string | null',
' colors: readonly SpriteManifestColor[]',
'}',
'export type SpriteManifestData = {',
' schemaVersion: 1',
" generator: '@gromlab/svg-sprites'",
' name: string',
' description?: string',
" mode: 'standalone@vite'",
" target: 'vite'",
" format: 'stack' | 'symbol'",
' iconCount: number',
' icons: readonly SpriteManifestIcon[]',
'}',
'export type SpriteManifest = SpriteManifestData & { spriteUrl: string }',
'',
'export declare const spriteManifestData: SpriteManifestData',
'export declare function createSpriteManifest(spriteUrl: string): SpriteManifest',
'export declare const spriteManifest: SpriteManifest',
'export default spriteManifest',
'',
].join('\n')
@@ -148,7 +265,7 @@ export function generateOutputFiles(
artifact: CompiledSpriteArtifact,
): GeneratedFile[] {
return [
{ path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) },
{ path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config, artifact) },
{ path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) },
{ path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) },
{ path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) },

View File

@@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js'
export const standaloneWebpackAdapter: ModeAdapter<'standalone@webpack'> = {
mode: 'standalone@webpack',
contractVersion: 1,
async generate(context) {
const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, {
rootViewBox: false,

View File

@@ -68,18 +68,102 @@ function iconDeclarations(config: ResolvedSpriteConfig, artifact: CompiledSprite
].join('\n')
}
function index(config: ResolvedSpriteConfig): string {
function index(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact): string {
const prefix = camel(config.name)
const pascalName = pascal(config.name)
const iconTagName = `${config.name}-icon`
return [
header(config.generatedNotice),
`import { ${prefix}IconIds, ${prefix}IconNames } from './icon-data.js'`,
'',
`export { ${prefix}IconIds, ${prefix}IconNames }`,
`export const ${prefix}SpriteUrl = new URL('./sprite.svg', import.meta.url).href`,
`export const ${prefix}IconTagName = ${JSON.stringify(iconTagName)}`,
`const ${prefix}IconViewBoxes = ${JSON.stringify(Object.fromEntries(artifact.icons.map((icon) => [icon.name, icon.viewBox])), null, 2)}`,
`const ${prefix}IconElementMarker = Symbol.for('@gromlab/svg-sprites/icon-element')`,
`const ${prefix}IconElementSignature = ${prefix}IconTagName + '|' + ${prefix}SpriteUrl`,
`function has${pascalName}Icon(icon) {`,
` return Object.prototype.hasOwnProperty.call(${prefix}IconIds, icon)`,
'}',
`export function get${pascalName}IconHref(icon) {`,
` return ${prefix}SpriteUrl + '#' + ${prefix}IconIds[icon]`,
'}',
`export function define${pascalName}IconElement() {`,
` if (typeof globalThis.customElements === 'undefined' || typeof globalThis.HTMLElement === 'undefined') return`,
` const existing = globalThis.customElements.get(${prefix}IconTagName)`,
' if (existing) {',
` if (existing[${prefix}IconElementMarker] === ${prefix}IconElementSignature) return`,
` throw new Error('Cannot define <' + ${prefix}IconTagName + '>: this custom element name is already registered.')`,
' }',
` class ${pascalName}IconElement extends globalThis.HTMLElement {`,
" static observedAttributes = ['icon']",
' constructor() {',
' super()',
' const ownerDocument = this.ownerDocument',
" const shadowRoot = this.attachShadow({ mode: 'open' })",
" const style = ownerDocument.createElement('style')",
" style.textContent = ':host{display:inline-block;width:1em;height:1em;vertical-align:-0.125em}:host([hidden]){display:none}svg{display:block;width:100%;height:100%}svg[hidden]{display:none}'",
" this._svg = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'svg')",
" this._svg.setAttribute('part', 'svg')",
" this._svg.setAttribute('aria-hidden', 'true')",
" this._svg.setAttribute('focusable', 'false')",
" this._svg.setAttribute('hidden', '')",
" this._use = ownerDocument.createElementNS('http://www.w3.org/2000/svg', 'use')",
" this._use.setAttribute('part', 'use')",
' this._svg.append(this._use)',
' shadowRoot.append(style, this._svg)',
' }',
' connectedCallback() {',
" if (Object.prototype.hasOwnProperty.call(this, 'icon')) {",
' const icon = this.icon',
' delete this.icon',
' this.icon = icon',
' }',
' this._renderIcon()',
' }',
' attributeChangedCallback() {',
' this._renderIcon()',
' }',
' get icon() {',
" const icon = this.getAttribute('icon')",
` return icon !== null && has${pascalName}Icon(icon) ? icon : null`,
' }',
' set icon(icon) {',
' if (icon === null || icon === undefined) {',
" this.removeAttribute('icon')",
' return',
' }',
` if (!has${pascalName}Icon(icon)) throw new TypeError('Unknown ${config.name} icon: ' + icon)`,
" this.setAttribute('icon', icon)",
' }',
' _renderIcon() {',
" const icon = this.getAttribute('icon')",
' if (icon === null) {',
' this._clearIcon()',
' return',
' }',
` if (!has${pascalName}Icon(icon)) {`,
' this._clearIcon()',
` if (this._invalidIcon !== icon) console.error('<' + ${prefix}IconTagName + '>: unknown icon "' + icon + '".')`,
' this._invalidIcon = icon',
' return',
' }',
' this._invalidIcon = undefined',
` const viewBox = ${prefix}IconViewBoxes[icon]`,
" if (viewBox === null) this._svg.removeAttribute('viewBox')",
" else this._svg.setAttribute('viewBox', viewBox)",
` this._use.setAttribute('href', get${pascalName}IconHref(icon))`,
" this._svg.removeAttribute('hidden')",
' }',
' _clearIcon() {',
" this._use.removeAttribute('href')",
" this._svg.removeAttribute('viewBox')",
" this._svg.setAttribute('hidden', '')",
' }',
' }',
` Object.defineProperty(${pascalName}IconElement, ${prefix}IconElementMarker, { value: ${prefix}IconElementSignature })`,
` globalThis.customElements.define(${prefix}IconTagName, ${pascalName}IconElement)`,
'}',
'',
].join('\n')
}
@@ -87,6 +171,7 @@ function index(config: ResolvedSpriteConfig): string {
function indexDeclarations(config: ResolvedSpriteConfig): string {
const prefix = camel(config.name)
const pascalName = pascal(config.name)
const iconTagName = `${config.name}-icon`
return [
header(config.generatedNotice),
`import type { ${pascalName}IconName } from './icon-data.js'`,
@@ -94,7 +179,17 @@ function indexDeclarations(config: ResolvedSpriteConfig): string {
`export { ${prefix}IconIds, ${prefix}IconNames } from './icon-data.js'`,
`export type { ${pascalName}IconName } from './icon-data.js'`,
`export declare const ${prefix}SpriteUrl: string`,
`export declare const ${prefix}IconTagName: ${JSON.stringify(iconTagName)}`,
`export declare function get${pascalName}IconHref(icon: ${pascalName}IconName): string`,
`export interface ${pascalName}IconElement extends HTMLElement {`,
` icon: ${pascalName}IconName | null`,
'}',
`export declare function define${pascalName}IconElement(): void`,
'declare global {',
' interface HTMLElementTagNameMap {',
` ${JSON.stringify(iconTagName)}: ${pascalName}IconElement`,
' }',
'}',
'',
].join('\n')
}
@@ -132,11 +227,33 @@ function manifest(config: ResolvedSpriteConfig, artifact: CompiledSpriteArtifact
function manifestTypes(config: ResolvedSpriteConfig): string {
return [
header(config.generatedNotice),
"import type { StandaloneSpriteManifest, StandaloneSpriteManifestData } from '@gromlab/svg-sprites'",
'',
"export declare const spriteManifestData: StandaloneSpriteManifestData<'standalone@webpack'>",
"export declare function createSpriteManifest(spriteUrl: string): StandaloneSpriteManifest<'standalone@webpack'>",
"export declare const spriteManifest: StandaloneSpriteManifest<'standalone@webpack'>",
'export type SpriteManifestColor = {',
' variable: `--icon-color-${number}`',
' fallback: string',
'}',
'export type SpriteManifestIcon = {',
' name: string',
' id: string',
' viewBox: string | null',
' colors: readonly SpriteManifestColor[]',
'}',
'export type SpriteManifestData = {',
' schemaVersion: 1',
" generator: '@gromlab/svg-sprites'",
' name: string',
' description?: string',
" mode: 'standalone@webpack'",
" target: 'webpack'",
" format: 'stack' | 'symbol'",
' iconCount: number',
' icons: readonly SpriteManifestIcon[]',
'}',
'export type SpriteManifest = SpriteManifestData & { spriteUrl: string }',
'',
'export declare const spriteManifestData: SpriteManifestData',
'export declare function createSpriteManifest(spriteUrl: string): SpriteManifest',
'export declare const spriteManifest: SpriteManifest',
'export default spriteManifest',
'',
].join('\n')
@@ -147,7 +264,7 @@ export function generateOutputFiles(
artifact: CompiledSpriteArtifact,
): GeneratedFile[] {
return [
{ path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config) },
{ path: path.posix.join(OUTPUT_DIR, 'index.js'), content: index(config, artifact) },
{ path: path.posix.join(OUTPUT_DIR, 'index.d.ts'), content: indexDeclarations(config) },
{ path: path.posix.join(OUTPUT_DIR, 'sprite.svg'), content: svg(artifact.bytes, config.generatedNotice) },
{ path: path.posix.join(OUTPUT_DIR, 'icon-data.js'), content: iconData(config, artifact) },

View File

@@ -5,8 +5,6 @@ import { generateOutputFiles } from './output.js'
export const standaloneAdapter: ModeAdapter<'standalone'> = {
mode: 'standalone',
contractVersion: 2,
async generate(context) {
const bytes = await compileSpriteContent(context.prepared.folder, context.config.transform, {
rootViewBox: false,
@@ -15,6 +13,7 @@ export const standaloneAdapter: ModeAdapter<'standalone'> = {
return {
files: generateOutputFiles(context.config, artifact),
createGitignore: false,
paths: {
generatedDir: '.svg-sprite',
sprite: '.svg-sprite/sprite.svg',