Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support jsxRuntime #613

Merged
merged 1 commit into from
Nov 1, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 = () => <svg><g /></svg>;

export default SvgComponent;"
`;

exports[`plugin javascript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "specifiers" 1`] = `
"import { h } from \\"preact\\";

const SvgComponent = () => <svg><g /></svg>;

export default SvgComponent;"
`;

exports[`plugin javascript #jsxRuntime supports "automatic" jsxRuntime 1`] = `
"const SvgComponent = () => <svg><g /></svg>;

export default SvgComponent;"
`;

exports[`plugin javascript #jsxRuntime supports "classic" jsxRuntime 1`] = `
"import * as React from \\"react\\";

const SvgComponent = () => <svg><g /></svg>;

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) => <svg><g /></svg>;

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';

Expand Down Expand Up @@ -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 = () => <svg><g /></svg>;

export default SvgComponent;"
`;

exports[`plugin typescript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "specifiers" 1`] = `
"import { h } from \\"preact\\";

const SvgComponent = () => <svg><g /></svg>;

export default SvgComponent;"
`;

exports[`plugin typescript #jsxRuntime supports "automatic" jsxRuntime 1`] = `
"const SvgComponent = () => <svg><g /></svg>;

export default SvgComponent;"
`;

exports[`plugin typescript #jsxRuntime supports "classic" jsxRuntime 1`] = `
"import * as React from \\"react\\";

const SvgComponent = () => <svg><g /></svg>;

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<SVGSVGElement>) => <svg><g /></svg>;

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';

Expand Down
53 changes: 53 additions & 0 deletions packages/babel-plugin-transform-svg-component/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,5 +228,58 @@ describe('plugin', () => {
expect(code).toMatchSnapshot()
})
})

describe('#jsxRuntime', () => {
it('supports "automatic" jsxRuntime', () => {
const { code } = testPlugin(language)('<svg><g /></svg>', {
jsxRuntime: 'automatic',
})
expect(code).toMatchSnapshot()
})

it('supports "classic" jsxRuntime', () => {
const { code } = testPlugin(language)('<svg><g /></svg>', {
jsxRuntime: 'classic',
})
expect(code).toMatchSnapshot()
})

it('allows to specify a custom "classic" jsxRuntime using "specifiers"', () => {
const { code } = testPlugin(language)('<svg><g /></svg>', {
jsxRuntime: 'classic',
jsxRuntimeImport: { specifiers: ['h'], source: 'preact' },
})
expect(code).toMatchSnapshot()
})

it('allows to specify a custom "classic" jsxRuntime using "namespace"', () => {
const { code } = testPlugin(language)('<svg><g /></svg>', {
jsxRuntime: 'classic',
jsxRuntimeImport: { namespace: 'Preact', source: 'preact' },
})
expect(code).toMatchSnapshot()
})

it('throws with invalid configuration', () => {
expect(() => {
testPlugin(language)('<svg><g /></svg>', {
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)('<svg><g /></svg>', {
memo: true,
ref: true,
importSource: 'preact/compat',
jsxRuntimeImport: { specifiers: ['h'], source: 'preact' },
})
expect(code).toMatchSnapshot()
})
})
})
11 changes: 10 additions & 1 deletion packages/babel-plugin-transform-svg-component/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
49 changes: 38 additions & 11 deletions packages/babel-plugin-transform-svg-component/src/variables.ts
Original file line number Diff line number Diff line change
@@ -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<typeof t.tsPropertySignature>
Expand All @@ -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) => {
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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)),
Expand Down
26 changes: 26 additions & 0 deletions packages/cli/src/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,32 @@ export default SvgFile
"
`;

exports[`cli should support various args: --jsx-runtime automatic 1`] = `
"const SvgFile = (props) => (
<svg width={48} height={1} xmlns=\\"http://www.w3.org/2000/svg\\" {...props}>
<path d=\\"M0 0h48v1H0z\\" fill=\\"#063855\\" fillRule=\\"evenodd\\" />
</svg>
)

export default SvgFile

"
`;

exports[`cli should support various args: --jsx-runtime classic-preact 1`] = `
"import { h } from 'preact'

const SvgFile = (props) => (
<svg width={48} height={1} xmlns=\\"http://www.w3.org/2000/svg\\" {...props}>
<path d=\\"M0 0h48v1H0z\\" fill=\\"#063855\\" fillRule=\\"evenodd\\" />
</svg>
)

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'
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ describe('cli', () => {

it.each([
['--no-dimensions'],
['--jsx-runtime classic-preact'],
['--jsx-runtime automatic'],
['--expand-props none'],
['--expand-props start'],
['--icon'],
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ program
'specify filename case ("pascal", "kebab", "camel") (default: "pascal")',
)
.option('--icon', 'use "1em" as width and height')
.option(
'--jsx-runtime <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')
Expand Down
23 changes: 20 additions & 3 deletions packages/core/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Omit<TransformOptions, 'state'>> {
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 = {
Expand Down