Repository: gregberge/svgr
Pull request #802 (2022-11-28 17:59:56)

Allow hyperapp compatibility via config file
## Summary

Right now I can't find a way to integrate svgr with my workflow (parcel2 building a hyperapp-based website), as there are a couple of missing things:

- no way to specify a custom JSX import config via a config file
- no way to use the custom JSX import config to specify that we want to use the default import

## Test plan

```
$ cat ~/Projects/KaraKara/browser2/package.json | jq .svgr
{
  "jsxRuntimeImport": {
    "source": "hyperapp-jsx-pragma",
    "defaultSpecifier": "h"
  },
  "icon": true,
  "replace-attr-values": "#4d4d4d=currentColor"
}

$ npx svgr ~/Projects/KaraKara/browser2/src/static/favicon.svg | head -n 5
import h from "hyperapp-jsx-pragma";
const SvgFavicon = (props) => (
...
```

vercel[bot]: **The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Updated |
| :--- | :----- | :------ | :------ |
| **svgr** | 🔄 Building ([Inspect](https://vercel.com/gregberge/svgr/DVXwJEzEoyQjfXEcGiW8RTpPQVJD)) |  | Nov 28, 2022 at 6:00PM (UTC) |


shish: Done~ (And fixed another couple of tables in the doc while I was there)
shish: Pushed again to "fix" formatting to make the CI tests happy. Apparently `prettier` doesn't like the tables being formatted correctly, so I reverted that part...
shish: And now with unit tests passing - I _think_ this is ready to merge now?

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 87c55ba..15b9ca2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -6,6 +6,9 @@ on:
   pull_request:
     branches: [main]
 
+permissions:
+  contents: read #  to fetch code (actions/checkout)
+
 jobs:
   build:
     runs-on: ubuntu-latest
diff --git a/packages/babel-plugin-transform-svg-component/src/types.ts b/packages/babel-plugin-transform-svg-component/src/types.ts
index c171a5b..c4caad7 100644
--- a/packages/babel-plugin-transform-svg-component/src/types.ts
+++ b/packages/babel-plugin-transform-svg-component/src/types.ts
@@ -29,6 +29,7 @@ interface State {
 export interface JSXRuntimeImport {
   source: string
   namespace?: string
+  defaultSpecifier?: string
   specifiers?: string[]
 }
 
diff --git a/packages/babel-plugin-transform-svg-component/src/variables.ts b/packages/babel-plugin-transform-svg-component/src/variables.ts
index 64a1481..e597b39 100644
--- a/packages/babel-plugin-transform-svg-component/src/variables.ts
+++ b/packages/babel-plugin-transform-svg-component/src/variables.ts
@@ -69,13 +69,17 @@ const getJsxRuntimeImport = (cfg: JSXRuntimeImport) => {
   const specifiers = (() => {
     if (cfg.namespace)
       return [t.importNamespaceSpecifier(t.identifier(cfg.namespace))]
+    if (cfg.defaultSpecifier) {
+      const identifier = t.identifier(cfg.defaultSpecifier)
+      return [t.importDefaultSpecifier(identifier)]
+    }
     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`,
+      `Specify "namespace", "defaultSpecifier", or "specifiers" in "jsxRuntimeImport" option`,
     )
   })()
   return t.importDeclaration(specifiers, t.stringLiteral(cfg.source))
diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts
index 9468ca8..98f2156 100644
--- a/packages/core/src/config.ts
+++ b/packages/core/src/config.ts
@@ -32,6 +32,12 @@ export interface Config {
   exportType?: 'named' | 'default'
   namedExport?: string
   jsxRuntime?: 'classic' | 'classic-preact' | 'automatic'
+  jsxRuntimeImport?: {
+    source: string
+    namespace?: string
+    specifiers?: string[]
+    defaultSpecifier?: string
+  }
 
   // CLI only
   index?: boolean
diff --git a/packages/plugin-jsx/src/index.ts b/packages/plugin-jsx/src/index.ts
index 9d198df..d05faca 100644
--- a/packages/plugin-jsx/src/index.ts
+++ b/packages/plugin-jsx/src/index.ts
@@ -7,6 +7,12 @@ import svgrBabelPreset, {
 import type { Plugin, Config } from '@svgr/core'
 
 const getJsxRuntimeOptions = (config: Config): Partial<SvgrPresetOptions> => {
+  if (config.jsxRuntimeImport) {
+    return {
+      importSource: config.jsxRuntimeImport.source,
+      jsxRuntimeImport: config.jsxRuntimeImport,
+    }
+  }
   switch (config.jsxRuntime) {
     case null:
     case undefined:
diff --git a/website/pages/docs/next.mdx b/website/pages/docs/next.mdx
index efbb289..5d59f5d 100644
--- a/website/pages/docs/next.mdx
+++ b/website/pages/docs/next.mdx
@@ -21,22 +21,42 @@ yarn add --dev @svgr/webpack
 
 ## Usage
 
-Using SVGR into Next.js is possible by configuring `@svgr/webpack`.
+Using SVGR in Next.js is possible with `@svgr/webpack`.
 
 **next.config.js**
 
 ```js
 module.exports = {
   webpack(config) {
-    config.module.rules.push({
-      test: /\.svg$/i,
-      issuer: /\.[jt]sx?$/,
-      use: ['@svgr/webpack'],
-    })
+    // Grab the existing rule that handles SVG imports
+    const fileLoaderRule = config.module.rules.find((rule) =>
+      rule.test?.test?.(".svg")
+    );
 
-    return config
+    config.module.rules.push(
+      // Reapply the existing rule, but only for svg imports ending in ?url
+      {
+        ...fileLoaderRule,
+        test: /\.svg$/i,
+        resourceQuery: /url/, // *.svg?url
+      },
+      // Convert all other *.svg imports to React components
+      {
+        test: /\.svg$/i,
+        issuer: /\.[jt]sx?$/,
+        resourceQuery: { not: /url/ }, // exclude if *.svg?url
+        use: ["@svgr/webpack"],
+      },
+    );
+
+    // Modify the file loader rule to ignore *.svg, since we have it handled now.
+    fileLoaderRule.exclude = /\.svg$/i;
+
+    return config;
   },
-}
+
+  // ...other config
+};
 ```
 
 **Your code**
@@ -51,4 +71,17 @@ const Example = () => (
 )
 ```
 
+Or, using the classic (URL) import:
+
+```js
+import Image from 'next/image'
+import starUrl from './star.svg?url'
+
+const Example = () => (
+  <div>
+    <Image src={starUrl} />
+  </div>
+)
+```
+
 Please refer to [SVGR webpack guide](/docs/webpack/) for advanced use cases.
diff --git a/website/pages/docs/options.mdx b/website/pages/docs/options.mdx
index bb7c2da..c9386e9 100644
--- a/website/pages/docs/options.mdx
+++ b/website/pages/docs/options.mdx
@@ -36,9 +36,9 @@ Specify a JSX runtime to use.
 - "automatic": do not add anything
 - "classic-preact": adds `import { h } from 'preact'` on the top of file
 
-| Default   | CLI Override    | API Override           |
-| --------- | --------------- | ---------------------- | ----------- | ----------------- |
-| `classic` | `--jsx-runtime` | `jsxRuntime: 'classic' | 'automatic' | 'classic-preact'` |
+| Default   | CLI Override    | API Override                                               |
+| --------- | --------------- | ---------------------------------------------------------- |
+| `classic` | `--jsx-runtime` | `jsxRuntime: 'classic' \| 'automatic' \| 'classic-preact'` |
 
 ## JSX runtime import source
 
@@ -46,17 +46,19 @@ Specify a custom JSX runtime source to use. Allows to customize the import added
 
 Example: `jsxRuntimeImport: { source: 'preact', specifiers: ['h'] }` for "classic-preact" equivalent.
 
-| Default | CLI Override | API Override                                                 |
-| ------- | ------------ | ------------------------------------------------------------ |
-| `null`  | -            | `jsxRuntimeImport: { source: string, specifiers: string[] }` |
+To use the default import instead of a list of names, you can use `defaultSpecifier`, for example to use svgr with `hyperapp-jsx-pragma`, you can specify `jsxRuntimeImport: { source: 'hyperapp-jsx-pragma', defaultSpecifier: 'h' }` to get an end result of `import h from "hyperapp-jsx-pragma";`
+
+| Default | CLI Override | API Override                                                                           |
+| ------- | ------------ | -------------------------------------------------------------------------------------- |
+| `null`  | -            | `jsxRuntimeImport: { source: string, specifiers: string[], defaultSpecifier: string }` |
 
 ## Icon
 
 Replace SVG `width` and `height` by a custom value. If value is omitted, it uses `1em` in order to make SVG size inherits from text size.
 
-| Default | CLI Override     | API Override   |
-| ------- | ---------------- | -------------- | ------ | ------- |
-| `false` | `--icon [value]` | `icon: boolean | string | number` |
+| Default | CLI Override     | API Override                        |
+| ------- | ---------------- | ----------------------------------- |
+| `false` | `--icon [value]` | `icon: boolean \| string \| number` |
 
 > If you use `--icon` without any argument, be careful of separating files from arguments using `--`, ex: `svgr --icon -- assets/svg`
 
diff --git a/website/pages/docs/remix.mdx b/website/pages/docs/remix.mdx
index e56eb65..39098ca 100644
--- a/website/pages/docs/remix.mdx
+++ b/website/pages/docs/remix.mdx
@@ -14,7 +14,7 @@ Configure your [Remix](https://remix.run/) project to import SVG as React compon
 ## Install
 
 ```bash
-npm install --save-dev @svgr/cli @svgr/plugin-svgo @svgr/plugin-jsx @svgr/plugin-prettier npm-watch npm-run-allnpm-watch npm-run-all
+npm install --save-dev @svgr/cli @svgr/plugin-svgo @svgr/plugin-jsx @svgr/plugin-prettier npm-watch npm-run-all
 ```
 
 ## Usage


Test patch:
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 4cb9694..93ded8d 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,11 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`plugin javascript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "defaultSpecifier" 1`] = `
+"import h from "hyperapp-jsx-pragma";
+const SvgComponent = () => <svg><g /></svg>;
+export default SvgComponent;"
+`;
+
 exports[`plugin javascript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "namespace" 1`] = `
 "import * as Preact from "preact";
 const SvgComponent = () => <svg><g /></svg>;
@@ -226,6 +232,12 @@ const Memo = memo(ForwardRef);
 export default Memo;"
 `;
 
+exports[`plugin typescript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "defaultSpecifier" 1`] = `
+"import h from "hyperapp-jsx-pragma";
+const SvgComponent = () => <svg><g /></svg>;
+export default SvgComponent;"
+`;
+
 exports[`plugin typescript #jsxRuntime allows to specify a custom "classic" jsxRuntime using "namespace" 1`] = `
 "import * as Preact from "preact";
 const SvgComponent = () => <svg><g /></svg>;
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 7272ff0..7ce0b15 100644
--- a/packages/babel-plugin-transform-svg-component/src/index.test.ts
+++ b/packages/babel-plugin-transform-svg-component/src/index.test.ts
@@ -338,6 +338,14 @@ describe('plugin', () => {
         expect(code).toMatchSnapshot()
       })
 
+      it('allows to specify a custom "classic" jsxRuntime using "defaultSpecifier"', () => {
+        const { code } = testPlugin(language)('<svg><g /></svg>', {
+          jsxRuntime: 'classic',
+          jsxRuntimeImport: { defaultSpecifier: 'h', source: 'hyperapp-jsx-pragma' },
+        })
+        expect(code).toMatchSnapshot()
+      })
+
       it('throws with invalid configuration', () => {
         expect(() => {
           testPlugin(language)('<svg><g /></svg>', {
@@ -345,7 +353,7 @@ describe('plugin', () => {
             jsxRuntimeImport: { source: 'preact' },
           })
         }).toThrow(
-          'Specify either "namespace" or "specifiers" in "jsxRuntimeImport" option',
+          'Specify "namespace", "defaultSpecifier", or "specifiers" in "jsxRuntimeImport" option',
         )
       })
     })