Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Syntax error when using SVGR with Webpack and configured to output TypeScript

I'm trying to use SVGR for the first time in a react project to be able to import .svg files as components.

Using SVGR with Webpack

Have configured SVGR in my webpack config as follows...

{
  test: /\.svg$/,
  use: [
    {
      loader: "@svgr/webpack",
      options: {
        typescript: true,
      },
    },
  ],
},

To test it out with an example, I have tried a simple svg at src/icons/clipboard.svg that looks like this...

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>clipboard</title>
<path d="M29 4h-9c0-2.209-1.791-4-4-4s-4 1.791-4 4h-9c-0.552 0-1 0.448-1 1v26c0 0.552 0.448 1 1 1h26c0.552 0 1-0.448 1-1v-26c0-0.552-0.448-1-1-1zM16 2c1.105 0 2 0.895 2 2s-0.895 2-2 2c-1.105 0-2-0.895-2-2s0.895-2 2-2zM28 30h-24v-24h4v3c0 0.552 0.448 1 1 1h14c0.552 0 1-0.448 1-1v-3h4v24z"></path>
<path d="M14 26.828l-6.414-7.414 1.828-1.828 4.586 3.586 8.586-7.586 1.829 1.828z"></path>
</svg>

With this svg file and the above config, SVGR ends up generating the following...

import * as React from "react";

function SvgClipboard(props: React.SVGProps<SVGSVGElement>) {
  return (
    <svg width={32} height={32} {...props}>
      <path d="M29 4h-9a4 4 0 00-8 0H3a1 1 0 00-1 1v26a1 1 0 001 1h26a1 1 0 001-1V5a1 1 0 00-1-1zM16 2a2 2 0 11.001 3.999A2 2 0 0116 2zm12 28H4V6h4v3a1 1 0 001 1h14a1 1 0 001-1V6h4v24z" />
      <path d="M14 26.828l-6.414-7.414 1.828-1.828L14 21.172l8.586-7.586 1.829 1.828z" />
    </svg>
  );
}

export default SvgClipboard;

However, this results in a Webpack error...

ERROR in ./src/icons/clipboard.svg
SyntaxError: unknown: Unexpected token, expected "," (3:27)
  1 | import * as React from "react";
  2 | 
> 3 | function SvgClipboard(props: React.SVGProps<SVGSVGElement>) {
    |                            ^

It appears the issue is with the props that gets included in the generated output.

So, if I disable props using the SVGR config setting, Expand Props, i.e. as such...

test: /\.svg$/,
use: [
  {
    loader: "@svgr/webpack",
    options: {
      typescript: true,
      expandProps: false,
    },
  },
],

then Webpack runs ok, but now I've lost access to any props that I want to pass in to the svg react component.

I can't seem to figure out why there is a syntax error.


Attempting something else to try and narrow it down...

If I disable the SVGR config setting for generating typescript, then the problem goes away. For example,

test: /\.svg$/,
use: [
  {
    loader: "@svgr/webpack",
    options: {
      typescript: false,
    },
  },
],

So this narrows it down a bit further for me. My thoughts then point toward how I'm loading typescript in Webpack.

In the doco for SVGR it states that

By default, @svgr/webpack includes a babel-loader with an optimized configuration.

So, perhaps the issue is in relation to how babel is configured?

When I look at that configuration, I don't see anything in there related to TypeScript... so, I'm wondering if the @svgr/webpack loader, with how I have it configured, is generating TypeScript but the in-built babel loader isn't parsing correctly - i.e. it perhaps assumes that it is receiving JavaScript? Maybe that is where the syntax error is coming from?

like image 575
Danoz Avatar asked Jul 16 '20 11:07

Danoz


1 Answers

I managed to get rid of this error using the following setup:

First, you need to make svgr emit TypeScript, and then disable its internal Babel processing step, because that is not configured to handle TypeScript. We will insert our own loader for that. Oh, and for some reason that I don't quite understand (see also below) you also have to tell svgr to output files with a tsx extension:

So somewhere in your Webpack configuration you would have

{
    test: /\.svg$/,
    use: [
        "babel-loader",
        {
            loader: "@svgr/webpack",
            options: {
                typescript: true,
                babel: false,
                ext: "tsx",
            },
        },
    ],
},

This assumes that you configure Babel in its own configuration file. Providing a full configuration for it is beyond the scope of this answer, but for the record: It of course needs to be configured to handle TypeScript and JSX.

Now, this doesn't seem to be enough: What's happening here, I think, is that Babel somehow receives the generated source in association with the original import path, which probably ends in .svg, or maybe even without any path at all, now that I think about it. Now Babel, or rather the TypeScript plugin/preset (sorry, my knowledge about Babel internals is very limited), doesn't think it necessary to process this file as TypeScript, because of course SVGs aren't TypeScript files, and if there is no path involved at all, there is also no hint as to why it should be TypeScript.

One way to force Babel to process this as TypeScript is configuring your @babel/preset-typescript as follows:

["@babel/typescript", {
    allExtensions: true,
    isTSX: true,
}],

This makes the TypeScript part of Babel disregard the extension and unconditionally process everything as TSX. Now, this might of course lead to other problems down the line, but I haven't found a way to configure this in a more granular way. (Although admittedly I haven't looked very hard, yet. Any pointers in the right direction are most welcome. :)

like image 85
Julian Kniephoff Avatar answered Nov 15 '22 04:11

Julian Kniephoff