From 97bda9a6a000c411e61910b9aaf147271ff7d8fb Mon Sep 17 00:00:00 2001 From: NullVoxPopuli-ai-agent Date: Wed, 22 Jul 2026 09:22:27 -0400 Subject: [PATCH 1/2] Add tsdown example project; fix Rolldown sprite assembly and dts output The rolldown entry point existed but the sprite-assembly hooks (renderChunk/generateBundle) were only registered for vite and rollup, so Rolldown-based builds (including tsdown) left __SVG_JAR_SPRITE____ placeholders unreplaced and never emitted sprite or ?file assets. Register the same hooks under the rolldown key. With tsdown's dts: true, SVG imports re-exported from a library entry polluted the declaration bundle with runtime JS: rolldown-plugin-dts resolves imports from virtual .d.ts twins, and the plugin resolved those SVG imports into the JS module graph. Resolve SVG imports from declaration importers to virtual .d.ts modules instead, loaded as per-target declaration code mirroring client/*.d.ts, so libraries get correctly typed exports (and the JS chunk graph stays unpolluted). Also: - test-projects/tsdown/library: icon library example exercising sprite, named sprite, inline, and file modes with declaration output; built by build:test-projects - Rolldown integration test mirroring the Rollup one, asserting placeholders are replaced and assets emitted - fix codegen.test.ts missing isEmbedded (typecheck was failing) Co-Authored-By: Claude Fable 5 --- CONTRIBUTING.md | 5 +- plugin/README.md | 31 ++++++- plugin/package.json | 3 +- plugin/src/codegen/dts.ts | 65 +++++++++++++ plugin/src/core/query.ts | 51 ++++++++++ plugin/src/hooks/load.ts | 8 +- plugin/src/hooks/resolve-id.ts | 12 ++- plugin/src/index.ts | 5 + plugin/test/_fixtures/entry-file-mode.js | 3 + plugin/test/integration/rolldown.test.ts | 93 +++++++++++++++++++ plugin/test/unit/codegen.test.ts | 1 + plugin/test/unit/dts.test.ts | 71 ++++++++++++++ plugin/test/unit/query.test.ts | 43 ++++++++- pnpm-lock.yaml | 18 ++++ pnpm-workspace.yaml | 1 + test-projects/tsdown/library/.gitignore | 2 + test-projects/tsdown/library/package.json | 17 ++++ .../tsdown/library/src/icons/arrow.svg | 1 + .../tsdown/library/src/icons/circle.svg | 1 + .../tsdown/library/src/icons/square.svg | 1 + .../tsdown/library/src/icons/star.svg | 4 + test-projects/tsdown/library/src/index.ts | 11 +++ test-projects/tsdown/library/src/types.d.ts | 6 ++ test-projects/tsdown/library/tsconfig.json | 11 +++ test-projects/tsdown/library/tsdown.config.ts | 10 ++ 25 files changed, 465 insertions(+), 9 deletions(-) create mode 100644 plugin/src/codegen/dts.ts create mode 100644 plugin/test/_fixtures/entry-file-mode.js create mode 100644 plugin/test/integration/rolldown.test.ts create mode 100644 plugin/test/unit/dts.test.ts create mode 100644 test-projects/tsdown/library/.gitignore create mode 100644 test-projects/tsdown/library/package.json create mode 100644 test-projects/tsdown/library/src/icons/arrow.svg create mode 100644 test-projects/tsdown/library/src/icons/circle.svg create mode 100644 test-projects/tsdown/library/src/icons/square.svg create mode 100644 test-projects/tsdown/library/src/icons/star.svg create mode 100644 test-projects/tsdown/library/src/index.ts create mode 100644 test-projects/tsdown/library/src/types.d.ts create mode 100644 test-projects/tsdown/library/tsconfig.json create mode 100644 test-projects/tsdown/library/tsdown.config.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0baa515..5963770 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,6 +63,9 @@ test-projects/vite/ One Vite app per supported framework target ├── vanilla/ ├── vue/ └── web-component/ + +test-projects/tsdown/ tsdown (Rolldown) projects +└── library/ Icon library with declaration output ``` ## Tests @@ -77,7 +80,7 @@ pnpm --filter @svg-jar/plugin test:unit ### Integration tests -A Rollup integration test builds a minimal bundle and asserts the output. +Rollup and Rolldown integration tests build a minimal bundle and assert the output. ```sh pnpm --filter @svg-jar/plugin test:unit # includes integration/ diff --git a/plugin/README.md b/plugin/README.md index 132758e..57b8dcf 100644 --- a/plugin/README.md +++ b/plugin/README.md @@ -12,7 +12,7 @@ -An [unplugin](https://github.com/unjs/unplugin) for importing SVGs as components. Supports sprite sheets, inline SVGs, and raw file exports across Vite and Rollup. +An [unplugin](https://github.com/unjs/unplugin) for importing SVGs as components. Supports sprite sheets, inline SVGs, and raw file exports across Vite, Rollup, and Rolldown (including [tsdown](https://tsdown.dev)). ## Install @@ -44,6 +44,33 @@ export default { }; ``` +### Rolldown + +```ts +// rolldown.config.ts +import svgJar from '@svg-jar/plugin/rolldown'; + +export default { + plugins: [svgJar({ target: 'ember' })], +}; +``` + +### tsdown + +[tsdown](https://tsdown.dev) is built on Rolldown, so it uses the Rolldown entry point. This is useful for building component libraries that re-export SVG components — declaration files (`dts: true`) get correctly typed exports for each SVG import. + +```ts +// tsdown.config.ts +import { defineConfig } from 'tsdown'; +import svgJar from '@svg-jar/plugin/rolldown'; + +export default defineConfig({ + entry: ['src/index.ts'], + dts: true, + plugins: [svgJar({ target: 'dom' })], +}); +``` + ## Usage ### Sprite mode (default) @@ -422,7 +449,7 @@ For a thorough analysis of this problem, see Cynthia Rey's [The state of SVGs on ## Requirements - Node.js >= 20 -- Vite or Rollup +- Vite, Rollup, or Rolldown (including tsdown) ## Acknowledgements diff --git a/plugin/package.json b/plugin/package.json index d811a51..fb0302b 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -81,7 +81,7 @@ ], "scripts": { "build": "tsdown", - "build:test-projects": "tsdown && for dir in ../test-projects/vite/*/; do (cd \"$dir\" && pnpm vite build); done", + "build:test-projects": "tsdown && for dir in ../test-projects/vite/*/; do (cd \"$dir\" && pnpm vite build); done && for dir in ../test-projects/tsdown/*/; do (cd \"$dir\" && pnpm build); done", "dev": "tsdown --watch", "format": "prettier --write .", "lint": "eslint .", @@ -104,6 +104,7 @@ "devDependencies": { "@playwright/test": "^1.59.1", "execa": "catalog:", + "rolldown": "catalog:", "rollup": "catalog:", "tsdown": "catalog:", "vitest": "catalog:" diff --git a/plugin/src/codegen/dts.ts b/plugin/src/codegen/dts.ts new file mode 100644 index 0000000..5913729 --- /dev/null +++ b/plugin/src/codegen/dts.ts @@ -0,0 +1,65 @@ +import type { SvgJarTarget } from '../core/options.ts'; +import type { SvgDtsKind } from '../core/query.ts'; + +/** + * Declaration code for component-shaped SVG imports, per target. + * + * These mirror the ambient declarations in `client/*.d.ts`, but as real + * module declarations so declaration bundlers (rolldown-plugin-dts, used + * by tsdown) can emit correct types for libraries that re-export SVG + * components. + */ +const COMPONENT_DTS: Record = { + dom: [ + `import type { SvgOptions } from '@svg-jar/plugin/runtime/dom';`, + `declare const component: (options?: SvgOptions) => SVGSVGElement;`, + `export default component;`, + ].join('\n'), + + ember: [ + `import type { ComponentLike } from '@glint/template';`, + `declare const Component: ComponentLike<{ Element: SVGSVGElement; Blocks: { default: [] } }>;`, + `export default Component;`, + ].join('\n'), + + react: [ + `import type { FC, SVGAttributes, ReactNode } from 'react';`, + `declare const Component: FC & { children?: ReactNode }>;`, + `export default Component;`, + ].join('\n'), + + preact: [ + `import type { FunctionComponent, JSX } from 'preact';`, + `declare const Component: FunctionComponent>;`, + `export default Component;`, + ].join('\n'), + + vue: [ + `import type { Component } from 'vue';`, + `declare const component: Component;`, + `export default component;`, + ].join('\n'), + + solid: [ + `import type { Component, JSX, ParentProps } from 'solid-js';`, + `declare const component: Component>>;`, + `export default component;`, + ].join('\n'), + + 'web-component': [`declare const ElementClass: typeof HTMLElement;`, `export default ElementClass;`].join('\n'), +}; + +/** Declaration code for `?file` imports - a plain URL string. */ +const FILE_DTS = [`declare const url: string;`, `export default url;`].join('\n'); + +/** + * Generates declaration (`.d.ts`) code for a virtual SVG declaration + * module. Served by the `load` hook when a declaration bundler resolves + * an SVG import from a `.d.ts` module. + * + * @param target Framework target from plugin options. + * @param kind `'component'` for sprite/inline imports, `'file'` for `?file`. + */ +export function generateDts(target: SvgJarTarget, kind: SvgDtsKind): string { + return kind === 'file' ? FILE_DTS : COMPONENT_DTS[target]; +} diff --git a/plugin/src/core/query.ts b/plugin/src/core/query.ts index f125448..c41dbbd 100644 --- a/plugin/src/core/query.ts +++ b/plugin/src/core/query.ts @@ -92,3 +92,54 @@ export function isSvgId(id: string): boolean { const filePath = queryIndex === -1 ? id : id.slice(0, queryIndex); return filePath.endsWith('.svg'); } + +/** + * The shape of a virtual SVG declaration module. + * + * - `'component'` - sprite/inline imports (default export is a component) + * - `'file'` - `?file` imports (default export is a URL string) + */ +export type SvgDtsKind = 'component' | 'file'; + +/** Suffix for virtual declaration modules of component-shaped SVG imports. */ +const SVG_DTS_COMPONENT_SUFFIX = '.svg-jar.d.ts'; + +/** Suffix for virtual declaration modules of `?file` SVG imports. */ +const SVG_DTS_FILE_SUFFIX = '.svg-jar-file.d.ts'; + +/** + * Returns true if the importer is a declaration module (`.d.ts`, `.d.mts`, + * `.d.cts`). Declaration bundlers like rolldown-plugin-dts (used by tsdown) + * resolve imports from virtual `.d.ts` twins of the source modules; SVG + * imports from those need declaration code, not component code. + */ +export function isDtsImporter(importer: string | undefined): boolean { + return importer != null && /\.d\.[cm]?ts$/.test(importer); +} + +/** + * Builds the module ID for a virtual SVG declaration module. + * + * The ID must end in `.d.ts` so declaration bundlers treat the module as + * declaration code. The query string is dropped - all component-shaped + * imports of the same file share one type, so they dedupe naturally. + * + * @example + * makeSvgDtsId('/project/icon.svg', 'component') + * // → '/project/icon.svg.svg-jar.d.ts' + */ +export function makeSvgDtsId(filePath: string, kind: SvgDtsKind): string { + return filePath + (kind === 'file' ? SVG_DTS_FILE_SUFFIX : SVG_DTS_COMPONENT_SUFFIX); +} + +/** + * Parses a virtual SVG declaration module ID created by {@link makeSvgDtsId}. + * + * @returns The declaration kind, or `null` if the ID is not a virtual + * SVG declaration module. + */ +export function parseSvgDtsId(id: string): SvgDtsKind | null { + if (id.endsWith(SVG_DTS_FILE_SUFFIX)) return 'file'; + if (id.endsWith(SVG_DTS_COMPONENT_SUFFIX)) return 'component'; + return null; +} diff --git a/plugin/src/hooks/load.ts b/plugin/src/hooks/load.ts index b0fe0ff..f0d0778 100644 --- a/plugin/src/hooks/load.ts +++ b/plugin/src/hooks/load.ts @@ -2,7 +2,8 @@ import { readFileSync } from 'node:fs'; import path from 'node:path'; import type { UnpluginBuildContext, UnpluginContext } from 'unplugin'; import type { PluginState } from '../core/state.ts'; -import { parseSvgId, isSvgId } from '../core/query.ts'; +import { parseSvgId, isSvgId, parseSvgDtsId } from '../core/query.ts'; +import { generateDts } from '../codegen/dts.ts'; import { parseSvg, hashSvg } from '../svg/parse.ts'; import { optimizeSvg } from '../svg/optimize.ts'; import { applyCurrentColor, stripDimensions, findEmbeddedRefs } from '../svg/transform.ts'; @@ -46,6 +47,11 @@ export function createLoadHook( state: PluginState, ): (this: UnpluginBuildContext & UnpluginContext, id: string) => Promise { return async function load(id) { + // Virtual SVG declaration modules (created by resolveId for imports + // from `.d.ts` modules) load as declaration code for the target. + const dtsKind = parseSvgDtsId(id); + if (dtsKind) return generateDts(state.options.target, dtsKind); + if (!isSvgId(id)) return null; const parsed = parseSvgId(id); diff --git a/plugin/src/hooks/resolve-id.ts b/plugin/src/hooks/resolve-id.ts index eec6ef9..c1dee35 100644 --- a/plugin/src/hooks/resolve-id.ts +++ b/plugin/src/hooks/resolve-id.ts @@ -1,7 +1,7 @@ import { existsSync } from 'node:fs'; import path from 'node:path'; import type { UnpluginBuildContext, UnpluginContext } from 'unplugin'; -import { isSvgId } from '../core/query.ts'; +import { isSvgId, isDtsImporter, makeSvgDtsId, parseSvgId } from '../core/query.ts'; /** * Creates the `resolveId` hook function. @@ -28,18 +28,24 @@ export function createResolveIdHook(): ( const filePath = queryIndex === -1 ? id : id.slice(0, queryIndex); const query = queryIndex === -1 ? '' : id.slice(queryIndex); + // SVG imports from declaration modules (rolldown-plugin-dts / tsdown + // resolve imports from virtual `.d.ts` twins) resolve to a virtual + // declaration module instead of the component module, so the emitted + // `.d.ts` bundle gets type declarations rather than runtime JS. + const dtsKind = isDtsImporter(importer) ? (parseSvgId(id).mode === 'file' ? 'file' : 'component') : null; + // Relative or absolute paths - resolve and re-append query. if (filePath.startsWith('.') || filePath.startsWith('/')) { if (!importer) return null; const resolved = path.resolve(path.dirname(importer), filePath); - return resolved + query; + return dtsKind ? makeSvgDtsId(resolved, dtsKind) : resolved + query; } // Bare specifier - try naive node_modules resolution. // This bypasses package.json `exports` which often doesn't include .svg files. if (importer) { const resolved = resolveFromNodeModules(filePath, path.dirname(importer)); - if (resolved) return resolved + query; + if (resolved) return dtsKind ? makeSvgDtsId(resolved, dtsKind) : resolved + query; } return null; diff --git a/plugin/src/index.ts b/plugin/src/index.ts index 509ff65..ed12f67 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -68,6 +68,11 @@ export const SvgJarPlugin: UnpluginInstance = renderChunk: createRenderChunkHook(state) as never, generateBundle: createGenerateBundleHook(state) as never, }, + + rolldown: { + renderChunk: createRenderChunkHook(state) as never, + generateBundle: createGenerateBundleHook(state) as never, + }, }; }); diff --git a/plugin/test/_fixtures/entry-file-mode.js b/plugin/test/_fixtures/entry-file-mode.js new file mode 100644 index 0000000..254f1d0 --- /dev/null +++ b/plugin/test/_fixtures/entry-file-mode.js @@ -0,0 +1,3 @@ +import simpleUrl from './simple.svg?file'; + +console.log(simpleUrl); diff --git a/plugin/test/integration/rolldown.test.ts b/plugin/test/integration/rolldown.test.ts new file mode 100644 index 0000000..fe20c15 --- /dev/null +++ b/plugin/test/integration/rolldown.test.ts @@ -0,0 +1,93 @@ +import path from 'node:path'; +import { rolldown, type RolldownOutput } from 'rolldown'; +import { describe, it, expect, beforeAll } from 'vitest'; +import { SvgJarPlugin } from '../../src/index.ts'; +import { PLACEHOLDER_RE } from '../../src/core/constants.ts'; + +const FIXTURES_DIR = path.join(import.meta.dirname, '..', '_fixtures'); +const ENTRY = path.join(FIXTURES_DIR, 'entry.js'); +const ENTRY_FILE_MODE = path.join(FIXTURES_DIR, 'entry-file-mode.js'); + +/** + * Builds the test fixture with Rolldown + svg-jar plugin and returns the output. + */ +async function build(options = {}, entry = ENTRY): Promise { + const bundle = await rolldown({ + input: entry, + plugins: [SvgJarPlugin.rolldown(options)], + // The generated components import the runtime from the published + // package name, which is not resolvable from inside the package itself. + external: [/^@svg-jar\/plugin\/runtime\//], + }); + return bundle.generate({ format: 'esm' }); +} + +function findChunk(output: RolldownOutput) { + const chunk = output.output.find((o) => o.type === 'chunk'); + if (!chunk || chunk.type !== 'chunk') throw new Error('no chunk in output'); + return chunk; +} + +function findAsset(output: RolldownOutput, match: (fileName: string) => boolean) { + return output.output.find((o) => o.type === 'asset' && match(o.fileName)); +} + +describe('Rolldown integration', () => { + let output: RolldownOutput; + + beforeAll(async () => { + output = await build({ target: 'dom' }); + }); + + describe('output chunks', () => { + it('generates a single output chunk', () => { + const chunks = output.output.filter((o) => o.type === 'chunk'); + expect(chunks).toHaveLength(1); + }); + + it('contains createSvg import for sprite mode SVGs', () => { + expect(findChunk(output).code).toContain('createSvg'); + }); + + it('replaces all sprite placeholders with final sprite URLs', () => { + const code = findChunk(output).code; + + PLACEHOLDER_RE.lastIndex = 0; + expect(code).not.toMatch(PLACEHOLDER_RE); + expect(code).toMatch(/\/assets\/sprite-[a-f0-9]+\.svg#[a-f0-9]+/); + expect(code).toMatch(/\/assets\/nav-[a-f0-9]+\.svg#[a-f0-9]+/); + }); + }); + + describe('sprite files', () => { + it('emits a default sprite asset with content hash', () => { + expect(findAsset(output, (f) => f.startsWith('assets/sprite-') && f.endsWith('.svg'))).toBeDefined(); + }); + + it('emits a named sprite asset with content hash', () => { + expect(findAsset(output, (f) => f.startsWith('assets/nav-') && f.endsWith('.svg'))).toBeDefined(); + }); + + it('default sprite contains the simple icon symbol', () => { + const asset = findAsset(output, (f) => f.startsWith('assets/sprite-')); + expect(asset?.type === 'asset' && asset.source).toContain(' { + let fileOutput: RolldownOutput; + + beforeAll(async () => { + fileOutput = await build({ target: 'dom' }, ENTRY_FILE_MODE); + }); + + it('exports the emitted asset URL', () => { + const code = findChunk(fileOutput).code; + expect(code).toMatch(/\/assets\/simple-[a-f0-9]+\.svg/); + }); + + it('emits the SVG as a standalone file asset', () => { + expect(findAsset(fileOutput, (f) => /^assets\/simple-[a-f0-9]+\.svg$/.test(f))).toBeDefined(); + }); + }); +}); diff --git a/plugin/test/unit/codegen.test.ts b/plugin/test/unit/codegen.test.ts index a80f5bc..1c043c7 100644 --- a/plugin/test/unit/codegen.test.ts +++ b/plugin/test/unit/codegen.test.ts @@ -11,6 +11,7 @@ const BASE_CTX: CodegenContext = { height: '24', mode: 'sprite', isDev: false, + isEmbedded: false, svgMarkup: '', symbolMarkup: '', refSymbols: [], diff --git a/plugin/test/unit/dts.test.ts b/plugin/test/unit/dts.test.ts new file mode 100644 index 0000000..fee3604 --- /dev/null +++ b/plugin/test/unit/dts.test.ts @@ -0,0 +1,71 @@ +import { describe, it, expect } from 'vitest'; +import { generateDts } from '../../src/codegen/dts.ts'; +import type { SvgJarTarget } from '../../src/core/options.ts'; + +const TARGETS: SvgJarTarget[] = ['dom', 'ember', 'react', 'preact', 'vue', 'solid', 'web-component']; + +describe('generateDts', () => { + it('generates a URL string declaration for file mode regardless of target', () => { + for (const target of TARGETS) { + expect(generateDts(target, 'file')).toMatchInlineSnapshot(` + "declare const url: string; + export default url;" + `); + } + }); + + it('generates a dom component declaration', () => { + expect(generateDts('dom', 'component')).toMatchInlineSnapshot(` + "import type { SvgOptions } from '@svg-jar/plugin/runtime/dom'; + declare const component: (options?: SvgOptions) => SVGSVGElement; + export default component;" + `); + }); + + it('generates an ember component declaration', () => { + expect(generateDts('ember', 'component')).toMatchInlineSnapshot(` + "import type { ComponentLike } from '@glint/template'; + declare const Component: ComponentLike<{ Element: SVGSVGElement; Blocks: { default: [] } }>; + export default Component;" + `); + }); + + it('generates a react component declaration', () => { + expect(generateDts('react', 'component')).toMatchInlineSnapshot(` + "import type { FC, SVGAttributes, ReactNode } from 'react'; + declare const Component: FC & { children?: ReactNode }>; + export default Component;" + `); + }); + + it('generates a preact component declaration', () => { + expect(generateDts('preact', 'component')).toMatchInlineSnapshot(` + "import type { FunctionComponent, JSX } from 'preact'; + declare const Component: FunctionComponent>; + export default Component;" + `); + }); + + it('generates a vue component declaration', () => { + expect(generateDts('vue', 'component')).toMatchInlineSnapshot(` + "import type { Component } from 'vue'; + declare const component: Component; + export default component;" + `); + }); + + it('generates a solid component declaration', () => { + expect(generateDts('solid', 'component')).toMatchInlineSnapshot(` + "import type { Component, JSX, ParentProps } from 'solid-js'; + declare const component: Component>>; + export default component;" + `); + }); + + it('generates a web-component declaration', () => { + expect(generateDts('web-component', 'component')).toMatchInlineSnapshot(` + "declare const ElementClass: typeof HTMLElement; + export default ElementClass;" + `); + }); +}); diff --git a/plugin/test/unit/query.test.ts b/plugin/test/unit/query.test.ts index f33caf3..6fcd2ae 100644 --- a/plugin/test/unit/query.test.ts +++ b/plugin/test/unit/query.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { parseSvgId, isSvgId } from '../../src/core/query.ts'; +import { parseSvgId, isSvgId, isDtsImporter, makeSvgDtsId, parseSvgDtsId } from '../../src/core/query.ts'; describe('parseSvgId', () => { describe('import mode', () => { @@ -113,3 +113,44 @@ describe('isSvgId', () => { expect(isSvgId('/project/svg-utils.ts')).toBe(false); }); }); + +describe('isDtsImporter', () => { + it('returns true for .d.ts, .d.mts, and .d.cts importers', () => { + expect(isDtsImporter('/project/src/index.d.ts')).toBe(true); + expect(isDtsImporter('/project/src/index.d.mts')).toBe(true); + expect(isDtsImporter('/project/src/index.d.cts')).toBe(true); + }); + + it('returns false for source modules and missing importers', () => { + expect(isDtsImporter('/project/src/index.ts')).toBe(false); + expect(isDtsImporter('/project/src/index.js')).toBe(false); + expect(isDtsImporter(undefined)).toBe(false); + }); +}); + +describe('makeSvgDtsId / parseSvgDtsId', () => { + it('round-trips a component declaration module ID', () => { + const id = makeSvgDtsId('/project/icon.svg', 'component'); + + expect(id).toBe('/project/icon.svg.svg-jar.d.ts'); + expect(parseSvgDtsId(id)).toBe('component'); + }); + + it('round-trips a file declaration module ID', () => { + const id = makeSvgDtsId('/project/icon.svg', 'file'); + + expect(id).toBe('/project/icon.svg.svg-jar-file.d.ts'); + expect(parseSvgDtsId(id)).toBe('file'); + }); + + it('declaration module IDs do not look like SVG imports', () => { + expect(isSvgId(makeSvgDtsId('/project/icon.svg', 'component'))).toBe(false); + expect(isSvgId(makeSvgDtsId('/project/icon.svg', 'file'))).toBe(false); + }); + + it('returns null for regular module IDs', () => { + expect(parseSvgDtsId('/project/icon.svg')).toBeNull(); + expect(parseSvgDtsId('/project/icon.svg?file')).toBeNull(); + expect(parseSvgDtsId('/project/src/index.d.ts')).toBeNull(); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1acdaff..f55f453 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,9 @@ catalogs: execa: specifier: ^9.6.0 version: 9.6.1 + rolldown: + specifier: ^1.0.0-rc.15 + version: 1.0.0-rc.15 rollup: specifier: ^4.60.1 version: 4.60.1 @@ -75,6 +78,9 @@ importers: execa: specifier: 'catalog:' version: 9.6.1 + rolldown: + specifier: 'catalog:' + version: 1.0.0-rc.15 rollup: specifier: 'catalog:' version: 4.60.1 @@ -85,6 +91,18 @@ importers: specifier: 'catalog:' version: 4.1.4(@types/node@25.5.0)(@vitest/browser-playwright@4.1.4)(@vitest/ui@4.1.4)(jsdom@25.0.1)(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)) + test-projects/tsdown/library: + devDependencies: + '@svg-jar/plugin': + specifier: workspace:* + version: link:../../../plugin + tsdown: + specifier: 'catalog:' + version: 0.21.8(typescript@6.0.2)(vue-tsc@3.2.6(typescript@6.0.2)) + typescript: + specifier: 'catalog:' + version: 6.0.2 + test-projects/vite/ember: dependencies: '@ember/test-waiters': diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5ed4f8e..54119ca 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,6 +5,7 @@ catalog: '@types/node': ^24.12.2 '@vitest/ui': ^4.1.4 execa: ^9.6.0 + rolldown: ^1.0.0-rc.15 rollup: ^4.60.1 tsdown: ^0.21.8 typescript: ~6.0.2 diff --git a/test-projects/tsdown/library/.gitignore b/test-projects/tsdown/library/.gitignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/test-projects/tsdown/library/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/test-projects/tsdown/library/package.json b/test-projects/tsdown/library/package.json new file mode 100644 index 0000000..a93de5c --- /dev/null +++ b/test-projects/tsdown/library/package.json @@ -0,0 +1,17 @@ +{ + "name": "@svg-jar/test-tsdown-library", + "private": true, + "type": "module", + "version": "0.0.0", + "exports": { + ".": "./dist/index.js" + }, + "scripts": { + "build": "tsdown" + }, + "devDependencies": { + "@svg-jar/plugin": "workspace:*", + "tsdown": "catalog:", + "typescript": "catalog:" + } +} diff --git a/test-projects/tsdown/library/src/icons/arrow.svg b/test-projects/tsdown/library/src/icons/arrow.svg new file mode 100644 index 0000000..534ae20 --- /dev/null +++ b/test-projects/tsdown/library/src/icons/arrow.svg @@ -0,0 +1 @@ + diff --git a/test-projects/tsdown/library/src/icons/circle.svg b/test-projects/tsdown/library/src/icons/circle.svg new file mode 100644 index 0000000..d9c8483 --- /dev/null +++ b/test-projects/tsdown/library/src/icons/circle.svg @@ -0,0 +1 @@ + diff --git a/test-projects/tsdown/library/src/icons/square.svg b/test-projects/tsdown/library/src/icons/square.svg new file mode 100644 index 0000000..2be60e5 --- /dev/null +++ b/test-projects/tsdown/library/src/icons/square.svg @@ -0,0 +1 @@ + diff --git a/test-projects/tsdown/library/src/icons/star.svg b/test-projects/tsdown/library/src/icons/star.svg new file mode 100644 index 0000000..4547134 --- /dev/null +++ b/test-projects/tsdown/library/src/icons/star.svg @@ -0,0 +1,4 @@ + + + + diff --git a/test-projects/tsdown/library/src/index.ts b/test-projects/tsdown/library/src/index.ts new file mode 100644 index 0000000..b753799 --- /dev/null +++ b/test-projects/tsdown/library/src/index.ts @@ -0,0 +1,11 @@ +// Default import - sprite mode +export { default as Arrow } from './icons/arrow.svg'; + +// Named sprite +export { default as Circle } from './icons/circle.svg?sprite=shapes'; + +// Inline mode +export { default as Square } from './icons/square.svg?unsafe-inline'; + +// File mode - raw URL +export { default as starUrl } from './icons/star.svg?file'; diff --git a/test-projects/tsdown/library/src/types.d.ts b/test-projects/tsdown/library/src/types.d.ts new file mode 100644 index 0000000..b22f559 --- /dev/null +++ b/test-projects/tsdown/library/src/types.d.ts @@ -0,0 +1,6 @@ +declare module '*.svg?sprite=shapes' { + import type { SvgOptions } from '@svg-jar/plugin/runtime/dom'; + + const component: (options?: SvgOptions) => SVGSVGElement; + export default component; +} diff --git a/test-projects/tsdown/library/tsconfig.json b/test-projects/tsdown/library/tsconfig.json new file mode 100644 index 0000000..f4d90fe --- /dev/null +++ b/test-projects/tsdown/library/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noEmit": true, + "types": ["@svg-jar/plugin/client/dom"] + }, + "include": ["src"] +} diff --git a/test-projects/tsdown/library/tsdown.config.ts b/test-projects/tsdown/library/tsdown.config.ts new file mode 100644 index 0000000..96936ae --- /dev/null +++ b/test-projects/tsdown/library/tsdown.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'tsdown'; +import svgJar from '@svg-jar/plugin/rolldown'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: 'esm', + platform: 'browser', + dts: true, + plugins: [svgJar({ target: 'dom' })], +}); From 407d721eea1d40b6be6bf06f163b814d642e7de0 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli-ai-agent Date: Thu, 23 Jul 2026 10:13:59 -0400 Subject: [PATCH 2/2] Address review: base option, narrowed tsdown example, dts assertions, rolldown app project - Add a `base` plugin option (trailing slash normalized) seeding state.base outside Vite; in Vite the resolved config base is used unless the option is set explicitly. This makes the internal-package copy-step model workable under Rolldown/tsdown, where configResolved never runs. - Narrow the tsdown example to the supported scenario: renamed library -> internal-package, now a component using a sprite icon internally plus an inline re-export and one ?file export, built with base: '/vendor/icons/' and a README documenting the asset copy step and when prebaked sprites are (not) appropriate. - Add a tsdown integration test that builds the example via execa (rebuilding the plugin first so it never tests a stale dist) and asserts on dist/index.js, dist/assets, and dist/index.d.ts - the declaration file must contain the typed exports and no runtime JS or .js imports. This covers the real dts path end to end; tsdown exiting 0 was previously sufficient even with polluted declarations. - Add test-projects/rolldown/vanilla: Rolldown as the application bundler, using the Vite apps' dist/assets layout, served with vite preview and covered by a new playwright project (port 4207) with a committed linux baseline. - Extend the rolldown integration test with base-option URL assertions; unit tests for base resolution. - README: document the base option and add a scope note - sprites are an app-level optimization; public libraries should ship raw SVGs or inline components; tsdown sprite output targets internal packages where the app controls the deploy origin. Co-Authored-By: Claude Fable 5 --- CONTRIBUTING.md | 7 +- plugin/README.md | 16 +++- plugin/package.json | 2 +- plugin/playwright.config.ts | 22 +++--- plugin/src/core/options.ts | 21 +++++ plugin/src/core/state.ts | 9 ++- plugin/src/index.ts | 2 +- plugin/test/e2e/helpers.ts | 7 +- plugin/test/e2e/rolldown-vanilla.spec.ts | 74 ++++++++++++++++++ .../all-svgs-rolldown-vanilla-linux.png | Bin 0 -> 79394 bytes plugin/test/integration/rolldown.test.ts | 13 +++ plugin/test/integration/tsdown.test.ts | 73 +++++++++++++++++ plugin/test/unit/options.test.ts | 14 ++++ pnpm-lock.yaml | 17 +++- .../library => rolldown/vanilla}/.gitignore | 0 test-projects/rolldown/vanilla/index.html | 11 +++ test-projects/rolldown/vanilla/package.json | 16 ++++ .../rolldown/vanilla/rolldown.config.ts | 16 ++++ .../vanilla}/src/icons/arrow.svg | 0 .../vanilla}/src/icons/circle.svg | 0 .../vanilla}/src/icons/square.svg | 0 .../vanilla}/src/icons/star.svg | 0 test-projects/rolldown/vanilla/src/main.ts | 38 +++++++++ .../vanilla}/src/types.d.ts | 0 .../vanilla}/tsconfig.json | 0 .../tsdown/internal-package/.gitignore | 2 + .../tsdown/internal-package/README.md | 21 +++++ .../package.json | 2 +- .../internal-package/src/icons/arrow.svg | 1 + .../internal-package/src/icons/square.svg | 1 + .../internal-package/src/icons/star.svg | 4 + .../tsdown/internal-package/src/index.ts | 24 ++++++ .../tsdown/internal-package/tsconfig.json | 11 +++ .../tsdown/internal-package/tsdown.config.ts | 17 ++++ test-projects/tsdown/library/src/index.ts | 11 --- test-projects/tsdown/library/tsdown.config.ts | 10 --- 36 files changed, 419 insertions(+), 43 deletions(-) create mode 100644 plugin/test/e2e/rolldown-vanilla.spec.ts create mode 100644 plugin/test/e2e/rolldown-vanilla.spec.ts-snapshots/all-svgs-rolldown-vanilla-linux.png create mode 100644 plugin/test/integration/tsdown.test.ts rename test-projects/{tsdown/library => rolldown/vanilla}/.gitignore (100%) create mode 100644 test-projects/rolldown/vanilla/index.html create mode 100644 test-projects/rolldown/vanilla/package.json create mode 100644 test-projects/rolldown/vanilla/rolldown.config.ts rename test-projects/{tsdown/library => rolldown/vanilla}/src/icons/arrow.svg (100%) rename test-projects/{tsdown/library => rolldown/vanilla}/src/icons/circle.svg (100%) rename test-projects/{tsdown/library => rolldown/vanilla}/src/icons/square.svg (100%) rename test-projects/{tsdown/library => rolldown/vanilla}/src/icons/star.svg (100%) create mode 100644 test-projects/rolldown/vanilla/src/main.ts rename test-projects/{tsdown/library => rolldown/vanilla}/src/types.d.ts (100%) rename test-projects/{tsdown/library => rolldown/vanilla}/tsconfig.json (100%) create mode 100644 test-projects/tsdown/internal-package/.gitignore create mode 100644 test-projects/tsdown/internal-package/README.md rename test-projects/tsdown/{library => internal-package}/package.json (84%) create mode 100644 test-projects/tsdown/internal-package/src/icons/arrow.svg create mode 100644 test-projects/tsdown/internal-package/src/icons/square.svg create mode 100644 test-projects/tsdown/internal-package/src/icons/star.svg create mode 100644 test-projects/tsdown/internal-package/src/index.ts create mode 100644 test-projects/tsdown/internal-package/tsconfig.json create mode 100644 test-projects/tsdown/internal-package/tsdown.config.ts delete mode 100644 test-projects/tsdown/library/src/index.ts delete mode 100644 test-projects/tsdown/library/tsdown.config.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5963770..73af8aa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -64,8 +64,11 @@ test-projects/vite/ One Vite app per supported framework target ├── vue/ └── web-component/ +test-projects/rolldown/ Rolldown-as-app-bundler projects +└── vanilla/ DOM app, e2e tested like the Vite apps + test-projects/tsdown/ tsdown (Rolldown) projects -└── library/ Icon library with declaration output +└── internal-package/ Internal monorepo package with `base` + declaration output ``` ## Tests @@ -80,7 +83,7 @@ pnpm --filter @svg-jar/plugin test:unit ### Integration tests -Rollup and Rolldown integration tests build a minimal bundle and assert the output. +Rollup and Rolldown integration tests build a minimal bundle and assert the output. A tsdown integration test builds `test-projects/tsdown/internal-package` and asserts on its dist output, including the bundled declaration file. ```sh pnpm --filter @svg-jar/plugin test:unit # includes integration/ diff --git a/plugin/README.md b/plugin/README.md index 57b8dcf..1f068be 100644 --- a/plugin/README.md +++ b/plugin/README.md @@ -57,7 +57,7 @@ export default { ### tsdown -[tsdown](https://tsdown.dev) is built on Rolldown, so it uses the Rolldown entry point. This is useful for building component libraries that re-export SVG components — declaration files (`dts: true`) get correctly typed exports for each SVG import. +[tsdown](https://tsdown.dev) is built on Rolldown, so it uses the Rolldown entry point. Declaration files (`dts: true`) get correctly typed exports for each SVG import. ```ts // tsdown.config.ts @@ -67,10 +67,15 @@ import svgJar from '@svg-jar/plugin/rolldown'; export default defineConfig({ entry: ['src/index.ts'], dts: true, - plugins: [svgJar({ target: 'dom' })], + plugins: [svgJar({ target: 'dom', base: '/vendor/icons/' })], }); ``` +> [!IMPORTANT] +> Sprite sheets are an **application-level** optimization — their benefits (app-wide symbol dedupe, per-symbol tree-shaking, a single sheet on a known origin) only exist when one build owns the whole module graph. A package built with tsdown emits sprite and `?file` assets into its own `dist/assets` with baked URLs that a consuming bundler will not copy or rewrite. +> +> Use sprite or `?file` output from tsdown only for **internal packages where the consuming app controls the deploy origin**: set the `base` option to a fixed public path, and have the app copy the package's `dist/assets` there as part of its build (see `test-projects/tsdown/internal-package`). For publicly distributed libraries, ship raw SVG files (and let the consuming app run svg-jar itself) or export inline components (`?unsafe-inline`), which need no asset serving. + ## Usage ### Sprite mode (default) @@ -139,6 +144,13 @@ svgJar({ // external files. See "Embedded sprites" below. // true: embed all sprites | string[]: embed named sprites | false (default): external files embedded: false, + + // Base public path prepended to emitted asset URLs (sprite sheets and + // ?file assets). Defaults to Vite's resolved `base` in Vite, '/' elsewhere. + // Set this when the build output is served from a known non-root location, + // e.g. an internal package whose dist/assets the consuming app copies to a + // fixed public path. A trailing slash is added if missing. + base: '/vendor/icons/', }); ``` diff --git a/plugin/package.json b/plugin/package.json index fb0302b..fd58f8e 100644 --- a/plugin/package.json +++ b/plugin/package.json @@ -81,7 +81,7 @@ ], "scripts": { "build": "tsdown", - "build:test-projects": "tsdown && for dir in ../test-projects/vite/*/; do (cd \"$dir\" && pnpm vite build); done && for dir in ../test-projects/tsdown/*/; do (cd \"$dir\" && pnpm build); done", + "build:test-projects": "tsdown && for dir in ../test-projects/vite/*/; do (cd \"$dir\" && pnpm vite build); done && for dir in ../test-projects/rolldown/*/ ../test-projects/tsdown/*/; do (cd \"$dir\" && pnpm build); done", "dev": "tsdown --watch", "format": "prettier --write .", "lint": "eslint .", diff --git a/plugin/playwright.config.ts b/plugin/playwright.config.ts index 7a29cee..9fc2d32 100644 --- a/plugin/playwright.config.ts +++ b/plugin/playwright.config.ts @@ -4,15 +4,17 @@ import { defineConfig } from '@playwright/test'; /** * Test projects and their preview ports. * Each project is built and served via `vite preview` on a unique port. + * `dir` is the project path relative to `test-projects/`. */ const testProjects = { - vanilla: 4200, - react: 4201, - vue: 4202, - preact: 4203, - solid: 4204, - ember: 4205, - 'web-component': 4206, + vanilla: { port: 4200, dir: 'vite/vanilla' }, + react: { port: 4201, dir: 'vite/react' }, + vue: { port: 4202, dir: 'vite/vue' }, + preact: { port: 4203, dir: 'vite/preact' }, + solid: { port: 4204, dir: 'vite/solid' }, + ember: { port: 4205, dir: 'vite/ember' }, + 'web-component': { port: 4206, dir: 'vite/web-component' }, + 'rolldown-vanilla': { port: 4207, dir: 'rolldown/vanilla' }, }; export default defineConfig({ @@ -22,16 +24,16 @@ export default defineConfig({ use: { headless: true, }, - projects: Object.entries(testProjects).map(([name, port]) => ({ + projects: Object.entries(testProjects).map(([name, { port }]) => ({ name, use: { baseURL: `http://localhost:${port}`, }, testMatch: `${name}.spec.ts`, })), - webServer: Object.entries(testProjects).map(([name, port]) => ({ + webServer: Object.entries(testProjects).map(([, { port, dir }]) => ({ command: `pnpm vite preview --port ${port} --strictPort`, - cwd: path.resolve(import.meta.dirname, '..', 'test-projects', 'vite', name), + cwd: path.resolve(import.meta.dirname, '..', 'test-projects', dir), port, reuseExistingServer: !process.env.CI, })), diff --git a/plugin/src/core/options.ts b/plugin/src/core/options.ts index f16f7c8..3f62180 100644 --- a/plugin/src/core/options.ts +++ b/plugin/src/core/options.ts @@ -46,6 +46,24 @@ export interface SvgJarOptions { * svgJar({ embedded: true }) */ embedded?: boolean | string[]; + + /** + * Base public path prepended to emitted asset URLs (sprite sheets and + * `?file` assets), e.g. `'/vendor/icons/'`. + * + * Defaults to the bundler's base path in Vite (`base` from the resolved + * config), and `'/'` in other bundlers. Set this when the build output + * is served from a known non-root location - for example an internal + * package built with Rolldown/tsdown whose `dist/assets` the consuming + * app copies to a fixed public path. + * + * A trailing slash is added if missing. + * + * @example + * // Emitted URLs become /vendor/icons/assets/sprite-.svg# + * svgJar({ base: '/vendor/icons/' }) + */ + base?: string; } /** @@ -57,6 +75,8 @@ export interface ResolvedOptions { defaultSprite: string; currentColor: boolean; embedded: boolean | string[]; + /** Normalized base path (trailing slash ensured), or undefined to use the bundler default. */ + base: string | undefined; } /** @@ -72,5 +92,6 @@ export function resolveOptions(options: SvgJarOptions = {}): ResolvedOptions { defaultSprite: options.defaultSprite ?? 'sprite', currentColor: options.currentColor ?? false, embedded: options.embedded ?? false, + base: options.base == null ? undefined : options.base.endsWith('/') ? options.base : `${options.base}/`, }; } diff --git a/plugin/src/core/state.ts b/plugin/src/core/state.ts index c152ff3..3cf2e8f 100644 --- a/plugin/src/core/state.ts +++ b/plugin/src/core/state.ts @@ -23,14 +23,19 @@ export class PluginState { /** Whether the current build is a dev server (serve) vs production build. */ isDev = false; - /** Vite base path (e.g. `"/"`). Used to construct final asset URLs. */ - base = '/'; + /** + * Base public path (e.g. `"/"`). Used to construct final asset URLs. + * Seeded from the `base` plugin option; in Vite the resolved config + * `base` is used when the option is not set. + */ + base: string; /** Project root directory. Used to compute relative paths. Defaults to cwd. */ root: string = process.cwd(); constructor(options: ResolvedOptions) { this.options = options; + this.base = options.base ?? '/'; } /** diff --git a/plugin/src/index.ts b/plugin/src/index.ts index ed12f67..65fa940 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -32,7 +32,7 @@ export const SvgJarPlugin: UnpluginInstance = vite: { configResolved(config) { state.isDev = config.command === 'serve'; - state.base = config.base ?? '/'; + state.base = state.options.base ?? config.base ?? '/'; state.root = config.root ?? ''; }, renderChunk: createRenderChunkHook(state) as never, diff --git a/plugin/test/e2e/helpers.ts b/plugin/test/e2e/helpers.ts index 101b17f..aa57f0b 100644 --- a/plugin/test/e2e/helpers.ts +++ b/plugin/test/e2e/helpers.ts @@ -19,9 +19,12 @@ export function readAsset(assetsDir: string, fileName: string): string { /** * Returns the dist/assets path for a test project. + * + * @param projectName Project directory name inside the bundler directory. + * @param bundler Bundler directory inside `test-projects/`. Default `'vite'`. */ -export function assetsDir(projectName: string): string { - return path.resolve(import.meta.dirname, '..', '..', '..', 'test-projects', 'vite', projectName, 'dist', 'assets'); +export function assetsDir(projectName: string, bundler = 'vite'): string { + return path.resolve(import.meta.dirname, '..', '..', '..', 'test-projects', bundler, projectName, 'dist', 'assets'); } // -- Page rendering assertions -- diff --git a/plugin/test/e2e/rolldown-vanilla.spec.ts b/plugin/test/e2e/rolldown-vanilla.spec.ts new file mode 100644 index 0000000..9660877 --- /dev/null +++ b/plugin/test/e2e/rolldown-vanilla.spec.ts @@ -0,0 +1,74 @@ +import { test, expect } from '@playwright/test'; +import { + assetsDir, + findFile, + expectSvgsVisible, + expectSpriteRef, + expectInlineSvg, + expectDefaultSprite, + expectNamedSprite, + expectSpriteContainsArrow, + expectSpriteNoInlineSvgs, + expectNamedSpriteContainsCircle, + expectChunkContainsSpriteRefs, + expectChunkContainsInlineMarkup, +} from './helpers.ts'; + +const ASSETS = assetsDir('vanilla', 'rolldown'); + +test.describe('Rolldown app', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/'); + }); + + // -- Page rendering -- + + test('page loads', async ({ page }) => { + await expect(page).toHaveTitle(/SVG Jar/); + }); + + test('renders SVG elements', async ({ page }) => { + await expectSvgsVisible(page); + }); + + test('renders sprite icons with ', async ({ page }) => { + await expectSpriteRef(page); + }); + + test('renders inline SVGs without ', async ({ page }) => { + await expectInlineSvg(page); + }); + + test('factory options create ', async ({ page }) => { + const title = page.locator('svg title'); + await expect(title.first()).toBeAttached(); + await expect(title.first()).toHaveText('Forward arrow'); + }); + + test('renders file mode SVG as an img', async ({ page }) => { + const img = page.locator('img[alt="Star"]'); + await expect(img).toBeAttached(); + const src = await img.getAttribute('src'); + expect(src).toMatch(/\.svg/); + }); + + test('all SVGs render correctly', async ({ page }) => { + const app = page.locator('#app'); + await expect(app).toBeVisible(); + await expect(app).toHaveScreenshot('all-svgs.png', { maxDiffPixelRatio: 0.01 }); + }); + + // -- Build output -- + + test('emits default sprite', () => expectDefaultSprite(ASSETS)); + test('emits named sprite', () => expectNamedSprite(ASSETS)); + test('sprite contains arrow symbol', () => expectSpriteContainsArrow(ASSETS)); + test('sprite excludes inline SVGs', () => expectSpriteNoInlineSvgs(ASSETS)); + test('named sprite contains circle', () => expectNamedSpriteContainsCircle(ASSETS)); + test('chunk references sprites', () => expectChunkContainsSpriteRefs(ASSETS)); + test('chunk embeds inline markup', () => expectChunkContainsInlineMarkup(ASSETS)); + + test('emits file-mode SVG', () => { + expect(findFile(ASSETS, (f) => f.includes('star') && f.endsWith('.svg'))).toBeDefined(); + }); +}); diff --git a/plugin/test/e2e/rolldown-vanilla.spec.ts-snapshots/all-svgs-rolldown-vanilla-linux.png b/plugin/test/e2e/rolldown-vanilla.spec.ts-snapshots/all-svgs-rolldown-vanilla-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..0ce481ca1f8e728715c196d5fc313c55771f63cf GIT binary patch literal 79394 zcmeFac{tVE`#1hJl~SpcG;5?#D�^niQfL4K!d=qCv{IQ<?`2q^;O0Q-eC$X%P0Q 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 literal 0 HcmV?d00001 diff --git a/plugin/test/integration/rolldown.test.ts b/plugin/test/integration/rolldown.test.ts index fe20c15..9cb5b03 100644 --- a/plugin/test/integration/rolldown.test.ts +++ b/plugin/test/integration/rolldown.test.ts @@ -90,4 +90,17 @@ describe('Rolldown integration', () => { expect(findAsset(fileOutput, (f) => /^assets\/simple-[a-f0-9]+\.svg$/.test(f))).toBeDefined(); }); }); + + describe('base option', () => { + it('prefixes sprite and file asset URLs with the configured base', async () => { + const [spriteOutput, fileOutput] = await Promise.all([ + build({ target: 'dom', base: '/vendor/icons/' }), + build({ target: 'dom', base: '/vendor/icons' }, ENTRY_FILE_MODE), + ]); + + expect(findChunk(spriteOutput).code).toMatch(/\/vendor\/icons\/assets\/sprite-[a-f0-9]+\.svg#[a-f0-9]+/); + // Trailing slash is added when missing + expect(findChunk(fileOutput).code).toMatch(/\/vendor\/icons\/assets\/simple-[a-f0-9]+\.svg/); + }); + }); }); diff --git a/plugin/test/integration/tsdown.test.ts b/plugin/test/integration/tsdown.test.ts new file mode 100644 index 0000000..eb12096 --- /dev/null +++ b/plugin/test/integration/tsdown.test.ts @@ -0,0 +1,73 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { execa } from 'execa'; +import { describe, it, expect, beforeAll } from 'vitest'; + +const PLUGIN_DIR = path.resolve(import.meta.dirname, '..', '..'); +const PROJECT_DIR = path.resolve(PLUGIN_DIR, '..', 'test-projects', 'tsdown', 'internal-package'); +const DIST = path.join(PROJECT_DIR, 'dist'); + +function readDist(fileName: string): string { + return fs.readFileSync(path.join(DIST, fileName), 'utf-8'); +} + +/** + * Builds the tsdown example project against the freshly built plugin and + * asserts on the actual dist output. This exercises the real declaration + * path (dts importer detection -> virtual `.d.ts` module -> declaration + * codegen -> rolldown-plugin-dts bundling), which unit tests cannot: the + * detection is coupled to rolldown-plugin-dts internals, and tsdown + * exiting 0 alone proved nothing (it also exited 0 while emitting a + * declaration file full of runtime JS). + */ +describe('tsdown integration (internal-package example)', () => { + beforeAll(async () => { + // Build the plugin first: the example resolves @svg-jar/plugin via the + // workspace, whose exports point at dist/. Skipping this would silently + // test a stale build. + await execa('pnpm', ['build'], { cwd: PLUGIN_DIR }); + await execa('pnpm', ['build'], { cwd: PROJECT_DIR }); + }, 120_000); + + describe('JS output', () => { + it('replaces sprite placeholders with base-prefixed URLs', () => { + const code = readDist('index.js'); + + expect(code).not.toContain('__SVG_JAR_SPRITE__'); + expect(code).toMatch(/\/vendor\/icons\/assets\/sprite-[a-f0-9]+\.svg#[a-f0-9]+/); + }); + + it('exports a base-prefixed URL for the ?file import', () => { + expect(readDist('index.js')).toMatch(/\/vendor\/icons\/assets\/star-[a-f0-9]+\.svg/); + }); + + it('emits the sprite and file assets', () => { + const assets = fs.readdirSync(path.join(DIST, 'assets')); + + expect(assets.find((f) => /^sprite-[a-f0-9]+\.svg$/.test(f))).toBeDefined(); + expect(assets.find((f) => /^star-[a-f0-9]+\.svg$/.test(f))).toBeDefined(); + }); + }); + + describe('declaration output', () => { + it('declares the component and URL exports', () => { + const dts = readDist('index.d.ts'); + + expect(dts).toContain('declare const component: (options?: SvgOptions) => SVGSVGElement;'); + expect(dts).toContain('declare const url: string;'); + expect(dts).toContain('declare function createNextButton(label: string): HTMLButtonElement;'); + expect(dts).toMatch(/export \{.*component as Square.*\}/); + }); + + it('contains no runtime code or JS imports', () => { + const dts = readDist('index.d.ts'); + + // createSvg may appear inside inlined doc comments; assert it is + // never imported or called in actual declaration code. + expect(dts).not.toMatch(/import\s*\{[^}]*createSvg/); + expect(dts).not.toMatch(/^\s*(?:declare\s+)?(?:const|var|let)[^=\n]*=\s*.*createSvg/m); + expect(dts).not.toMatch(/from\s+["'][^"']*\.js["']/); + expect(dts).not.toContain('__SVG_JAR_SPRITE__'); + }); + }); +}); diff --git a/plugin/test/unit/options.test.ts b/plugin/test/unit/options.test.ts index dc74f1b..6b8d098 100644 --- a/plugin/test/unit/options.test.ts +++ b/plugin/test/unit/options.test.ts @@ -31,4 +31,18 @@ describe('resolveOptions', () => { expect(resolved.svgo).toBe(svgoConfig); }); + + describe('base', () => { + it('defaults to undefined (bundler decides)', () => { + expect(resolveOptions({}).base).toBeUndefined(); + }); + + it('preserves a base with a trailing slash', () => { + expect(resolveOptions({ base: '/vendor/icons/' }).base).toBe('/vendor/icons/'); + }); + + it('adds a missing trailing slash', () => { + expect(resolveOptions({ base: '/vendor/icons' }).base).toBe('/vendor/icons/'); + }); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f55f453..40c198f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,7 +91,22 @@ importers: specifier: 'catalog:' version: 4.1.4(@types/node@25.5.0)(@vitest/browser-playwright@4.1.4)(@vitest/ui@4.1.4)(jsdom@25.0.1)(vite@8.0.8(@types/node@25.5.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0)) - test-projects/tsdown/library: + test-projects/rolldown/vanilla: + devDependencies: + '@svg-jar/plugin': + specifier: workspace:* + version: link:../../../plugin + rolldown: + specifier: 'catalog:' + version: 1.0.0-rc.15 + typescript: + specifier: 'catalog:' + version: 6.0.2 + vite: + specifier: 'catalog:' + version: 8.0.1(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)(@types/node@25.5.0)(esbuild@0.27.7)(jiti@2.6.1)(terser@5.46.1)(tsx@4.21.0) + + test-projects/tsdown/internal-package: devDependencies: '@svg-jar/plugin': specifier: workspace:* diff --git a/test-projects/tsdown/library/.gitignore b/test-projects/rolldown/vanilla/.gitignore similarity index 100% rename from test-projects/tsdown/library/.gitignore rename to test-projects/rolldown/vanilla/.gitignore diff --git a/test-projects/rolldown/vanilla/index.html b/test-projects/rolldown/vanilla/index.html new file mode 100644 index 0000000..a6a99fe --- /dev/null +++ b/test-projects/rolldown/vanilla/index.html @@ -0,0 +1,11 @@ +<!doctype html> +<html> + <head> + <meta charset="utf-8" /> + <title>SVG Jar Rolldown Test + + +
+ + + diff --git a/test-projects/rolldown/vanilla/package.json b/test-projects/rolldown/vanilla/package.json new file mode 100644 index 0000000..cc6df07 --- /dev/null +++ b/test-projects/rolldown/vanilla/package.json @@ -0,0 +1,16 @@ +{ + "name": "@svg-jar/test-rolldown-vanilla", + "private": true, + "type": "module", + "version": "0.0.0", + "scripts": { + "build": "rolldown -c && cp index.html dist/", + "preview": "vite preview" + }, + "devDependencies": { + "@svg-jar/plugin": "workspace:*", + "rolldown": "catalog:", + "typescript": "catalog:", + "vite": "catalog:" + } +} diff --git a/test-projects/rolldown/vanilla/rolldown.config.ts b/test-projects/rolldown/vanilla/rolldown.config.ts new file mode 100644 index 0000000..85b4cd8 --- /dev/null +++ b/test-projects/rolldown/vanilla/rolldown.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'rolldown'; +import svgJar from '@svg-jar/plugin/rolldown'; + +// Rolldown as the application bundler: one build owns the whole module +// graph, so sprite assembly works exactly like the Vite apps. +export default defineConfig({ + input: 'src/main.ts', + plugins: [svgJar({ target: 'dom' })], + output: { + dir: 'dist', + // Match the Vite apps' layout: JS chunks live in dist/assets/ alongside + // the emitted sprite and file assets. + entryFileNames: 'assets/[name].js', + chunkFileNames: 'assets/[name]-[hash].js', + }, +}); diff --git a/test-projects/tsdown/library/src/icons/arrow.svg b/test-projects/rolldown/vanilla/src/icons/arrow.svg similarity index 100% rename from test-projects/tsdown/library/src/icons/arrow.svg rename to test-projects/rolldown/vanilla/src/icons/arrow.svg diff --git a/test-projects/tsdown/library/src/icons/circle.svg b/test-projects/rolldown/vanilla/src/icons/circle.svg similarity index 100% rename from test-projects/tsdown/library/src/icons/circle.svg rename to test-projects/rolldown/vanilla/src/icons/circle.svg diff --git a/test-projects/tsdown/library/src/icons/square.svg b/test-projects/rolldown/vanilla/src/icons/square.svg similarity index 100% rename from test-projects/tsdown/library/src/icons/square.svg rename to test-projects/rolldown/vanilla/src/icons/square.svg diff --git a/test-projects/tsdown/library/src/icons/star.svg b/test-projects/rolldown/vanilla/src/icons/star.svg similarity index 100% rename from test-projects/tsdown/library/src/icons/star.svg rename to test-projects/rolldown/vanilla/src/icons/star.svg diff --git a/test-projects/rolldown/vanilla/src/main.ts b/test-projects/rolldown/vanilla/src/main.ts new file mode 100644 index 0000000..00c8827 --- /dev/null +++ b/test-projects/rolldown/vanilla/src/main.ts @@ -0,0 +1,38 @@ +// Default import - sprite mode +import Arrow from './icons/arrow.svg'; + +// Named sprite +import Circle from './icons/circle.svg?sprite=shapes'; + +// Inline mode +import Square from './icons/square.svg?unsafe-inline'; + +// File mode - raw URL +import starUrl from './icons/star.svg?file'; + +const app = document.getElementById('app')!; + +// Sprite mode - call the factory to create a DOM node +app.appendChild(Arrow()); + +// Sprite mode with options - attributes, title, desc +app.appendChild( + Arrow({ + class: 'icon', + 'aria-label': 'Navigate forward', + title: 'Forward arrow', + desc: 'An arrow pointing to the right', + }), +); + +// Named sprite +app.appendChild(Circle()); + +// Inline mode +app.appendChild(Square()); + +// File mode - create an img element with the URL +const img = document.createElement('img'); +img.src = starUrl; +img.alt = 'Star'; +app.appendChild(img); diff --git a/test-projects/tsdown/library/src/types.d.ts b/test-projects/rolldown/vanilla/src/types.d.ts similarity index 100% rename from test-projects/tsdown/library/src/types.d.ts rename to test-projects/rolldown/vanilla/src/types.d.ts diff --git a/test-projects/tsdown/library/tsconfig.json b/test-projects/rolldown/vanilla/tsconfig.json similarity index 100% rename from test-projects/tsdown/library/tsconfig.json rename to test-projects/rolldown/vanilla/tsconfig.json diff --git a/test-projects/tsdown/internal-package/.gitignore b/test-projects/tsdown/internal-package/.gitignore new file mode 100644 index 0000000..1eae0cf --- /dev/null +++ b/test-projects/tsdown/internal-package/.gitignore @@ -0,0 +1,2 @@ +dist/ +node_modules/ diff --git a/test-projects/tsdown/internal-package/README.md b/test-projects/tsdown/internal-package/README.md new file mode 100644 index 0000000..82a7e31 --- /dev/null +++ b/test-projects/tsdown/internal-package/README.md @@ -0,0 +1,21 @@ +# tsdown internal package example + +Models an **internal monorepo package** built with [tsdown](https://tsdown.dev), consumed by an application the same team owns. + +Sprite sheets are an application-level optimization: their benefits (app-wide symbol dedupe, per-symbol tree-shaking, a single sheet on a known origin) only exist when one build owns the whole module graph. A package built with tsdown emits its sprite and `?file` assets to its own `dist/assets/` with baked URLs, which a consuming bundler will not copy or rewrite. That is only viable when you **control the deploy origin** — i.e. an internal package whose consuming app takes on serving the assets: + +1. The plugin is configured with a fixed base path (see `tsdown.config.ts`): + + ```ts + svgJar({ target: 'dom', base: '/vendor/icons/' }); + ``` + +2. The consuming app copies this package's assets to that path as part of its build, e.g.: + + ```sh + cp -r node_modules/@your-org/icons/dist/assets public/vendor/icons/assets + ``` + +For a **publicly distributed library**, don't prebake sprites: ship raw SVG files (and let the consuming app run svg-jar itself), or export inline components (`?unsafe-inline`), which need no asset serving. + +Declaration output (`dts: true`) works for any of these shapes — SVG re-exports get correctly typed declarations per target. diff --git a/test-projects/tsdown/library/package.json b/test-projects/tsdown/internal-package/package.json similarity index 84% rename from test-projects/tsdown/library/package.json rename to test-projects/tsdown/internal-package/package.json index a93de5c..f5e95de 100644 --- a/test-projects/tsdown/library/package.json +++ b/test-projects/tsdown/internal-package/package.json @@ -1,5 +1,5 @@ { - "name": "@svg-jar/test-tsdown-library", + "name": "@svg-jar/test-tsdown-internal-package", "private": true, "type": "module", "version": "0.0.0", diff --git a/test-projects/tsdown/internal-package/src/icons/arrow.svg b/test-projects/tsdown/internal-package/src/icons/arrow.svg new file mode 100644 index 0000000..534ae20 --- /dev/null +++ b/test-projects/tsdown/internal-package/src/icons/arrow.svg @@ -0,0 +1 @@ + diff --git a/test-projects/tsdown/internal-package/src/icons/square.svg b/test-projects/tsdown/internal-package/src/icons/square.svg new file mode 100644 index 0000000..2be60e5 --- /dev/null +++ b/test-projects/tsdown/internal-package/src/icons/square.svg @@ -0,0 +1 @@ + diff --git a/test-projects/tsdown/internal-package/src/icons/star.svg b/test-projects/tsdown/internal-package/src/icons/star.svg new file mode 100644 index 0000000..4547134 --- /dev/null +++ b/test-projects/tsdown/internal-package/src/icons/star.svg @@ -0,0 +1,4 @@ + + + + diff --git a/test-projects/tsdown/internal-package/src/index.ts b/test-projects/tsdown/internal-package/src/index.ts new file mode 100644 index 0000000..ed3f63f --- /dev/null +++ b/test-projects/tsdown/internal-package/src/index.ts @@ -0,0 +1,24 @@ +// An internal package in a monorepo, built with tsdown and consumed by an +// app the same team owns. The app copies dist/assets/ to a fixed public +// path matching the `base` plugin option (see tsdown.config.ts and README). + +import Arrow from './icons/arrow.svg'; + +/** + * A button that renders a sprite icon internally. The sprite sheet is + * emitted to dist/assets/ and served by the consuming app from the + * configured base path. + */ +export function createNextButton(label: string): HTMLButtonElement { + const button = document.createElement('button'); + button.type = 'button'; + button.append(label, Arrow({ 'aria-hidden': 'true' })); + return button; +} + +// Inline components are safe to re-export from any package - the markup is +// embedded in the JS, no asset serving required. +export { default as Square } from './icons/square.svg?unsafe-inline'; + +// File mode works here because the app serves dist/assets/ from the base path. +export { default as starUrl } from './icons/star.svg?file'; diff --git a/test-projects/tsdown/internal-package/tsconfig.json b/test-projects/tsdown/internal-package/tsconfig.json new file mode 100644 index 0000000..f4d90fe --- /dev/null +++ b/test-projects/tsdown/internal-package/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noEmit": true, + "types": ["@svg-jar/plugin/client/dom"] + }, + "include": ["src"] +} diff --git a/test-projects/tsdown/internal-package/tsdown.config.ts b/test-projects/tsdown/internal-package/tsdown.config.ts new file mode 100644 index 0000000..adccd7f --- /dev/null +++ b/test-projects/tsdown/internal-package/tsdown.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'tsdown'; +import svgJar from '@svg-jar/plugin/rolldown'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: 'esm', + platform: 'browser', + dts: true, + plugins: [ + svgJar({ + target: 'dom', + // The consuming app copies this package's dist/assets/ directory to + // public/vendor/icons/assets/, so emitted asset URLs resolve at runtime. + base: '/vendor/icons/', + }), + ], +}); diff --git a/test-projects/tsdown/library/src/index.ts b/test-projects/tsdown/library/src/index.ts deleted file mode 100644 index b753799..0000000 --- a/test-projects/tsdown/library/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Default import - sprite mode -export { default as Arrow } from './icons/arrow.svg'; - -// Named sprite -export { default as Circle } from './icons/circle.svg?sprite=shapes'; - -// Inline mode -export { default as Square } from './icons/square.svg?unsafe-inline'; - -// File mode - raw URL -export { default as starUrl } from './icons/star.svg?file'; diff --git a/test-projects/tsdown/library/tsdown.config.ts b/test-projects/tsdown/library/tsdown.config.ts deleted file mode 100644 index 96936ae..0000000 --- a/test-projects/tsdown/library/tsdown.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from 'tsdown'; -import svgJar from '@svg-jar/plugin/rolldown'; - -export default defineConfig({ - entry: ['src/index.ts'], - format: 'esm', - platform: 'browser', - dts: true, - plugins: [svgJar({ target: 'dom' })], -});