Home > Software engineering >  Why does SVGR not generate a TypeScript declaration file in my esbuild configuration?
Why does SVGR not generate a TypeScript declaration file in my esbuild configuration?

Time:05-21

I am creating an SVG icon library in TypeScript. So far, SVGR has been terrific, but the final piece I need are the generated types to allow passing title and ref to the bundled components.

Reproduction

I'm not sure what all may be helpful to see, so I will include a repo and some code samples. But with the following setup, svgr is creating the components from the SVG imports, but no types are generated. As a result, whenever I try to use an icon from this package, I am warned that the declaration file for it cannot be found.

POC Repo: https://github.com/yuschick/icon-demo

src/index.ts

export { default as TestIcon } from "./svg/test.svg";
export { default as ArrowThinLeft } from './svg/arrow-thin-left.svg';

package.json

{
  "module": "dist/index.js",
  "types": "dist/index.d.ts",
  "type": "module",
  "scripts": {
    "build": "node esbuild.config.js",
  },
  "devDependencies": {
    "esbuild": "^0.14.39",
    "esbuild-plugin-svgr": "^1.0.1",
    "typescript": "^4.6.3"
  }
}

esbuild.config.js

import esbuild from "esbuild";
import svgr from "esbuild-plugin-svgr";

esbuild.build({
    bundle: true,
    entryPoints: ["src/index.ts"],
    external: ["react"],
    format: "esm",
    // minify: true,
    outfile: "dist/index.js",
    plugins: [svgr({ titleProp: true, ref: true, expandProps: false, typescript: true })],
})
  .catch(() => process.exit(1));

tsconfig.json

{
  "compilerOptions": {
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "alwaysStrict": true,
    "baseUrl": "src",
    "checkJs": true,
    "declaration": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "lib": ["dom", "dom.iterable", "esnext"],
    "module": "esnext",
    "moduleResolution": "node",
    "noEmit": true,
    "outDir": "dist",
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "esnext"
  },
  "include": ["src", "global.d.ts"]
}

global.d.ts

declare module '*.svg' {
    const content: string;
    export default content;
}

Expected behavior

By passing typescript: true into the svgr options, I am expecting a index.d.tsx file to be generated based on the generated components.

Per the SVGR docs:

Generates .tsx files with TypeScript typings.

Note: While I am using SVGR with ESBuild, the same problem exists when trying with Rollup as well.

CodePudding user response:

Problem

By passing typescript: true into the svgr options, I am expecting a index.d.tsx file to be generated based on the generated components.

This expectation is not correct. SVGR will only generate .tsx files if you specify the typescript option and not .d.ts files. From documentation (which you've found as well):

TypeScript

Generates .tsx files with TypeScript typings.

However, even with this option specified, SVGR won't generate .tsx files because you're using this within an esbuild plugin. esbuild-plugin-svgr under the hood utilises the transform function from @svgr/core (see esbuild-plugin-svgr/index.js:L4). The transform function only transforms the SVG to JSX or TSX but never writes the output(s). @svgr/cli handles writing the output(s) to file(s), for example see @svgr/cli/src/dirCommand.ts.

Solution

I think you have two options:

Separate out SVGR from esbuild, then generate a .d.ts file

In other words stop using esbuild-plugin-svgr and use @svgr/cli instead. You will then have to run SVGR before esbuild; either manually, chaining commands in a script or using npm's pre-script hook. Afterwards you'll have to generate a .d.ts file. Although, looking at esbuild#95, it doesn't seem it's supported out of the box. You may want to explore suggestions listed there including:

  • Using the TypeScript compiler to emit a declaration file (tsc --emitDeclarationOnly)
  • Using a plugin such as esbuild-plugin-d.ts

Your build chain may look like this at the end:

  1. Generate .tsx files from SVGs using @svgr/cli.
  2. Bundle using esbuild.
  3. Generate .d.ts file, either as a separate step using tsc or merging with the step above using an esbuild plugin.

Generate a .d.ts file from your JavaScript bundle

If for some reason you really want to keep your current build configuration you could generate a .d.ts file from your JavaScript bundle file with tsc dist/index.js --declaration --allowJs --emitDeclarationOnly (see TypeScript documentation). Caveat is TypeScript may not be able to infer all the types so you may have an incomplete type declaration file.

  • Related