-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
perf(icons): optimize icons, scripts, merge declarations
- Loading branch information
Showing
10 changed files
with
69 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,173 +1,79 @@ | ||
/* eslint-disable no-console */ | ||
import fs from "node:fs"; | ||
import path from "node:path"; | ||
import fs from "node:fs/promises"; | ||
import { parse, resolve } from "node:path"; | ||
import { transform } from "@svgr/core"; | ||
|
||
import { generateComponent } from "./generateComponent"; | ||
import { extractColors, extractSize, replaceFill } from "./utils"; | ||
|
||
// Resolve arguments | ||
const inputPath = "assets"; | ||
const outputPath = "src"; | ||
|
||
const componentOutputFolder = outputPath | ||
? path.resolve(process.cwd(), outputPath) | ||
: path.resolve(process.cwd()); | ||
|
||
const knownSubfolders: Record<string, boolean> = {}; | ||
|
||
const transformToJsx = (svgCode: string) => { | ||
return transform.sync(svgCode, { | ||
const transformToJsx = (fileData: string, iconName: string) => { | ||
const svgJsx = transform.sync(fileData, { | ||
plugins: ["@svgr/plugin-jsx"], | ||
jsxRuntime: "automatic", | ||
// we only care about the JSX part of the generated React component | ||
template: (vars, { tpl }) => tpl`${vars.jsx}`, | ||
}); | ||
|
||
// TODO: simplify this logic | ||
const viewBox = extractSize(svgJsx); | ||
const colorArray = extractColors(svgJsx); | ||
const jsxSvgFixedColors = replaceFill(svgJsx, colorArray) | ||
// remove svg tag, keeping only the content | ||
.replace(/<svg.*?>(.*?)<\/svg>;/s, "$1"); | ||
|
||
// Wrap it up in a React component | ||
return generateComponent(jsxSvgFixedColors, iconName, colorArray, viewBox); | ||
}; | ||
|
||
const writeFile = (processedSVG: string, fileName: string, subFolder = ".") => { | ||
fs.mkdirSync(path.resolve(componentOutputFolder, subFolder), { | ||
recursive: true, | ||
}); | ||
/** Converts all SVGs in `inputDir` to JSX & writes result to `outputFile` */ | ||
async function convertSvgFiles(inputDir: string, outputFile: string) { | ||
const files = await fs.readdir(resolve(inputDir), { withFileTypes: true }); | ||
const svgFiles = files.filter((f) => f.isFile() && f.name.endsWith(".svg")); | ||
|
||
const file = path.resolve( | ||
componentOutputFolder, | ||
subFolder, | ||
`${fileName}.tsx`, | ||
const data = await Promise.all( | ||
svgFiles.map(async (f) => ({ | ||
name: `${parse(f.name).name}`.replace(/[-.]g/, ""), | ||
data: await fs.readFile(resolve(inputDir, f.name), "utf-8"), | ||
})), | ||
); | ||
|
||
fs.writeFile(file, processedSVG, { flag: "w" }, (err) => { | ||
if (err) { | ||
console.error(`Output file ${file} not writable ${err.code}`); | ||
} | ||
}); | ||
const headers = `import { createHvIcon } from "./IconBase";`; | ||
const output = data.map((f) => transformToJsx(f.data, f.name)).join(""); | ||
|
||
const exportName = fileName.split(".").join(""); | ||
const exportString = `export { ${exportName} } from "./${fileName}";\n`; | ||
|
||
if (subFolder === ".") { | ||
fs.appendFile( | ||
path.resolve(componentOutputFolder, `icons.ts`), | ||
exportString, | ||
() => {}, | ||
); | ||
} else { | ||
fs.appendFile( | ||
path.resolve(componentOutputFolder, subFolder, `index.ts`), | ||
exportString, | ||
() => {}, | ||
); | ||
} | ||
|
||
if (!knownSubfolders[subFolder]) { | ||
knownSubfolders[subFolder] = true; | ||
|
||
if (subFolder === ".") { | ||
fs.appendFile( | ||
path.resolve(componentOutputFolder, `index.ts`), | ||
[ | ||
`export * from "./IconBase";`, | ||
`export * from "./IconSprite";`, | ||
"\n", | ||
`export * from "./icons";`, | ||
`import * as icons from "./icons";`, | ||
`export { icons };`, | ||
"\n", | ||
].join("\n"), | ||
() => {}, | ||
); | ||
} else { | ||
const subFolderName = subFolder.replace("./", ""); | ||
fs.appendFile( | ||
path.resolve(componentOutputFolder, subFolder, "..", `index.ts`), | ||
[ | ||
`export * from "${subFolder}";`, | ||
`import * as ${subFolderName} from "${subFolder}";`, | ||
`export { ${subFolderName} };`, | ||
"\n", | ||
].join("\n"), | ||
() => {}, | ||
); | ||
} | ||
} | ||
}; | ||
await fs.writeFile(resolve(outputFile), `${headers}\n${output}`); | ||
} | ||
|
||
const runUtil = ( | ||
fileToRead: string, | ||
iconName: string, | ||
subFolder = ".", | ||
depth = 0, | ||
) => { | ||
fs.readFile(fileToRead, "utf8", (err, fileData) => { | ||
if (err) { | ||
console.error(err); | ||
return; | ||
} // exit early | ||
|
||
let output = transformToJsx(fileData); | ||
|
||
const viewBox = extractSize(output); | ||
const colorArray = extractColors(output); | ||
|
||
output = replaceFill(output, colorArray) | ||
// remove svg tag, keeping only the content | ||
.replace(/<svg.*?>(.*?)<\/svg>;/s, "$1"); | ||
|
||
// Wrap it up in a React component | ||
output = generateComponent( | ||
output, | ||
iconName, | ||
colorArray, | ||
viewBox, | ||
`${".".repeat(depth + 1)}`, | ||
); | ||
writeFile(output, iconName, subFolder); | ||
}); | ||
}; | ||
const makeHeaders = (name: string) => ` | ||
export * from "./${name}"; | ||
import * as ${name} from "./${name}"; | ||
export { ${name} }; | ||
`; | ||
|
||
const isFolder = (dirPath: string) => | ||
fs.existsSync(dirPath) && fs.lstatSync(dirPath).isDirectory(); | ||
async function main() { | ||
const inputPath = "assets"; | ||
const outputPath = "src"; | ||
|
||
const processFile = (file: string, subFolder = ".", depth = 0) => { | ||
const extension = path.extname(file); | ||
const fileName = path.basename(file, extension); | ||
await fs.mkdir(resolve(outputPath), { recursive: true }); | ||
|
||
if (extension === ".svg") { | ||
const componentName = fileName.replace(/[-.]g/, ""); | ||
runUtil(file, componentName, subFolder, depth); | ||
} | ||
}; | ||
await convertSvgFiles(inputPath, `${outputPath}/icons.tsx`); | ||
|
||
const runUtilForJustFilesInDir = ( | ||
folder: string, | ||
subFolder = ".", | ||
depth = 0, | ||
) => { | ||
fs.readdir(folder, (err, files) => { | ||
if (err) { | ||
console.log(err); | ||
return; | ||
} // Get out early if not found | ||
|
||
files.forEach((file) => { | ||
const filePath = path.resolve(folder, file); | ||
if (!isFolder(filePath)) { | ||
processFile(filePath, subFolder, depth); | ||
} else { | ||
runUtilForJustFilesInDir( | ||
filePath, | ||
`${subFolder}/${path.basename(file)}`, | ||
depth + 1, | ||
); | ||
} | ||
}); | ||
}); | ||
}; | ||
const files = await fs.readdir(inputPath, { withFileTypes: true }); | ||
const subDirs = files.filter((f) => f.isDirectory()).map((f) => f.name); | ||
|
||
await Promise.all( | ||
subDirs.map((dir) => | ||
convertSvgFiles(`${inputPath}/${dir}`, `${outputPath}/${dir}.tsx`), | ||
), | ||
); | ||
|
||
fs.mkdir(outputPath, { recursive: true }, (err) => { | ||
if (err) throw err; | ||
}); | ||
const allDirs = ["icons", ...subDirs]; | ||
|
||
fs.writeFile(path.resolve(process.cwd(), outputPath, `index.ts`), "", () => {}); | ||
const indexFile = ` | ||
export * from "./IconBase"; | ||
export * from "./IconSprite"; | ||
${allDirs.map(makeHeaders).join("")} | ||
`; | ||
await fs.writeFile(resolve("src/index.ts"), indexFile); | ||
} | ||
|
||
runUtilForJustFilesInDir(`${process.cwd()}/${inputPath}`); | ||
main(); |
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
module.exports = { | ||
export default { | ||
multipass: true, | ||
quiet: "true", | ||
plugins: [ | ||
|