I am trying to generate React SVG icons components by using below TS script:
scripts/generate.ts
:
import * as path from 'path';
import * as iconDefs from '../src/components';
import * as fs from 'fs';
import { promisify } from 'util';
import { template } from 'lodash';
const writeFile = promisify(fs.writeFile);
type IconDefinitionWithIdentifier = {
svgIdentifier: string;
iconDef: React.ComponentType;
};
function walk<T>(fn: (iconDef: IconDefinitionWithIdentifier) => Promise<T>) {
return Promise.all(
Object.keys(iconDefs).map((svgIdentifier) => {
const iconDef = (iconDefs as { [id: string]: React.ComponentType })[svgIdentifier];
return fn({ svgIdentifier, iconDef });
}),
);
}
async function generateIcons() {
const iconsDir = path.join(__dirname, '../src/icons');
try {
await promisify(fs.access)(iconsDir);
} catch {
await promisify(fs.mkdir)(iconsDir);
}
const render = template(
`
import React from 'react';
import { DMIcon, DMIconProps } from '../dm-icon';
const <%= svgIdentifier %> = (props: DMIconProps) => {
return <DMIcon {...props} icon={<%= iconDef %>} />;
}
export default <%= svgIdentifier %>;
`.trim(),
);
await walk(async (item) => {
await writeFile(path.resolve(__dirname, `../src/icons/${item.svgIdentifier}.tsx`), render(item));
});
}
generateIcons();
scripts/tsconfig.json
:
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "node",
"jsx": "preserve",
},
}
package.json
:
"type": "module",
"scripts": {
"generate:step2": "rimraf ./src/icons && cross-env TS_NODE_PROJECT=scripts/tsconfig.json node --experimental-specifier-resolution=node --loader ts-node/esm scripts/generate.ts"
},
/** other configs **/
Got below error:
$ npm run generate:step2
npm warn ignoring workspace config at D:workspacemrdulindm-iconspackagesicons-react/.npmrc
> @d-matrix/[email protected] generate:step2
> rimraf ./src/icons && cross-env TS_NODE_PROJECT=scripts/tsconfig.json node --experimental-specifier-resolution=node --loader ts-node/esm scripts/generate.ts
(node:19928) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
(node:19928) ExperimentalWarning: The Node.js specifier resolution flag is experimental. It could change or be removed at any time.
(node:19928) ExperimentalWarning: The Node.js specifier resolution flag is experimental. It could change or be removed at any time.
(Use `node --trace-warnings ...` to show where the warning was created)
file:///D:/workspace/mrdulin/dm-icons/packages/icons-react/src/components/excel.tsx:14
return (<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false" aria-labelledby={titleId} {...props}>
^
SyntaxError: Unexpected token '<'
at ModuleLoader.moduleStrategy (node:internal/modules/esm/translators:152:18)
at ModuleLoader.moduleProvider (node:internal/modules/esm/loader:299:14)
Node.js v18.20.3
src/components/excel.tsx
:
import type { SVGProps } from "react";
interface SVGRProps {
title?: string;
titleId?: string;
}
const SvgExcel = ({
title,
titleId,
...props
}: SVGProps<SVGSVGElement> & SVGRProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 16 16"
fill="currentColor"
aria-hidden="true"
focusable="false"
aria-labelledby={titleId}
{...props}
>
{title ? <title id={titleId}>{title}</title> : null}
<g fill="none" fillRule="nonzero">
<path d="M0 0h16v16H0z" />
<path
fill="#0E512B"
d="M14.957 9.043H1.043L0 7.653 1.391 6.26H14.61L16 7.652z"
/>
<path
fill="#E6E6E6"
d="M13.913 16H2.087a.695.695 0 0 1-.696-.696V.696c0-.385.312-.696.696-.696h8.348l4.174 4.174v11.13a.695.695 0 0 1-.696.696"
/>
<path fill="#B3B3B3" d="M10.435 0v3.478c0 .385.311.696.695.696h3.479z" />
<path
fill="#1B7343"
d="M15.304 13.913H.696A.695.695 0 0 1 0 13.217V7.652h16v5.565a.695.695 0 0 1-.696.696"
/>
<path
fill="#FFF"
d="M5.771 12.174h-.854l-.532-.854-.527.854h-.835l.906-1.398-.852-1.334h.819l.493.845.474-.845h.843l-.865 1.392zM6.078 12.174V9.442h.738v2.136h1.051v.596zM10.09 11.344q0 .255-.129.451a.84.84 0 0 1-.371.307q-.243.11-.57.11-.272 0-.458-.04a1.7 1.7 0 0 1-.385-.133v-.657q.211.108.44.169.226.06.418.06.165 0 .24-.057a.18.18 0 0 0 .077-.146q0-.057-.03-.098a.4.4 0 0 0-.1-.085 4 4 0 0 0-.363-.176 1.7 1.7 0 0 1-.4-.235.7.7 0 0 1-.198-.262.9.9 0 0 1-.065-.35.7.7 0 0 1 .275-.588q.275-.21.755-.21.423 0 .865.195l-.226.57q-.384-.175-.662-.175-.143 0-.209.05a.15.15 0 0 0-.065.125q0 .081.083.144.082.063.451.232.353.16.49.34a.74.74 0 0 1 .137.46M12.973 12.174h-.854l-.532-.854-.527.854h-.835l.906-1.398-.852-1.334h.819l.493.845.474-.845h.843l-.865 1.392z"
/>
</g>
</svg>
);
export default SvgExcel;
I expect the generated SVG icon component like:
import React from 'react';
import { DMIcon, DMIconProps } from '../dm-icon';
const Excel = (props: DMIconProps) => {
return <DMIcon {...props} icon={SvgExcel} />;
}
export default Excel;
GitHub Repo
Related question: ts-node and svg import gives: SyntaxError: Unexpected token ‘<‘
Related discussion: https://github.com/TypeStrong/ts-node/discussions/1965