Last active
January 12, 2022 07:34
-
-
Save sergeysova/33061985430617c417fb90915e4f29df to your computer and use it in GitHub Desktop.
This file contains hidden or 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
#!/usr/bin/env node | |
import fs from "fs/promises"; | |
import path from "path"; | |
import { fileURLToPath } from "url"; | |
import { unified } from "unified"; | |
import rehypeParse from "rehype-parse"; | |
import { VFile } from "vfile"; | |
import { write } from "to-vfile"; | |
import generate from "@babel/generator"; | |
import t from "@babel/types"; | |
import template from "@babel/template"; | |
import change from "change-case"; | |
const __dirname = path.dirname(fileURLToPath(import.meta.url)); | |
const source = await fs.readFile( | |
path.resolve(__dirname, "./src/icons.html"), | |
"utf-8" | |
); | |
/** | |
* Chunks separated by --- | |
* Each chunks is a line with a name, on the next lines svg source | |
*/ | |
const chunks = source.split("\n---\n"); | |
const DIR_ICONS_TARGET = path.resolve(__dirname, "src/icons"); | |
await fs.mkdir(DIR_ICONS_TARGET, { recursive: true }); | |
const icons = chunks | |
.map((chunk) => { | |
const separatorPlace = chunk.indexOf("\n"); | |
const name = chunk.substr(0, separatorPlace); | |
const source = chunk.substr(separatorPlace + 1); | |
if (!name || !source) return null; | |
return { name: name.trim(), source: source.trim() }; | |
}) | |
.filter(Boolean); | |
const COMPONENT_TPL = template.default( | |
` | |
import { h } from "forest"; | |
export function %%name%%(fn?: () => void) { | |
%%content%% | |
} | |
`, | |
{ plugins: ["typescript"] } | |
); | |
const removeProperties = ['className', 'width', 'height'] | |
// TODO: icons still broken due https://github.com/rehypejs/rehype/issues/87 | |
const remapProperties = { | |
clipRule: 'clip-rule', | |
fillRule: 'fill-rule', | |
} | |
function tagToForest(tree) { | |
const children = []; | |
if (Array.isArray(tree.children)) { | |
tree.children.forEach((subTree) => { | |
const converted = tagToForest(subTree); | |
if (converted) { | |
if (Array.isArray(converted)) { | |
children.push(...converted); | |
} else { | |
children.push(converted); | |
} | |
} | |
}); | |
} | |
if (tree.type === "element") { | |
const svgRoot = tree.tagName === "svg"; | |
const properties = []; | |
if (svgRoot) { | |
properties.push({ key: "xmlns", value: "http://www.w3.org/2000/svg" }); | |
} | |
for (let property in tree.properties) { | |
const key = property in remapProperties ? remapProperties[property] : property | |
const value = tree.properties[property]; | |
if (removeProperties.includes(property)) { | |
// Just remove it | |
} | |
else if (Array.isArray(value)) { | |
properties.push({ key, value: value.join(" ") }); | |
} else { | |
properties.push({ key, value: value }); | |
} | |
} | |
const attrs = t.objectProperty( | |
t.stringLiteral("attr"), | |
t.objectExpression( | |
properties.map((p) => | |
t.objectProperty(t.stringLiteral(p.key), t.stringLiteral(p.value)) | |
) | |
) | |
); | |
if (svgRoot) { | |
children.unshift( | |
t.expressionStatement( | |
t.optionalCallExpression(t.identifier("fn"), [], true) | |
) | |
); | |
} | |
const fn = t.objectMethod( | |
"method", | |
t.identifier("fn"), | |
[], | |
t.blockStatement(children) | |
); | |
return t.expressionStatement( | |
t.callExpression(t.identifier("h"), [ | |
t.stringLiteral(tree.tagName), | |
t.objectExpression([attrs, fn]), | |
]) | |
); | |
} | |
if (tree.type === "root" && children.length > 0) { | |
return children[0]; | |
} | |
return children; | |
} | |
function forestStringify() { | |
this.Compiler = compiler; | |
function compiler(tree, file) { | |
const Name = change.pascalCase(file.stem); | |
file.extname = ".ts"; | |
const ast = COMPONENT_TPL({ | |
name: Name, | |
content: tagToForest(tree), | |
}); | |
return generate.default( | |
{ | |
type: "Program", | |
body: ast, | |
}, | |
{ | |
filename: file.basename, | |
} | |
).code; | |
} | |
} | |
const processor = unified() | |
.use(rehypeParse, { | |
emitParseErrors: true, | |
duplicateAttribute: false, | |
fragment: true, | |
space: 'svg', | |
}) | |
.use(forestStringify); | |
const tasks = icons.map(async (icon) => { | |
const fileName = change.paramCase(icon.name); | |
console.log(`Writing "${fileName}"`); | |
const file = new VFile({ | |
path: `${DIR_ICONS_TARGET}/${fileName}.html`, | |
value: icon.source, | |
}); | |
await processor.process(file).then((file) => write(file)); | |
}); | |
await tasks; | |
console.log(`Finished ${tasks.length} icons`); | |
const indexSource = icons | |
.map( | |
(icon) => `export { ${icon.name} } from './${change.paramCase(icon.name)}';` | |
) | |
.join("\n"); | |
await fs.writeFile(`${DIR_ICONS_TARGET}/index.ts`, indexSource); | |
console.log("Wrote index.ts file"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment