diff --git a/packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.ts.snap b/packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.ts.snap
index 6ab44453..10ab6f63 100644
--- a/packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.ts.snap
+++ b/packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.ts.snap
@@ -1,5 +1,46 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`plugin javascript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "namespace" 1`] = `
+"import * as Preact from \\"preact\\";
+
+const SvgComponent = () => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin javascript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "specifiers" 1`] = `
+"import { h } from \\"preact\\";
+
+const SvgComponent = () => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin javascript #jsxRuntime supports "automatic" jsxRuntime 1`] = `
+"const SvgComponent = () => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin javascript #jsxRuntime supports "classic" jsxRuntime 1`] = `
+"import * as React from \\"react\\";
+
+const SvgComponent = () => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin javascript allows to specify a different import source 1`] = `
+"import { h } from \\"preact\\";
+import { forwardRef, memo } from \\"preact/compat\\";
+
+const SvgComponent = (_, ref) => ;
+
+const ForwardRef = forwardRef(SvgComponent);
+const Memo = memo(ForwardRef);
+export default Memo;"
+`;
+
exports[`plugin javascript custom templates support basic template 1`] = `
"import * as React from 'react';
@@ -162,6 +203,47 @@ const Memo = memo(ForwardRef);
export default Memo;"
`;
+exports[`plugin typescript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "namespace" 1`] = `
+"import * as Preact from \\"preact\\";
+
+const SvgComponent = () => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin typescript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "specifiers" 1`] = `
+"import { h } from \\"preact\\";
+
+const SvgComponent = () => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin typescript #jsxRuntime supports "automatic" jsxRuntime 1`] = `
+"const SvgComponent = () => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin typescript #jsxRuntime supports "classic" jsxRuntime 1`] = `
+"import * as React from \\"react\\";
+
+const SvgComponent = () => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin typescript allows to specify a different import source 1`] = `
+"import { h } from \\"preact\\";
+import { Ref, forwardRef, memo } from \\"preact/compat\\";
+
+const SvgComponent = (_, ref: Ref) => ;
+
+const ForwardRef = forwardRef(SvgComponent);
+const Memo = memo(ForwardRef);
+export default Memo;"
+`;
+
exports[`plugin typescript custom templates support basic template 1`] = `
"import * as React from 'react';
diff --git a/packages/babel-plugin-transform-svg-component/src/index.test.ts b/packages/babel-plugin-transform-svg-component/src/index.test.ts
index 0d0ff38b..35aa2628 100644
--- a/packages/babel-plugin-transform-svg-component/src/index.test.ts
+++ b/packages/babel-plugin-transform-svg-component/src/index.test.ts
@@ -228,5 +228,58 @@ describe('plugin', () => {
expect(code).toMatchSnapshot()
})
})
+
+ describe('#jsxRuntime', () => {
+ it('supports "automatic" jsxRuntime', () => {
+ const { code } = testPlugin(language)('', {
+ jsxRuntime: 'automatic',
+ })
+ expect(code).toMatchSnapshot()
+ })
+
+ it('supports "classic" jsxRuntime', () => {
+ const { code } = testPlugin(language)('', {
+ jsxRuntime: 'classic',
+ })
+ expect(code).toMatchSnapshot()
+ })
+
+ it('allows to specify a custom "classic" jsxRuntime using "specifiers"', () => {
+ const { code } = testPlugin(language)('', {
+ jsxRuntime: 'classic',
+ jsxRuntimeImport: { specifiers: ['h'], source: 'preact' },
+ })
+ expect(code).toMatchSnapshot()
+ })
+
+ it('allows to specify a custom "classic" jsxRuntime using "namespace"', () => {
+ const { code } = testPlugin(language)('', {
+ jsxRuntime: 'classic',
+ jsxRuntimeImport: { namespace: 'Preact', source: 'preact' },
+ })
+ expect(code).toMatchSnapshot()
+ })
+
+ it('throws with invalid configuration', () => {
+ expect(() => {
+ testPlugin(language)('', {
+ jsxRuntime: 'classic',
+ jsxRuntimeImport: { source: 'preact' },
+ })
+ }).toThrow(
+ 'Specify either "namespace" or "specifiers" in "jsxRuntimeImport" option',
+ )
+ })
+ })
+
+ it('allows to specify a different import source', () => {
+ const { code } = testPlugin(language)('', {
+ memo: true,
+ ref: true,
+ importSource: 'preact/compat',
+ jsxRuntimeImport: { specifiers: ['h'], source: 'preact' },
+ })
+ expect(code).toMatchSnapshot()
+ })
})
})
diff --git a/packages/babel-plugin-transform-svg-component/src/types.ts b/packages/babel-plugin-transform-svg-component/src/types.ts
index fe4a9889..f1af5586 100644
--- a/packages/babel-plugin-transform-svg-component/src/types.ts
+++ b/packages/babel-plugin-transform-svg-component/src/types.ts
@@ -26,6 +26,12 @@ interface State {
caller?: { previousExport?: string | null }
}
+export interface JSXRuntimeImport {
+ source: string
+ namespace?: string
+ specifiers?: string[]
+}
+
export interface Options {
typescript?: boolean
titleProp?: boolean
@@ -36,5 +42,8 @@ export interface Options {
native?: boolean
memo?: boolean
exportType?: 'named' | 'default'
- namedExport: string
+ namedExport?: string
+ jsxRuntime?: 'automatic' | 'classic'
+ jsxRuntimeImport?: JSXRuntimeImport
+ importSource?: string
}
diff --git a/packages/babel-plugin-transform-svg-component/src/variables.ts b/packages/babel-plugin-transform-svg-component/src/variables.ts
index 198fb2df..6eb386c1 100644
--- a/packages/babel-plugin-transform-svg-component/src/variables.ts
+++ b/packages/babel-plugin-transform-svg-component/src/variables.ts
@@ -1,5 +1,5 @@
import { types as t } from '@babel/core'
-import type { Options, TemplateVariables } from './types'
+import type { Options, TemplateVariables, JSXRuntimeImport } from './types'
const tsOptionalPropertySignature = (
...args: Parameters
@@ -15,6 +15,7 @@ interface Context {
interfaces: t.TSInterfaceDeclaration[]
props: (t.Identifier | t.ObjectPattern)[]
imports: t.ImportDeclaration[]
+ importSource: string
}
const getOrCreateImport = ({ imports }: Context, sourceValue: string) => {
@@ -40,7 +41,7 @@ const tsTypeReferenceSVGProps = (ctx: Context) => {
return t.tsTypeReference(identifier)
}
const identifier = t.identifier('SVGProps')
- getOrCreateImport(ctx, 'react').specifiers.push(
+ getOrCreateImport(ctx, ctx.importSource).specifiers.push(
t.importSpecifier(identifier, identifier),
)
return t.tsTypeReference(
@@ -53,7 +54,7 @@ const tsTypeReferenceSVGProps = (ctx: Context) => {
const tsTypeReferenceSVGRef = (ctx: Context) => {
const identifier = t.identifier('Ref')
- getOrCreateImport(ctx, 'react').specifiers.push(
+ getOrCreateImport(ctx, ctx.importSource).specifiers.push(
t.importSpecifier(identifier, identifier),
)
return t.tsTypeReference(
@@ -64,6 +65,29 @@ const tsTypeReferenceSVGRef = (ctx: Context) => {
)
}
+const getJsxRuntimeImport = (cfg: JSXRuntimeImport) => {
+ const specifiers = (() => {
+ if (cfg.namespace)
+ return [t.importNamespaceSpecifier(t.identifier(cfg.namespace))]
+ if (cfg.specifiers)
+ return cfg.specifiers.map((specifier) => {
+ const identifier = t.identifier(specifier)
+ return t.importSpecifier(identifier, identifier)
+ })
+ throw new Error(
+ `Specify either "namespace" or "specifiers" in "jsxRuntimeImport" option`,
+ )
+ })()
+ return t.importDeclaration(specifiers, t.stringLiteral(cfg.source))
+}
+
+const defaultJsxRuntimeImport: JSXRuntimeImport = {
+ source: 'react',
+ namespace: 'React',
+}
+
+const defaultImportSource = 'react'
+
export const getVariables = ({
opts,
jsx,
@@ -77,6 +101,7 @@ export const getVariables = ({
const imports: t.ImportDeclaration[] = []
const exports: (t.VariableDeclaration | t.ExportDeclaration)[] = []
const ctx = {
+ importSource: opts.importSource ?? defaultImportSource,
exportIdentifier: componentName,
opts,
interfaces,
@@ -85,12 +110,11 @@ export const getVariables = ({
exports,
}
- imports.push(
- t.importDeclaration(
- [t.importNamespaceSpecifier(t.identifier('React'))],
- t.stringLiteral('react'),
- ),
- )
+ if (opts.jsxRuntime !== 'automatic') {
+ imports.push(
+ getJsxRuntimeImport(opts.jsxRuntimeImport ?? defaultJsxRuntimeImport),
+ )
+ }
if (opts.native) {
getOrCreateImport(ctx, 'react-native-svg').specifiers.push(
@@ -171,7 +195,7 @@ export const getVariables = ({
}
const forwardRef = t.identifier('forwardRef')
const ForwardRef = t.identifier('ForwardRef')
- getOrCreateImport(ctx, 'react').specifiers.push(
+ getOrCreateImport(ctx, ctx.importSource).specifiers.push(
t.importSpecifier(forwardRef, forwardRef),
)
exports.push(
@@ -188,7 +212,7 @@ export const getVariables = ({
if (opts.memo) {
const memo = t.identifier('memo')
const Memo = t.identifier('Memo')
- getOrCreateImport(ctx, 'react').specifiers.push(
+ getOrCreateImport(ctx, ctx.importSource).specifiers.push(
t.importSpecifier(memo, memo),
)
exports.push(
@@ -203,6 +227,9 @@ export const getVariables = ({
}
if (opts.state.caller?.previousExport || opts.exportType === 'named') {
+ if (!opts.namedExport) {
+ throw new Error(`"namedExport" not specified`)
+ }
exports.push(
t.exportNamedDeclaration(null, [
t.exportSpecifier(ctx.exportIdentifier, t.identifier(opts.namedExport)),
diff --git a/packages/cli/src/__snapshots__/index.test.ts.snap b/packages/cli/src/__snapshots__/index.test.ts.snap
index dad8c0d9..a11c02f9 100644
--- a/packages/cli/src/__snapshots__/index.test.ts.snap
+++ b/packages/cli/src/__snapshots__/index.test.ts.snap
@@ -196,6 +196,32 @@ export default SvgFile
"
`;
+exports[`cli should support various args: --jsx-runtime automatic 1`] = `
+"const SvgFile = (props) => (
+
+)
+
+export default SvgFile
+
+"
+`;
+
+exports[`cli should support various args: --jsx-runtime classic-preact 1`] = `
+"import { h } from 'preact'
+
+const SvgFile = (props) => (
+
+)
+
+export default SvgFile
+
+"
+`;
+
exports[`cli should support various args: --native --expand-props none 1`] = `
"import * as React from 'react'
import Svg, { Path } from 'react-native-svg'
diff --git a/packages/cli/src/index.test.ts b/packages/cli/src/index.test.ts
index 676a20f5..4017bf3e 100644
--- a/packages/cli/src/index.test.ts
+++ b/packages/cli/src/index.test.ts
@@ -114,6 +114,8 @@ describe('cli', () => {
it.each([
['--no-dimensions'],
+ ['--jsx-runtime classic-preact'],
+ ['--jsx-runtime automatic'],
['--expand-props none'],
['--expand-props start'],
['--icon'],
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index 429e8a0c..ff78db6a 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -106,6 +106,10 @@ program
'specify filename case ("pascal", "kebab", "camel") (default: "pascal")',
)
.option('--icon', 'use "1em" as width and height')
+ .option(
+ '--jsx-runtime ',
+ 'specify JSX runtime ("automatic", "classic", "classic-preact") (default: "classic")',
+ )
.option('--typescript', 'transform svg into typescript')
.option('--native', 'add react-native support with react-native-svg')
.option('--memo', 'add React.memo into the result component')
diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts
index 09ba6bfc..113e38ab 100644
--- a/packages/core/src/config.ts
+++ b/packages/core/src/config.ts
@@ -6,23 +6,40 @@ import type { TransformOptions as BabelTransformOptions } from '@babel/core'
import type { ConfigPlugin } from './plugins'
import type { State } from './state'
-export interface Config extends Partial> {
+export interface Config {
+ ref?: boolean
+ titleProp?: boolean
+ expandProps?: boolean | 'start' | 'end'
dimensions?: boolean
- runtimeConfig?: boolean
+ icon?: boolean
native?: boolean
+ svgProps?: {
+ [key: string]: string
+ }
+ replaceAttrValues?: {
+ [key: string]: string
+ }
+ runtimeConfig?: boolean
typescript?: boolean
prettier?: boolean
prettierConfig?: PrettierOptions
svgo?: boolean
svgoConfig?: SvgoOptions
configFile?: string
+ template?: TransformOptions['template']
+ memo?: boolean
+ exportType?: 'named' | 'default'
+ namedExport?: string
+ jsxRuntime?: 'classic' | 'classic-preact' | 'automatic'
// CLI only
index?: boolean
plugins?: ConfigPlugin[]
// JSX
- jsx?: { babelConfig?: BabelTransformOptions }
+ jsx?: {
+ babelConfig?: BabelTransformOptions
+ }
}
export const DEFAULT_CONFIG: Config = {
diff --git a/packages/plugin-jsx/src/index.test.ts b/packages/plugin-jsx/src/index.test.ts
index b1e968e0..f4e3bccd 100644
--- a/packages/plugin-jsx/src/index.test.ts
+++ b/packages/plugin-jsx/src/index.test.ts
@@ -17,7 +17,7 @@ const svgBaseCode = `
`
describe('plugin', () => {
- it('should transform code', () => {
+ it('transforms code', () => {
const result = jsx(svgBaseCode, {}, { componentName: 'SvgComponent' })
expect(result).toMatchInlineSnapshot(`
"import * as React from \\"react\\";
@@ -28,7 +28,35 @@ describe('plugin', () => {
`)
})
- it('should accept jsx config', () => {
+ it('supports "automatic" runtime', () => {
+ const result = jsx(
+ svgBaseCode,
+ { jsxRuntime: 'automatic' },
+ { componentName: 'SvgComponent' },
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "const SvgComponent = () => ;
+
+ export default SvgComponent;"
+ `)
+ })
+
+ it('supports "preact" preset', () => {
+ const result = jsx(
+ svgBaseCode,
+ { jsxRuntime: 'classic-preact' },
+ { componentName: 'SvgComponent' },
+ )
+ expect(result).toMatchInlineSnapshot(`
+ "import { h } from \\"preact\\";
+
+ const SvgComponent = () => ;
+
+ export default SvgComponent;"
+ `)
+ })
+
+ it('accepts jsx config', () => {
const dropTitle = () => ({
visitor: {
JSXElement(path: any) {
diff --git a/packages/plugin-jsx/src/index.ts b/packages/plugin-jsx/src/index.ts
index 87dfd3ba..57e9c844 100644
--- a/packages/plugin-jsx/src/index.ts
+++ b/packages/plugin-jsx/src/index.ts
@@ -1,8 +1,33 @@
import { parse } from 'svg-parser'
import hastToBabelAst from '@svgr/hast-util-to-babel-ast'
import { transformFromAstSync, createConfigItem } from '@babel/core'
-import svgrBabelPreset from '@svgr/babel-preset'
-import type { Plugin } from '@svgr/core'
+import svgrBabelPreset, {
+ Options as SvgrPresetOptions,
+} from '@svgr/babel-preset'
+import type { Plugin, Config } from '@svgr/core'
+
+const getJsxRuntimeOptions = (config: Config): Partial => {
+ switch (config.jsxRuntime) {
+ case null:
+ case undefined:
+ case 'classic':
+ return {
+ jsxRuntime: 'classic',
+ importSource: 'react',
+ jsxRuntimeImport: { namespace: 'React', source: 'react' },
+ }
+ case 'classic-preact':
+ return {
+ jsxRuntime: 'classic',
+ importSource: 'preact/compat',
+ jsxRuntimeImport: { specifiers: ['h'], source: 'preact' },
+ }
+ case 'automatic':
+ return { jsxRuntime: 'automatic' }
+ default:
+ throw new Error(`Unsupported "jsxRuntime" "${config.jsxRuntime}"`)
+ }
+}
const jsxPlugin: Plugin = (code, config, state) => {
const filePath = state.filePath || 'unknown'
@@ -10,12 +35,30 @@ const jsxPlugin: Plugin = (code, config, state) => {
const babelTree = hastToBabelAst(hastTree)
+ const svgPresetOptions: SvgrPresetOptions = {
+ ref: config.ref,
+ titleProp: config.titleProp,
+ expandProps: config.expandProps,
+ dimensions: config.dimensions,
+ icon: config.icon,
+ native: config.native,
+ svgProps: config.svgProps,
+ replaceAttrValues: config.replaceAttrValues,
+ typescript: config.typescript,
+ template: config.template,
+ memo: config.memo,
+ exportType: config.exportType,
+ namedExport: config.namedExport,
+ ...getJsxRuntimeOptions(config),
+ state,
+ }
+
const result = transformFromAstSync(babelTree, code, {
caller: {
name: 'svgr',
},
presets: [
- createConfigItem([svgrBabelPreset, { ...config, state }], {
+ createConfigItem([svgrBabelPreset, svgPresetOptions], {
type: 'preset',
}),
],
diff --git a/tsconfig.json b/tsconfig.json
index a5fb6577..9eed761b 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -11,6 +11,7 @@
"module": "ESNext",
"strict": true,
"sourceMap": true,
- "declaration": true
+ "declaration": true,
+ "resolveJsonModule": true
}
}