Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 15 additions & 5 deletions plugin/src/hooks/transform.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import path from 'node:path';
import type { UnpluginBuildContext, UnpluginContext } from 'unplugin';
import type { TransformResult, UnpluginBuildContext, UnpluginContext } from 'unplugin';
import type { PluginState } from '../core/state.ts';
import { isSvgId } from '../core/query.ts';
import { generateCode } from '../codegen/index.ts';
import { hashSvg } from '../svg/parse.ts';
import { createSymbol, serializeSvg } from '../svg/serialize.ts';

/**
* The transform replaces each SVG module's code wholesale, so there is no
* meaningful mapping back to the source. Return an explicitly empty sourcemap
* (`{ mappings: '' }`) per the Rollup convention so bundlers building with
* sourcemaps enabled don't warn SOURCEMAP_BROKEN for every imported SVG.
*/
function withEmptyMap(code: string): TransformResult {
return { code, map: { mappings: '' } };
}

/**
* Creates the `transform` hook function.
*
Expand All @@ -19,7 +29,7 @@ import { createSymbol, serializeSvg } from '../svg/serialize.ts';
*/
export function createTransformHook(
state: PluginState,
): (this: UnpluginBuildContext & UnpluginContext, code: string, id: string) => string | null {
): (this: UnpluginBuildContext & UnpluginContext, code: string, id: string) => TransformResult {
return function transform(code, id) {
if (!isSvgId(id)) return null;

Expand All @@ -40,7 +50,7 @@ export function createTransformHook(
// Dev mode: export the file path relative to root.
// Vite's dev server serves files directly from the filesystem.
const relativePath = state.root ? path.relative(state.root, filePath) : filePath;
return `${preamble}export default ${JSON.stringify(`${state.base}${relativePath}`)};`;
return withEmptyMap(`${preamble}export default ${JSON.stringify(`${state.base}${relativePath}`)};`);
}

// Build mode: generate a hashed filename and store for generateBundle to emit.
Expand All @@ -50,7 +60,7 @@ export function createTransformHook(

state.fileAssets.set(id, { fileName, source: svgModule.svgSource });

return `${preamble}export default ${JSON.stringify(`${state.base}${fileName}`)};`;
return withEmptyMap(`${preamble}export default ${JSON.stringify(`${state.base}${fileName}`)};`);
}

// Build symbol markup for dev sprite mode.
Expand Down Expand Up @@ -95,6 +105,6 @@ export function createTransformHook(
refSymbols,
});

return `${preamble}${generated}`;
return withEmptyMap(`${preamble}${generated}`);
};
}
12 changes: 11 additions & 1 deletion plugin/test/integration/tsdown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,24 @@ function readDist(fileName: string): string {
* declaration file full of runtime JS).
*/
describe('tsdown integration (internal-package example)', () => {
let buildOutput: string;

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 });
const result = await execa('pnpm', ['build'], { cwd: PROJECT_DIR, all: true });
buildOutput = result.all ?? '';
}, 120_000);

it('emits no sourcemap warnings', () => {
// The example builds with `sourcemap: true`; a transform hook that
// returns a string without a map makes rolldown warn SOURCEMAP_BROKEN
// once per imported SVG.
expect(buildOutput).not.toContain('SOURCEMAP_BROKEN');
});

describe('JS output', () => {
it('replaces sprite placeholders with base-prefixed URLs', () => {
const code = readDist('index.js');
Expand Down
Loading
Loading