diff --git a/.changeset/hot-jokes-float.md b/.changeset/hot-jokes-float.md new file mode 100644 index 0000000..7efa7d9 --- /dev/null +++ b/.changeset/hot-jokes-float.md @@ -0,0 +1,20 @@ +--- +"@monicon/icon-loader": minor +"@monicon/core": minor +"@monicon/esbuild": minor +"@monicon/loader": minor +"@monicon/metro": minor +"@monicon/native": minor +"@monicon/nuxt": minor +"@monicon/qwik": minor +"@monicon/react": minor +"@monicon/rollup": minor +"@monicon/rspack": minor +"@monicon/svelte": minor +"@monicon/typescript-config": minor +"@monicon/vite": minor +"@monicon/vue": minor +"@monicon/webpack": minor +--- + +implement type generation diff --git a/apps/docs/pages/troubleshooting/_meta.tsx b/apps/docs/pages/troubleshooting/_meta.tsx index 0176481..21304b5 100644 --- a/apps/docs/pages/troubleshooting/_meta.tsx +++ b/apps/docs/pages/troubleshooting/_meta.tsx @@ -1,4 +1,5 @@ export default { + typescript: "TypeScript", monorepo: "Monorepo", "module-resolution": "Module Resolution", "bundle-size": "Bundle Size", diff --git a/apps/docs/pages/troubleshooting/typescript.mdx b/apps/docs/pages/troubleshooting/typescript.mdx new file mode 100644 index 0000000..a8f88a2 --- /dev/null +++ b/apps/docs/pages/troubleshooting/typescript.mdx @@ -0,0 +1,27 @@ +# TypeScript + +Monicon uses TypeScript to generate types for the icons. To enable TypeScript type definitions for your icons, add the following configuration to your `tsconfig.json` file: + +```json filename="tsconfig.json" copy {2} +{ + "include": [".monicon/*.d.ts"] +} +``` + +You can also use the `typesFileName` option to specify the name of the file to output the types to. If you want to disable the types file, you can set the `generateTypes` option to `false`. + +```ts filename="apps/vite-react/vite.config.ts" copy {9,10} +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import monicon from "@monicon/vite"; + +export default defineConfig({ + plugins: [ + react(), + monicon({ + typesFileName: "types", + generateTypes: true, + }), + ], +}); +``` diff --git a/apps/vite-react/tsconfig.app.json b/apps/vite-react/tsconfig.app.json index f0a2350..f185aad 100644 --- a/apps/vite-react/tsconfig.app.json +++ b/apps/vite-react/tsconfig.app.json @@ -20,5 +20,5 @@ "noUnusedParameters": true, "noFallthroughCasesInSwitch": true }, - "include": ["src"] + "include": ["src", ".monicon/*.d.ts"] } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index d61cd48..ee68ea0 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -54,6 +54,18 @@ export type MoniconOptions = { * @default process.cwd() */ root?: string; + /** + * Whether to generate the types file. + * + * @default true + */ + generateTypes?: boolean; + /** + * The name of the file to output the types to. + * + * @default "types" + */ + typesFileName?: string; }; export type Icon = { @@ -69,6 +81,8 @@ const defaultOptions: Required = { collections: [], customCollections: {}, root: process.cwd(), + generateTypes: true, + typesFileName: "types", }; export const getResolveAlias = () => { @@ -102,6 +116,14 @@ export const getIconsFilePath = (opts?: MoniconOptions) => { ); }; +const getTypesFilePath = (opts?: MoniconOptions) => { + const options: Required = { ...defaultOptions, ...opts }; + + const autoGeneratedPath = getAutoGeneratedPath(options.root); + + return path.resolve(autoGeneratedPath, `${options.typesFileName}.d.ts`); +}; + export const transformIcon = (svg: string) => { const svgObject = parseSync(svg); @@ -205,9 +227,60 @@ export const loadIcons = async (opts?: MoniconOptions) => { writeIcons(loadedIcons, outputPath, options.type); + if (options.generateTypes) { + const typesOutputPath = getTypesFilePath(options); + + const iconNames = Object.keys(loadedIcons); + + writeTypes(iconNames, typesOutputPath); + } + return loadedIcons; }; +const writeTypes = (iconNames: string[], outputPath: string) => { + const iconNamesAsCode = iconNames.map((name) => `\n\t| "${name}"`).join(""); + + const code = `// This file is automatically generated by Monicon. Do not edit this file directly. +import "@monicon/icon-loader"; + +declare module "@monicon/icon-loader" { + export type MoniconIconName = ${iconNamesAsCode}; + + export type MoniconProps = { + /** + * The name of the icon to render. + * + * @example "mdi:home" + * + * For TypeScript users, you must check below for more information. + * + * @link https://monicon-docs.vercel.app/troubleshooting/typescript + */ + name: MoniconIconName; + /** + * The size of the icon. + * + * @default collection's view box size + */ + size?: number; + /** + * The color of the icon. + * + * @default "currentColor" + */ + color?: string; + /** + * The stroke width of the icon. This feature is only available for limited icon collections. + */ + strokeWidth?: number; + }; +} +`; + + fs.writeFileSync(outputPath, code); +}; + const writeIcons = ( icons: Record, outputPath: string, diff --git a/packages/icon-loader/src/index.ts b/packages/icon-loader/src/index.ts index 422951c..d618f4a 100644 --- a/packages/icon-loader/src/index.ts +++ b/packages/icon-loader/src/index.ts @@ -4,9 +4,31 @@ import { parseSync, stringify } from "svgson"; import icons from "@monicon/runtime"; export type MoniconProps = { + /** + * The name of the icon to render. + * + * @example "mdi:home" + * + * For TypeScript users, you must check below for more information. + * + * @link https://monicon-docs.vercel.app/troubleshooting/typescript + */ name: string; + /** + * The size of the icon. + * + * @default collection's view box size + */ size?: number; + /** + * The color of the icon. + * + * @default "currentColor" + */ color?: string; + /** + * The stroke width of the icon. This feature is only available for limited icon collections. + */ strokeWidth?: number; };