diff --git a/.changeset/ninety-bees-exercise.md b/.changeset/ninety-bees-exercise.md new file mode 100644 index 0000000..8dcdb7f --- /dev/null +++ b/.changeset/ninety-bees-exercise.md @@ -0,0 +1,22 @@ +--- +"@monicon/loader": patch +"@monicon/core": patch +"@monicon/qwik-app": patch +"@monicon/babel-plugin": patch +"@monicon/esbuild": patch +"@monicon/icon-loader": patch +"@monicon/metro": patch +"@monicon/native": patch +"@monicon/nuxt": patch +"@monicon/qwik": patch +"@monicon/react": patch +"@monicon/rollup": patch +"@monicon/rspack": patch +"@monicon/svelte": patch +"@monicon/typescript-config": patch +"@monicon/vite": patch +"@monicon/vue": patch +"@monicon/webpack": patch +--- + +implement custom loaders diff --git a/apps/docs/pages/_meta.tsx b/apps/docs/pages/_meta.tsx index 5e8a645..92140d8 100644 --- a/apps/docs/pages/_meta.tsx +++ b/apps/docs/pages/_meta.tsx @@ -1,6 +1,7 @@ export default { index: "Introduction", installation: "Installation", + customization: "Customization", troubleshooting: "Troubleshooting", contact: { title: "Contact ↗", diff --git a/apps/docs/pages/customization/_meta.tsx b/apps/docs/pages/customization/_meta.tsx new file mode 100644 index 0000000..68b8c2a --- /dev/null +++ b/apps/docs/pages/customization/_meta.tsx @@ -0,0 +1,6 @@ +export default { + "local-collections": "Local Collections", + "json-collections": "JSON Collections", + "remote-collections": "Remote Collections", + "custom-loader": "Custom Loader", +}; diff --git a/apps/docs/pages/customization/custom-loader.mdx b/apps/docs/pages/customization/custom-loader.mdx new file mode 100644 index 0000000..d82fcfc --- /dev/null +++ b/apps/docs/pages/customization/custom-loader.mdx @@ -0,0 +1,48 @@ +# Custom Loader + +Custom loaders in Monicon allow you to define your own logic for providing SVG icons. This is especially useful when you want to generate icons dynamically or load them from a non-standard source. Below is an example of how to write and use a custom loader. + +```ts +import { Loader } from "@monicon/loader"; + +export const fooLoader: Loader = () => async () => { + return { + bar: '', + }; +}; +``` + +```ts filename="vite.config.ts" +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import monicon from "@monicon/vite"; + +export default defineConfig({ + plugins: [ + react(), + monicon({ + customCollections: { + foo: fooLoader(), + }, + }), + ], +}); +``` + +### Usage + +Once your custom loader is set up, you can use the icons it provides in your React components: + +```tsx filename="src/App.tsx" +import { Monicon } from "@monicon/react"; + +function App() { + return ( +
+ +
+ ); +} + +export default App; +``` diff --git a/apps/docs/pages/customization/json-collections.mdx b/apps/docs/pages/customization/json-collections.mdx new file mode 100644 index 0000000..6c7b7df --- /dev/null +++ b/apps/docs/pages/customization/json-collections.mdx @@ -0,0 +1,51 @@ +# JSON Collections + +JSON Collections in Monicon allow you to define custom icons from JSON files or URLs. These JSON files typically map icon names to their corresponding SVG data, making it easy to include and manage custom icon sets in your project. In the example above, the loadJSONCollection function fetches icons from a hosted JSON file and integrates them into your application. + +```ts filename="vite.config.ts" +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import monicon from "@monicon/vite"; +import { loadJSONCollection } from "@monicon/loader"; + +export default defineConfig({ + plugins: [ + react(), + monicon({ + customCollections: { + json: loadJSONCollection( + "https://gist.githubusercontent.com/oktaysenkan/39681ecdb066dc44c52fa840dacc7562/raw/6aa7b8f8bf9d806742be0e1c4759809391d00bcd/icons.json" + ), + }, + }), + ], +}); +``` + +### Example JSON File + +Here is an example JSON file you can use for a Monicon JSON Collection. This file maps an icon name (network) to its corresponding SVG markup: + +```json filename="https://gist.githubusercontent.com/oktaysenkan/39681ecdb066dc44c52fa840dacc7562/raw/6aa7b8f8bf9d806742be0e1c4759809391d00bcd/icons.json" +{ + "network": "" +} +``` + +### Usage + +You can now use Monicon in your React components. Here’s an example of how to use Monicon in a React component. + +```tsx filename="src/App.tsx" +import { Monicon } from "@monicon/react"; + +function App() { + return ( +
+ +
+ ); +} + +export default App; +``` diff --git a/apps/docs/pages/customization/local-collections.mdx b/apps/docs/pages/customization/local-collections.mdx new file mode 100644 index 0000000..1869dca --- /dev/null +++ b/apps/docs/pages/customization/local-collections.mdx @@ -0,0 +1,55 @@ +# Local Collections + +Local Collections in Monicon allow you to use icons stored locally on your file system. These collections can be loaded using the `loadLocalCollection` function, which points to the directory containing your SVG files. This setup is ideal for projects where icons are managed locally, providing a quick and flexible way to integrate custom icon sets. + +```ts filename="vite.config.ts" +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import monicon from "@monicon/vite"; +import { loadLocalCollection } from "@monicon/loader"; + +export default defineConfig({ + plugins: [ + react(), + monicon({ + customCollections: { + local: loadLocalCollection("assets/icons"), + }, + }), + ], +}); +``` + +### Example Directory Structure + +For the above configuration, your assets/icons directory might look like this: + +``` +assets/ +└── icons/ + ├── folder.svg + ├── user.svg + └── network.svg +``` + +Each SVG file in the directory is automatically assigned a name based on its filename (e.g., `folder` for `folder.svg`). + +### Usage + +You can now use Monicon in your React components. Here’s an example of how to use an icon from your local collection in a React component: + +```tsx filename="src/App.tsx" +import { Monicon } from "@monicon/react"; + +function App() { + return ( +
+ + + +
+ ); +} + +export default App; +``` diff --git a/apps/docs/pages/customization/remote-collections.mdx b/apps/docs/pages/customization/remote-collections.mdx new file mode 100644 index 0000000..3363fc8 --- /dev/null +++ b/apps/docs/pages/customization/remote-collections.mdx @@ -0,0 +1,43 @@ +# Remote Collections + +Remote Collections in Monicon allow you to fetch and use icons directly from remote URLs. These collections are configured using the `loadRemoteCollection` function, which maps icon names to their respective URLs. This setup is perfect for integrating third-party or dynamically fetched icons into your project. + +```ts filename="vite.config.ts" +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; +import monicon from "@monicon/vite"; +import { loadRemoteCollection } from "@monicon/loader"; + +export default defineConfig({ + plugins: [ + react(), + monicon({ + customCollections: { + remote: loadRemoteCollection({ + download: "https://api.iconify.design/lucide:cloud-download.svg", + attachment: "https://api.iconify.design/ri:attachment-2.svg", + }), + }, + }), + ], +}); +``` + +### Usage + +You can now use Monicon in your React components. Here’s an example of how to use icons from your remote collection in a React component: + +```tsx filename="src/App.tsx" +import { Monicon } from "@monicon/react"; + +function App() { + return ( +
+ + +
+ ); +} + +export default App; +``` diff --git a/apps/docs/pages/index.mdx b/apps/docs/pages/index.mdx index 778f175..9810efc 100644 --- a/apps/docs/pages/index.mdx +++ b/apps/docs/pages/index.mdx @@ -5,6 +5,7 @@ Monicon is an easy-to-use icon library that makes adding icons to your projects ## Why Use Monicon? - **Huge Icon Library**: Get access to over 200,000 icons from famous sets like Material Design, Feather, and Font Awesome. +- **Custom Icons**: You can create custom icons and use them in your projects. Monicon makes it easy to add your own icons. - **Works with Modern Tools**: Monicon supports tools like Vite, Webpack, Rollup, and others, so it's ready for any project. - **Fast and Efficient**: Monicon loads icons quickly to keep your project running smoothly. No flickering or lagging. - **Easy to Use**: Works with React, Vue, Svelte, Next.js, and other popular frameworks, making icon integration simple. You can discover icons on the [Icones](https://icones.js.org/) website. @@ -12,4 +13,4 @@ Monicon is an easy-to-use icon library that makes adding icons to your projects - **Collaboration**: Monicon helps you collaborate with designers to speed up the design process and get better results. [Iconify Figma Plugin](https://www.figma.com/community/plugin/735098390272716381/iconify) allows you to use icons directly in Figma. - **Free and Open Source**: Monicon is free to use and open source, so you can use it in any project without restrictions. -No matter which framework you use, Monicon makes adding and managing icons easy. Check out the documentation to see how Monicon can help improve your next project with great-looking icons! \ No newline at end of file +No matter which framework you use, Monicon makes adding and managing icons easy. Check out the documentation to see how Monicon can help improve your next project with great-looking icons! diff --git a/apps/docs/pages/installation/_meta.tsx b/apps/docs/pages/installation/_meta.tsx index 6681dec..a64640d 100644 --- a/apps/docs/pages/installation/_meta.tsx +++ b/apps/docs/pages/installation/_meta.tsx @@ -7,6 +7,7 @@ export default { vue: "Vue", nuxt: "Nuxt", svelte: "Svelte", + "react-webpack": "React (Webpack)", "react-rollup": "React (Rollup)", "react-rspack": "React (Rspack)", }; diff --git a/apps/docs/pages/installation/react-webpack.mdx b/apps/docs/pages/installation/react-webpack.mdx new file mode 100644 index 0000000..0ac4a14 --- /dev/null +++ b/apps/docs/pages/installation/react-webpack.mdx @@ -0,0 +1,79 @@ +import { Steps, Callout } from "nextra/components"; + +# Install Monicon with React (Webpack) + +Setting up Monicon with React and Webpack is a straightforward process. This guide will walk you through the installation and configuration steps to get started with Monicon in your React project. + + + +## Install + +To get started, you’ll need to install the necessary dependencies for Monicon. In your project directory, run the following command to install the dependencies. + +```sh npm2yarn +npm i @monicon/react @monicon/webpack +``` + +Now you should install the development dependency `@iconify/json` for the icon sets. This package provides a comprehensive collection of icons that can be easily integrated into your project. + +```sh npm2yarn +npm i -D @iconify/json + +# or specific icon sets +npm i -D @iconify-json/mdi @iconify-json/feather +``` + +## Configure Webpack + +Now that the dependencies are installed, you’ll need to configure Webpack to use Monicon. + +```js filename="webpack.config.js" +module.exports = { + plugins: [ + new MoniconPlugin({ + icons: [ + "mdi:home", + "feather:activity", + "logos:active-campaign", + "lucide:badge-check", + ], + // Load all icons from the listed collections + collections: ["radix-icons"], + }), + ], +}; +``` + + +The `icons` array in the `monicon` plugin configuration specifies the icon sets you want to use in your project. You can add more icon sets as needed. + +For a complete list of available icon sets, refer to the [Icones](https://icones.js.org/) website. + + + +## Usage + +You can now use Monicon in your React components. Here’s an example of how to use Monicon in a React component. + +```tsx filename="src/App.tsx" +import { Monicon } from "@monicon/react"; + +function App() { + return ( +
+ + + + +
+ ); +} + +export default App; +``` + +## Next Steps + +You’ve successfully set up Monicon with React and Webpack! You can now explore more icon sets and customize your usage further. + +
diff --git a/apps/docs/public/sitemap-0.xml b/apps/docs/public/sitemap-0.xml index 396fa0f..95241a7 100644 --- a/apps/docs/public/sitemap-0.xml +++ b/apps/docs/public/sitemap-0.xml @@ -1,16 +1,17 @@ -https://monicon-docs.vercel.app2024-10-28T23:26:46.211Zdaily0.7 -https://monicon-docs.vercel.app/installation/nextjs2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/nuxt2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/qwik2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/react2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/react-native2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/react-rollup2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/react-rspack2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/remix2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/svelte2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/installation/vue2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/troubleshooting/module-resolution2024-10-28T23:26:46.212Zdaily0.7 -https://monicon-docs.vercel.app/troubleshooting/monorepo2024-10-28T23:26:46.212Zdaily0.7 +https://monicon-docs.vercel.app2024-11-16T16:35:45.476Zdaily0.7 +https://monicon-docs.vercel.app/installation/nextjs2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/nuxt2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/qwik2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/react2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/react-native2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/react-rollup2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/react-rspack2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/remix2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/svelte2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/installation/vue2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/troubleshooting/bundle-size2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/troubleshooting/module-resolution2024-11-16T16:35:45.477Zdaily0.7 +https://monicon-docs.vercel.app/troubleshooting/monorepo2024-11-16T16:35:45.477Zdaily0.7 \ No newline at end of file diff --git a/apps/vite-react/src/App.tsx b/apps/vite-react/src/App.tsx index 7f11d79..eec6089 100644 --- a/apps/vite-react/src/App.tsx +++ b/apps/vite-react/src/App.tsx @@ -5,11 +5,10 @@ import { Monicon } from "@monicon/react"; function App() { return (
- - - - - + + + +
); } diff --git a/apps/vite-react/vite.config.ts b/apps/vite-react/vite.config.ts index 4e4223f..c8138f4 100644 --- a/apps/vite-react/vite.config.ts +++ b/apps/vite-react/vite.config.ts @@ -1,8 +1,12 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import monicon from "@monicon/vite"; +import { + loadLocalCollection, + loadJSONCollection, + loadRemoteCollection, +} from "@monicon/loader"; -// https://vitejs.dev/config/ export default defineConfig({ plugins: [ react(), @@ -18,6 +22,16 @@ export default defineConfig({ "lucide:badge-check", ], collections: ["lucide"], + customCollections: { + local: loadLocalCollection("../../packages/icons"), + json: loadJSONCollection( + "https://gist.githubusercontent.com/oktaysenkan/39681ecdb066dc44c52fa840dacc7562/raw/6aa7b8f8bf9d806742be0e1c4759809391d00bcd/icons.json" + ), + remote: loadRemoteCollection({ + download: "https://api.iconify.design/lucide:cloud-download.svg", + attachment: "https://api.iconify.design/ri:attachment-2.svg", + }), + }, outputFileName: "vite-react", }), ], diff --git a/packages/core/package.json b/packages/core/package.json index 3b92331..f620c6b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -32,12 +32,14 @@ "devDependencies": { "@iconify/json": "^2.2.251", "@iconify/types": "^2.0.0", + "@monicon/loader": "*", "@monicon/typescript-config": "0.0.152", "tsup": "^8.0.1", "typescript": "^5.3.3" }, "dependencies": { "@iconify/utils": "^2.1.33", + "fuuu": "^0.0.8", "svgson": "^5.3.1" } } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4fdc553..acf1b32 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,10 +1,12 @@ import { stringToIcon } from "@iconify/utils/lib/icon/name"; import { loadCollectionFromFS } from "@iconify/utils/lib/loader/fs"; import { loadNodeIcon } from "@iconify/utils/lib/loader/node-loader"; +import type { Loader } from "@monicon/loader"; import fs from "fs"; import path, { dirname } from "path"; import { parseSync } from "svgson"; import { fileURLToPath } from "url"; +import * as f from "fuuu"; import { toPx } from "./utils"; @@ -18,7 +20,7 @@ export type MoniconOptions = { * * You can explore available icons at https://icones.js.org */ - icons: string[]; + icons?: string[]; /** * The names of the collections to load. All icons from the collections will be loaded. * @@ -27,6 +29,10 @@ export type MoniconOptions = { * You can explore available collections at https://icones.js.org */ collections?: string[]; + /** + * Custom collections to load icons from different sources. + */ + customCollections?: Record>; /** * The name of the file to output the icons to. The file extension will be added automatically based on the type. */ @@ -54,6 +60,7 @@ const defaultOptions: Required = { type: "cjs", icons: [], collections: [], + customCollections: {}, }; export const getResolveAlias = () => { @@ -121,7 +128,16 @@ export const loadIcon = async (iconName: string) => { return; } - return transformIcon(svg); + const transformedIcon = f.syncSafe(() => transformIcon(svg)); + + if (transformedIcon.error) { + console.warn( + `[Monicon] The icon "${iconName}" could not be transformed. This icon might not be in the correct format.` + ); + return; + } + + return transformedIcon.data; }; const getIconsFromCollection = async (collectionName: string) => { @@ -165,6 +181,20 @@ export const loadIcons = async (opts?: MoniconOptions) => { loadedIcons[iconName] = icon; } + const loaders = Object.entries(options.customCollections); + + for await (const [loaderName, loader] of loaders) { + const loaderResult = await loader(); + + for await (const [iconName, svg] of Object.entries(loaderResult)) { + const icon = transformIcon(svg); + + const name = `${loaderName}:${iconName}`; + + loadedIcons[name] = icon; + } + } + const outputPath = getIconsFilePath(options); writeIcons(loadedIcons, outputPath, options.type); diff --git a/packages/icons/folder.svg b/packages/icons/folder.svg new file mode 100644 index 0000000..4cf30cd --- /dev/null +++ b/packages/icons/folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/nested/Folder.svg b/packages/icons/nested/Folder.svg new file mode 100644 index 0000000..4cf30cd --- /dev/null +++ b/packages/icons/nested/Folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/loader/.gitignore b/packages/loader/.gitignore new file mode 100644 index 0000000..cf309c2 --- /dev/null +++ b/packages/loader/.gitignore @@ -0,0 +1,28 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +node_modules +.pnp +.pnp.js + + +# misc +.DS_Store +*.pem + +# build +dist + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local + +# turbo +.turbo \ No newline at end of file diff --git a/packages/loader/package.json b/packages/loader/package.json new file mode 100644 index 0000000..7b9d0e2 --- /dev/null +++ b/packages/loader/package.json @@ -0,0 +1,36 @@ +{ + "name": "@monicon/loader", + "version": "0.0.152", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "files": [ + "dist" + ], + "exports": { + ".": { + "import": "./dist/index.mjs", + "require": "./dist/index.js", + "types": "./dist/index.d.ts" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "clean": "rm -rf dist" + }, + "devDependencies": { + "@monicon/typescript-config": "0.0.152", + "tsup": "^8.0.1", + "typescript": "^5.3.3" + }, + "dependencies": { + "fuuu": "^0.0.8", + "glob": "^11.0.0", + "radashi": "^12.2.3", + "slugify": "^1.6.6", + "svgson": "^5.3.1" + } +} diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts new file mode 100644 index 0000000..34c1141 --- /dev/null +++ b/packages/loader/src/index.ts @@ -0,0 +1,180 @@ +import { glob } from "glob"; +import { readFileSync } from "fs"; +import slugify from "slugify"; +import * as _ from "radashi"; +import path from "path"; +import * as f from "fuuu"; +import { parseSync } from "svgson"; + +slugify.extend({ "/": "-" }); + +export type LoaderResult = Record; + +export type Loader = ( + input: T +) => () => Promise | LoaderResult; + +export type JSONCollectionLoaderOptions = + | string + | { + url: string; + options?: RequestInit; + }; + +type Content = { + name: string; + content: string; +}; + +const isValidSvg = (svg: string) => { + const parsed = f.syncSafe(() => parseSync(svg)); + + return !parsed.error; +}; + +export const loadJSONCollection: Loader = + (input) => async () => { + const url = typeof input === "string" ? input : input.url; + + const options = typeof input === "string" ? undefined : input.options; + + const response = await f.safe(() => fetch(url, options)); + + if (response.error) { + console.warn(`[Monicon] Request to "${url}" failed.`); + return {}; + } + + const content = await f.safe(() => response.data.json()); + + if (content.error) { + console.warn(`[Monicon] Unable to parse response from "${url}".`); + return {}; + } + + Object.entries(content.data).forEach(([key, value]) => { + if (!isValidSvg(value)) { + console.warn( + `[Monicon] The response from "${url}" is not a valid SVG.` + ); + delete content.data[key]; + } + }); + + return content.data as LoaderResult; + }; + +export type RemoteCollectionLoaderOptions = Record< + string, + | string + | { + url: string; + options?: RequestInit; + } +>; + +export const loadRemoteCollection: Loader = + (input) => async () => { + const asArray = await Promise.all( + Object.entries(input).map(async ([key, value]) => { + const url = typeof value === "string" ? value : value.url; + + const options = typeof value === "string" ? undefined : value.options; + + const response = await f.safe(() => fetch(url, options)); + + if (response.error) { + console.warn(`[Monicon] Request to "${url}" failed.`); + return; + } + + const content = await f.safe(() => response.data.text()); + + if (content.error) { + console.warn(`[Monicon] Unable to parse response from "${url}".`); + return; + } + + if (!isValidSvg(content.data)) { + console.warn( + `[Monicon] The response from "${url}" is not a valid SVG.` + ); + return; + } + + return { name: key, content: content.data } as Content; + }) + ); + + const asArrayFiltered = asArray.filter((item) => !!item) as Content[]; + + const asObject = _.objectify( + asArrayFiltered, + (item) => item.name, + (item) => item.content + ); + + return asObject as LoaderResult; + }; + +export type LocalCollectionLoaderOptions = + | string + | { + directory: string; + }; + +export const loadLocalCollection: Loader = + (input) => () => { + const directory = typeof input === "string" ? input : input.directory; + + if (!directory) throw new Error("directory is required"); + + const directoryAbsolutePath = path.resolve(directory); + + const filePaths = glob.sync(`${directory}/**/*.svg`); + + if (!filePaths.length) { + console.warn( + `[Monicon] No files were found in the directory "${directory}".` + ); + } + + const files = filePaths.map((filePath) => { + const content = f.syncSafe(() => readFileSync(filePath, "utf-8")); + + if (content.error) { + console.warn( + `[Monicon] The file "${filePath}" was not found. This file might not exist, or the required icon file might not be in the correct format.` + ); + return; + } + + if (!isValidSvg(content.data)) { + console.warn(`[Monicon] The file "${filePath}" is not a valid SVG.`); + return; + } + + const fileAbsolutePath = path.resolve(filePath); + + const relativePath = fileAbsolutePath.replace( + `${directoryAbsolutePath}/`, + "" + ); + + const fileNameWithoutExtension = relativePath.slice(0, -4).trim(); + + const slugified = slugify(fileNameWithoutExtension, { lower: true }); + + return { name: slugified, content: content.data } as Content; + }); + + const filesFiltered = files.filter((item) => !!item) as Content[]; + + const asObject = _.objectify( + filesFiltered, + (item) => item.name, + (item) => item.content + ); + + return asObject; + }; diff --git a/packages/loader/tsconfig.json b/packages/loader/tsconfig.json new file mode 100644 index 0000000..073ce66 --- /dev/null +++ b/packages/loader/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@monicon/typescript-config/react-native-library", + "include": ["."], + "exclude": ["dist", "build", "node_modules"], + "compilerOptions": { + "strict": true + } +} diff --git a/packages/loader/tsup.config.ts b/packages/loader/tsup.config.ts new file mode 100644 index 0000000..0ee6a6b --- /dev/null +++ b/packages/loader/tsup.config.ts @@ -0,0 +1,9 @@ +import { defineConfig, Options } from "tsup"; + +export default defineConfig((options: Options) => ({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + external: ["@monicon/runtime"], + ...options, +})); diff --git a/yarn.lock b/yarn.lock index b9a5269..608558b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13009,6 +13009,11 @@ functions-have-names@^1.2.3: resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +fuuu@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/fuuu/-/fuuu-0.0.8.tgz#c2b7ecb59b22348ad3786600c9e95e6af2f3c11a" + integrity sha512-zQFHWeevoaQRBtrXoHJ/qz/HvhkSz5+ZPu1+sIqXQxfh/MlB0Mi1G+1wAfaZS6z4mRtp9BgPqyYOWwBTka96SA== + gauge@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" @@ -13239,6 +13244,18 @@ glob@^10.3.10: minipass "^7.1.2" path-scurry "^1.11.1" +glob@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.0.tgz#6031df0d7b65eaa1ccb9b29b5ced16cea658e77e" + integrity sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g== + dependencies: + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + glob@^6.0.1: version "6.0.4" resolved "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz" @@ -15059,6 +15076,13 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.0.2.tgz#11f9468a3730c6ff6f56823a820d7e3be9bef015" + integrity sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw== + dependencies: + "@isaacs/cliui" "^8.0.2" + javascript-stringify@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" @@ -15896,6 +15920,11 @@ lru-cache@^10.2.0: resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz" integrity sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ== +lru-cache@^11.0.0: + version "11.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" + integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -17547,6 +17576,13 @@ minimatch@9.0.5, minimatch@^9.0.0, minimatch@^9.0.3: dependencies: brace-expansion "^2.0.1" +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^5.0.1, minimatch@^5.1.0: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" @@ -19110,6 +19146,14 @@ path-scurry@^1.11.1, path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-to-regexp@0.1.10: version "0.1.10" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz" @@ -20042,6 +20086,11 @@ radash@^12.1.0: resolved "https://registry.npmjs.org/radash/-/radash-12.1.0.tgz" integrity sha512-b0Zcf09AhqKS83btmUeYBS8tFK7XL2e3RvLmZcm0sTdF1/UUlHSsjXdCcWNxe7yfmAlPve5ym0DmKGtTzP6kVQ== +radashi@^12.2.3: + version "12.2.3" + resolved "https://registry.yarnpkg.com/radashi/-/radashi-12.2.3.tgz#0af46bd25d9e2fd35e2923364494cdfb49776b44" + integrity sha512-KenCSk3vdquRkVwjQOwj1bdazsskNtpKTqnxR8CGFw42BqmIoW+lMRtT+qOZTkvzXHTbRnw8gL9mpDehAHENEQ== + radix3@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/radix3/-/radix3-1.1.2.tgz#fd27d2af3896c6bf4bcdfab6427c69c2afc69ec0" @@ -21736,9 +21785,9 @@ slice-ansi@^2.0.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" -slugify@^1.3.4: +slugify@^1.3.4, slugify@^1.6.6: version "1.6.6" - resolved "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz" + resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.6.6.tgz#2d4ac0eacb47add6af9e04d3be79319cbcc7924b" integrity sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw== smob@^1.0.0: