From 721275603b844811ab33149e843f76c363a75027 Mon Sep 17 00:00:00 2001 From: Martin Bittnerm Date: Fri, 22 Aug 2025 17:42:02 +0200 Subject: [PATCH] chore: upgrade to new material icons library --- package-lock.json | 61 +++++++++-- packages/icons/materialicons.config.json | 3 +- packages/icons/package.json | 2 +- packages/icons/src/lib/generate.ts | 128 +++++++++++++---------- 4 files changed, 127 insertions(+), 67 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52ba2358d..3fcfe255a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2422,6 +2422,7 @@ "version": "11.14.0", "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "dev": true, "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", @@ -2481,6 +2482,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2497,6 +2499,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2513,6 +2516,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2529,6 +2533,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2545,6 +2550,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2561,6 +2567,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2577,6 +2584,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2593,6 +2601,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2609,6 +2618,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2625,6 +2635,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2641,6 +2652,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2657,6 +2669,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2673,6 +2686,7 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2689,6 +2703,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2705,6 +2720,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2721,6 +2737,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2737,6 +2754,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2753,6 +2771,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2769,6 +2788,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2785,6 +2805,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2801,6 +2822,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2817,6 +2839,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2833,6 +2856,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2849,6 +2873,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2865,6 +2890,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -4135,6 +4161,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@material-design-icons/svg": { + "version": "0.14.15", + "resolved": "https://registry.npmjs.org/@material-design-icons/svg/-/svg-0.14.15.tgz", + "integrity": "sha512-6nbjwGwyJnphwQUscJAYqw1Tk6+W8KvsgOAeyVgzIFXVsHfgX5XyplTUcZ29wbcTUysMMyCUi1LYpmFKA/e61g==", + "dev": true + }, "node_modules/@mdx-js/react": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", @@ -5673,6 +5705,7 @@ "version": "8.5.3", "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.5.3.tgz", "integrity": "sha512-ZLlr2pltbj/hmC54lggJTnh09FCAJR62lIdiXNwa+V+/eJz0CfD8tfGmZGKPSmaQeZBpMwAOeRM97k2oLPF+0w==", + "dev": true, "license": "MIT", "dependencies": { "@storybook/csf": "0.1.12", @@ -5720,6 +5753,7 @@ "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -6598,6 +6632,7 @@ "version": "9.3.4", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", + "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", @@ -8293,6 +8328,7 @@ "version": "0.16.1", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz", "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.0.1" @@ -8650,6 +8686,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/better-opn/-/better-opn-3.0.2.tgz", "integrity": "sha512-aVNobHnJqLiUelTaHat9DZ1qM2w0C0Eym4LPI/3JxOnSokGVdsl1T1kN7TFvsEAD8G47A6VKQ0TVHqbBnYMJlQ==", + "dev": true, "license": "MIT", "dependencies": { "open": "^8.0.4" @@ -10561,6 +10598,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -11306,6 +11344,7 @@ "version": "0.24.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -11346,6 +11385,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, "license": "MIT", "dependencies": { "debug": "^4.3.4" @@ -12155,6 +12195,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", @@ -14443,6 +14484,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, "license": "MIT", "bin": { "is-docker": "cli.js" @@ -14810,6 +14852,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, "license": "MIT", "dependencies": { "is-docker": "^2.0.0" @@ -16314,6 +16357,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -17385,13 +17429,6 @@ "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", "license": "MIT" }, - "node_modules/material-design-icons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/material-design-icons/-/material-design-icons-3.0.1.tgz", - "integrity": "sha512-t19Z+QZBwSZulxptEu05kIm+UyfIdJY1JDwI+nx02j269m6W414whiQz9qfvQIiLrdx71RQv+T48nHhuQXOCIQ==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -21415,6 +21452,7 @@ "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "dev": true, "license": "MIT", "dependencies": { "define-lazy-prop": "^2.0.0", @@ -22232,7 +22270,7 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "devOptional": true, + "dev": true, "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -23070,6 +23108,7 @@ "version": "0.23.9", "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.9.tgz", "integrity": "sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==", + "dev": true, "license": "MIT", "dependencies": { "ast-types": "^0.16.1", @@ -23086,6 +23125,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -24148,6 +24188,7 @@ "version": "8.5.3", "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.5.3.tgz", "integrity": "sha512-2WtNBZ45u1AhviRU+U+ld588tH8gDa702dNSq5C8UBaE9PlOsazGsyp90dw1s9YRvi+ejrjKAupQAU0GwwUiVg==", + "dev": true, "license": "MIT", "dependencies": { "@storybook/core": "8.5.3" @@ -25595,6 +25636,7 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -26698,6 +26740,7 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -26899,6 +26942,7 @@ "name": "@t3n/icons", "version": "0.8.1", "devDependencies": { + "@material-design-icons/svg": "^0.14.15", "@svgr/core": "^8.1.0", "@svgr/plugin-jsx": "^8.1.0", "@svgr/plugin-prettier": "^8.1.0", @@ -26909,7 +26953,6 @@ "@types/react": "^18.3.12", "chalk": "^5.4.1", "fs-extra": "^11.3.0", - "material-design-icons": "^3.0.1", "react": "^18.3.1" }, "peerDependencies": { diff --git a/packages/icons/materialicons.config.json b/packages/icons/materialicons.config.json index 99b84cf93..a452bebaf 100644 --- a/packages/icons/materialicons.config.json +++ b/packages/icons/materialicons.config.json @@ -2,5 +2,6 @@ "renameRules": { "3dRotation": "ThreeDRotation" }, - "ignoreFolders": ["iconfont", "sprites"] + "ignoreFolders": [], + "defaultStyle": "filled" } diff --git a/packages/icons/package.json b/packages/icons/package.json index 2625df8d1..4e358b86c 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -30,7 +30,7 @@ "@types/react": "^18.3.12", "chalk": "^5.4.1", "fs-extra": "^11.3.0", - "material-design-icons": "^3.0.1", + "@material-design-icons/svg": "^0.14.15", "react": "^18.3.1" }, "sideEffects": false, diff --git a/packages/icons/src/lib/generate.ts b/packages/icons/src/lib/generate.ts index 2e433f99b..9bfebc424 100644 --- a/packages/icons/src/lib/generate.ts +++ b/packages/icons/src/lib/generate.ts @@ -16,6 +16,7 @@ interface MaterialIconsConfig { [key: string]: string; }; ignoreFolders: string[]; + defaultStyle: string; } interface IconComponent { @@ -41,7 +42,7 @@ const MATERIAL_COMPONENTS_FOLDER_PATH = path.join( ); const MATERIAL_ICONS_FOLDER_PATH = path.resolve( __dirname, - '../../../../node_modules/material-design-icons', + '../../../../node_modules/@material-design-icons/svg', ); const INDEX_FILE_PATH = path.join(COMPONENTS_FOLDER_PATH, 'index.ts'); @@ -73,13 +74,21 @@ const readDirectoryContents = async (dir: string): Promise => { const getPathEnd = (filePath: string) => (filePath.match(/[^/]+$/) || [''])[0]; -const generateComponentName = (fileName: string) => - getPathEnd(fileName) +const generateComponentName = (fileName: string) => { + let componentName = getPathEnd(fileName) .replace('.svg', '') .split(/[^A-Za-z0-9äöüß]+/) .map((word) => capitalizeString(word)) .join(''); + // Handle component names that start with numbers by prefixing with 'Icon' + if (/^\d/.test(componentName)) { + componentName = `Icon${componentName}`; + } + + return componentName; +}; + const filterFilesBySvg = (files: string[]) => files.filter((file) => /\.svg$/.test(file)); @@ -157,67 +166,74 @@ const generateIconComponents = async ( }; const generateMaterialIconComponents = async (): Promise => { - const { dirs } = await readDirectoryContents(MATERIAL_ICONS_FOLDER_PATH); - - const categoryNames = dirs - .filter( - (dirPath) => - !(materialIconsConfig as MaterialIconsConfig).ignoreFolders.filter( - (categoryName) => dirPath.indexOf(categoryName) > -1, - ).length, - ) - .map((dirPath) => getPathEnd(dirPath)); - - const categoryFiles = await Promise.all( - categoryNames.map(async (categoryName) => { - const { files } = await readDirectoryContents( - path.resolve( - MATERIAL_ICONS_FOLDER_PATH, - categoryName, - 'svg/production', - ), - ); - - return files.filter((filePath) => /24px\.svg$/.test(filePath)); - }), - ); - const svgFiles = categoryFiles.reduce( - (allFiles, files) => [...allFiles, ...files], - [], + // Use the configured default style (e.g., 'filled') to maintain backward compatibility + const defaultStyle = + (materialIconsConfig as MaterialIconsConfig).defaultStyle || 'filled'; + const styleFolderPath = path.resolve( + MATERIAL_ICONS_FOLDER_PATH, + defaultStyle, ); - return Promise.all( + console.log(`Using Material Icons style: ${defaultStyle}`); + + const { files } = await readDirectoryContents(styleFolderPath); + const svgFiles = files.filter((filePath) => /\.svg$/.test(filePath)); + + console.log(`Found ${svgFiles.length} Material Icons to process`); + + // Keep track of seen component names to avoid duplicates + const seenComponents = new Set(); + + const components = await Promise.all( svgFiles.map(async (svgPath) => { - const svg = await fs.readFile(svgPath, 'utf8'); - const componentName = [ - generateComponentName(svgPath).replace('Ic', '').replace('24px', ''), - ].map( - (name) => - (materialIconsConfig as MaterialIconsConfig).renameRules[name] || - name, - )[0]; + try { + const svg = await fs.readFile(svgPath, 'utf8'); + const fileName = getPathEnd(svgPath).replace('.svg', ''); + const baseComponentName = generateComponentName(fileName); + + // Apply rename rules if any exist + const componentName = + (materialIconsConfig as MaterialIconsConfig).renameRules[ + baseComponentName + ] || baseComponentName; + + // Skip duplicates to prevent case sensitivity issues + if (seenComponents.has(componentName.toLowerCase())) { + console.log(`Skipping duplicate component: ${componentName}`); + return null; + } + seenComponents.add(componentName.toLowerCase()); + + console.log( + `Generating ${chalk.black.bgWhite( + componentName, + )} component from Material Icons`, + ); - console.log( - `Generating ${chalk.black.bgWhite( - componentName, - )} component from Material Icons`, - ); + const reactComponent = await svgToReactComponent(svg, componentName); - const categoryName = svgPath.split('/').reverse()[3]; - const reactComponent = await svgToReactComponent(svg, componentName); - const fileDest = path.join( - MATERIAL_COMPONENTS_FOLDER_PATH, - categoryName, - `${componentName}.tsx`, - ); + // Store components directly in the material folder (no subfolders) to maintain compatibility + const fileDest = path.join( + MATERIAL_COMPONENTS_FOLDER_PATH, + `${componentName}.tsx`, + ); - return { - name: componentName, - path: fileDest, - reactComponent, - }; + return { + name: componentName, + path: fileDest, + reactComponent, + }; + } catch (error) { + console.error(`Error processing ${svgPath}:`, error); + return null; + } }), ); + + // Filter out any failed components + return components.filter( + (component) => component !== null, + ) as IconComponent[]; }; const writeComponents = async (components: IconComponents) => {