What could be the reason for SVGR not producing a TypeScript declaration file in my esbuild setup?

I have been working on developing a custom SVG icon library using TypeScript. So far, the SVGR tool has been quite useful in creating components from SVG imports. However, I am encountering an issue with generating types that would allow me to pass attributes like title and ref to the bundled components.

Reproduction

To provide context for my issue, I will share a link to a sample repository along with code snippets. Despite setting up everything as specified, SVGR is successfully creating components but not generating the necessary types. This results in an error whenever I attempt to use an icon from this package.

Repository Link: 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

I expect by including typescript: true in the svgr options, a index.d.tsx file should be generated based on the component definitions created by SVGR.

According to SVGR documentation:

This feature generates .tsx files with TypeScript typings.

Note: While currently utilizing SVGR with ESBuild, similar difficulties persist when attempting to integrate with Rollup bundler.

Answer №1

Solving the TypeScript Generation Issue in SVGR

When passing typescript: true in the svgr options, the expectation is to generate a index.d.tsx file based on the components created. However, this expectation is incorrect as SVGR only generates .tsx files with the typescript option enabled, not .d.ts files. This information can be found in the documentation.

Even with the typescript option specified, SVGR will not generate .tsx files when used within an esbuild plugin. The esbuild-plugin-svgr relies on the @svgr/core's transform function, which does not write output files itself but rather transforms SVG to JSX or TSX code. To address this issue, there are two potential solutions:

Option 1: Separate SVGR from esbuild for .d.ts file generation

To generate a .d.ts file, consider using @svgr/cli instead of esbuild-plugin-svgr. Run SVGR before esbuild manually, through script chaining, or utilizing npm's pre-script hook. You may need to explore alternative methods like emitting declaration files with the TypeScript compiler or using plugins like esbuild-plugin-d.ts.

Your updated build chain could involve:

  1. Generating .tsx files from SVGs using @svgr/cli.
  2. Bundling with esbuild.
  3. Generating a .d.ts file separately with tsc or merging it into the previous step with an esbuild plugin.

Option 2: Generating .d.ts from JavaScript bundle

If retaining the current build setup is important, you could create a .d.ts file from the JavaScript bundle using

tsc dist/index.js --declaration --allowJs --emitDeclarationOnly
. Keep in mind that TypeScript might struggle to infer all types accurately, resulting in an incomplete type declaration file.

Answer №2

Encountered a similar issue with svgr while attempting to package typescript types alongside the rollup-plugin-dts plugin.

Resolved it by creating a unified icon.d.ts file:

import { SVGProps } from 'react';
declare const ReactComponent: (props: SVGProps<SVGSVGElement>) => JSX.Element;
export { ReactComponent };

and then implemented the alias plugin in this manner:

import alias from '@rollup/plugin-alias';
...
      plugins: [
        alias({
          entries: [
            {
              find: /^.*\.svg$/,
              replacement: 'src/types/icon.d.ts',
            },
          ],
        }),
        dts(),
      ],

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The initial function that gets executed in the lodash chain is tap()

When using lodash chain to perform actions synchronously, I encountered an issue where .tap() is executed before the desired stage. I have been unable to find a solution using promises. I expected lodash chain to ensure actions are carried out in a synch ...

Dynamically apply classes in Angular using ngClass

Help needed with setting a class dynamically. Any guidance is appreciated. Below is the class in my SCSS file: .form-validation.invalid { border: 2px solid red } In my ts file, there's a variable named isEmailValid. When this variable is set to ...

Behavior of Shadow DOM role when using the <a> element without an href attribute

Recently, I started working with the shadow DOM and encountered a strange issue: In my Ionic Angular application, there is a text styled like a link in this form (simplified): <a href="${ifDefined(this.href)}">link</a> When testing ...

Transitioning React components organized in groups to TypeScript

As I transition my react project to incorporate typescript, one challenge I encountered was adjusting the file structure. In its simplified form, here is how the original js project's file structure looked like: src components index.js inputs butt ...

The FullCalendarModule does not have a property named 'registerPlugins' in its type definition

Currently in the process of integrating fullcalendar into my Angular 15 project and encountering the following error: Error: src/app/views/pages/takvim/takvim.module.ts:18:20 - error TS2339: Property 'registerPlugins' does not exist on type &apo ...

When utilizing the Map.get() method in typescript, it may return undefined, which I am effectively managing in my code

I'm attempting to create a mapping of repeated letters using a hashmap and then find the first non-repeated character in a string. Below is the function I've developed for this task: export const firstNonRepeatedFinder = (aString: string): strin ...

A step-by-step guide on creating a Decorator using the TypeScript compile API

How can I create a custom class in TypeScript with multiple 'class-validator' decorators to ensure the property types are correct? I am considering using `ts.factory.createDecorator`, but I'm unsure how to obtain a `ts.Expression` for it. ...

Tips for presenting SVG symbols using Interpolation within Angular 7 from a JSON document

When it comes to displaying content in Angular 7 components, JSON is used. However, I have encountered a problem while trying to incorporate SVG icons from our UX team into the component using JSON. Using the img tag restricts me from applying a CSS class ...

Can you explain the distinction between @types/material-ui and the official @mui/types bundle?

When it comes to npm packages, I came across @types/material-ui and @mui/types. I'm aware that the former is backed by the Definitely Typed community, but what's the reasoning behind its existence when an official types package already exists? D ...

After the transition from Angular 8 to Angular 9, an issue arose with the node_modules/@zerohouse/router-tab/zerohouse-router-tab.d.ts file, as it was not declared

Error Image package.json { "name": "client", "version": "0.0.0", "license": "MIT", "scripts": { "ng": "ng", "serveapp": "ng serve ...

Use a spy to mock a component method using karma and jasmine

Encountering this error message during testing: ERROR: 'Error during cleanup of component' The issue stems from the following code snippet : ngOnDestroy(){ methodCallToMock() } To address this, I need to mock the methodCallToMock() functi ...

Unable to attach to 'leafletOptions' as it is unrecognized as a property of 'div'

It seems like I keep encountering this problem, which is often resolved by adjusting import statements. Right now, my imports look like this: import { LeafletModule } from 'node_modules/@asymmetrik/ngx-leaflet'; import * as L from 'leaflet& ...

What about combining a fat arrow function with a nested decorator?

Trying to implement a fat arrow function with a nestjs decorator in a controller. Can it be done in the following way : @Controller() export class AppController { @Get() findAll = (): string => 'This is coming from a fat arrow !'; } Wh ...

What could be the reason it's not functioning as expected? Maybe something to do with T extending a Record with symbols mapped

type Check<S extends Record<unique, unknown>> = S; type Output = Check<{ b: number; }>; By defining S extends Record<unique, unknown>, the Check function only accepts objects with unique keys. So why does Check<{b:number}> ...

Having trouble with the service connection in Stackblitz?

Objective: I am trying to establish a connection with the Data service in StackBlitz. Issue: Unfortunately, my attempts are not successful. Can anyone pinpoint what I am overlooking? Project Link: https://stackblitz.com/edit/angular-mpy6pr Many th ...

Improving the structure of a TypeScript switch statement

Looking for a more efficient way to implement a switch statement in TypeScript? Here is the current code snippet from a component: switch (actionType) { case Type.Cancel { this.cancel(); break; } case Type.Discard { thi ...

Guide on showing a component exclusively for iPads with React and TypeScript

I need help displaying an icon only in the component for iPad devices, and not on other devices. As a beginner in coding for iPads and mobile devices, I am unsure how to achieve this specific requirement for the iPad device. Below is the code snippet tha ...

Attempting to create a sorting functionality using Vue and Typescript

I am trying to implement a feature where clicking on the TableHead should toggle between sorting by most stock on top and least stock on top. Currently, I have two buttons for this functionality, but it's not very user-friendly. My approach involves ...

Utilizing Prisma Enum with Nest JS: A Comprehensive Guide

While working on my backend with Prisma and NestJS, I have encountered an issue. I defined an enum in Prisma that works fine in debug mode, but when I generate the Prisma client using nest build, I get an error. Object.values(client_1.RoomSharingType).join ...

When bootstrap is imported, SVG is displayed with a square background

The combination of reactJS and bootstrap has caused an unexpected issue for me. After importing bootstrap, strange squares have started to appear behind my SVG images... https://i.sstatic.net/4vmP8.png Is anyone familiar with what is causing this and ho ...