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.
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;
}
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.
By passing
typescript: true
into thesvgr
options, I am expecting aindex.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
.
I think you have two options:
.d.ts
fileIn 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:
tsc --emitDeclarationOnly
)esbuild-plugin-d.ts
Your build chain may look like this at the end:
.tsx
files from SVGs using @svgr/cli
..d.ts
file, either as a separate step using tsc
or merging with the step above using an esbuild plugin..d.ts
file from your JavaScript bundleIf 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.