Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/

import {describe, expect, it} from 'vitest';
import {mkdir, mkdtempDisposable, realpath, writeFile} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import path from 'node:path';
import {transform} from '@svgr/core';
import svgo from '@svgr/plugin-svgo';
Expand All @@ -15,6 +17,8 @@ import type {SVGRConfig} from '../options';

const siteDir = path.join(__dirname, 'site');

const typescriptSvgrConfig = 'module.exports = {typescript: true};\n';

function gradientSvg(color: string) {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<title>Gradient</title>
Expand Down Expand Up @@ -53,6 +57,35 @@ function getIds(code: string): string[] {
);
}

// SVGR searches from the SVG file upward, so each case needs its own directory.
// Cosmiconfig caches a search path for the process lifetime.
async function withSvgrConfigFile(
configSource: string,
location: 'parent' | 'site',
): Promise<{siteDirectory: string; filePath: string} & AsyncDisposable> {
const parent = await mkdtempDisposable(
path.join(await realpath(tmpdir()), 'docusaurus-svgr-'),
);
const siteDirectory = path.join(parent.path, 'site');
const filePath = 'src/icon.svg';
await mkdir(path.join(siteDirectory, 'src'), {recursive: true});
await writeFile(path.join(siteDirectory, filePath), gradientSvg('red'));
const configDirectory = location === 'parent' ? parent.path : siteDirectory;
await writeFile(path.join(configDirectory, 'svgr.config.js'), configSource);
return {
siteDirectory,
filePath,
[Symbol.asyncDispose]: parent[Symbol.asyncDispose],
};
}

function expectPlainReactSvg(code: string) {
expect(code).toContain('export default');
expect(code).toContain('viewBox="0 0 10 10"');
expect(code).not.toContain('interface SVGRProps');
expect(code).not.toContain('import type { SVGProps }');
}

describe('createSVGRConfig', () => {
it('prefixes SVG ids to avoid clashes between inline SVGs', async () => {
const codeA = await transformSvg({
Expand Down Expand Up @@ -122,4 +155,49 @@ describe('createSVGRConfig', () => {
expect(getIds(code)).toEqual(['a']);
expect(code).toContain('fill="url(#a)"');
});

it('ignores a parent svgr config that enables TypeScript output', async () => {
await using site = await withSvgrConfigFile(typescriptSvgrConfig, 'parent');
const code = await transformSvg({
svg: gradientSvg('red'),
filePath: site.filePath,
siteDirectory: site.siteDirectory,
});
expectPlainReactSvg(code);
});

it('loads a parent svgr config when runtimeConfig is enabled', async () => {
await using site = await withSvgrConfigFile(typescriptSvgrConfig, 'parent');
const code = await transformSvg({
svg: gradientSvg('red'),
filePath: site.filePath,
siteDirectory: site.siteDirectory,
svgrConfig: {runtimeConfig: true},
});
expect(code).toContain('interface SVGRProps');
expect(code).toContain('import type { SVGProps }');
});

it('ignores an svgr config in the site directory', async () => {
await using site = await withSvgrConfigFile(typescriptSvgrConfig, 'site');
const code = await transformSvg({
svg: gradientSvg('red'),
filePath: site.filePath,
siteDirectory: site.siteDirectory,
});
expectPlainReactSvg(code);
});

it('ignores a malformed svgr config discovered from a parent directory', async () => {
await using site = await withSvgrConfigFile(
'module.exports = {\n',
'parent',
);
const code = await transformSvg({
svg: gradientSvg('red'),
filePath: site.filePath,
siteDirectory: site.siteDirectory,
});
expectPlainReactSvg(code);
});
});
3 changes: 3 additions & 0 deletions packages/docusaurus-plugin-svgr/src/svgrLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ function createDefaultSVGRConfig({siteDir}: {siteDir: string}): SVGRConfig {
svgo: true,
svgoConfig: createDefaultSVGOConfig({siteDir}),
titleProp: true,
// Skip svgr.config.js discovered in the site directory or an ancestor.
// See https://github.com/meta/docusaurus/issues/8646
runtimeConfig: false,
};
}

Expand Down
3 changes: 3 additions & 0 deletions website/docs/api/plugins/plugin-svgr.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ const defaultSvgrConfig = {
prettier: false,
svgo: true,
titleProp: true,
runtimeConfig: false,
svgoConfig: {
plugins: [
{
Expand Down Expand Up @@ -88,4 +89,6 @@ const defaultSvgrConfig = {

The `prefixIds` plugin prefixes SVG ids (for example, `id="a"` becomes `id="svg-3f2a1c-a"`) to avoid clashes between multiple SVGs inlined in the same page. Class names are not prefixed.

`runtimeConfig: false` disables discovery of [SVGR configuration files](https://react-svgr.com/docs/configuration-files/) (for example `svgr.config.js`) in the site directory and ancestor directories. Configure SVGR through the `svgrConfig` option.

Providing `svgoConfig` replaces the default [SVGO](https://svgo.dev/) config entirely. Use `svgoConfig: undefined` to read your own [`svgo.config.js` file](https://svgo.dev/docs/introduction/#configuration) instead.