Skip to content

Commit

Permalink
feat: metadata, custom template with icon data (#50)
Browse files Browse the repository at this point in the history
* feat: metadata, custom template with icon data, preview json  icon data in plugin

* changeset
  • Loading branch information
junghyeonsu authored Oct 18, 2024
1 parent 55cbf31 commit fc5f4ae
Show file tree
Hide file tree
Showing 45 changed files with 5,160 additions and 5,820 deletions.
7 changes: 7 additions & 0 deletions .changeset/fifty-timers-develop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@icona/generator": minor
"@icona/types": minor
---

- Add `metadata` system
- Add custom template with `IconaIconData` in React Config
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
39 changes: 35 additions & 4 deletions figma-plugin/plugin-src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type TargetNode =
type ExtractedNode = {
id: string;
name: string;
description?: string;
};

const makeComponentName = ({
Expand Down Expand Up @@ -43,6 +44,7 @@ const makeComponentName = ({
const findComponentInNode = (
node: TargetNode,
setName?: string,
description?: string,
): ExtractedNode | ExtractedNode[] => {
switch (node.type) {
case "FRAME":
Expand All @@ -57,13 +59,13 @@ const findComponentInNode = (
separator: "_",
});

return { id: node.id, name: svgName };
return { id: node.id, name: svgName, description };
}

case "COMPONENT_SET": {
return node.children.flatMap((child: any) =>
findComponentInNode(child, node.name),
);
return node.children.flatMap((child: any) => {
return findComponentInNode(child, node.name, node.description);
});
}

default: {
Expand All @@ -90,17 +92,46 @@ export function getAssetFramesInFrame(targetFrame: FrameNode): ExtractedNode[] {
return targetNodes.filter((component) => component);
}

function createRegexWithDelimiters(
startDelimiter: string,
endDelimiter: string,
): RegExp {
// 특수 문자 이스케이프 처리
const escapeRegExp = (string: string) =>
string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");

const start = escapeRegExp(startDelimiter);
const end = escapeRegExp(endDelimiter);

return new RegExp(`${start}(.*?)${end}`);
}

export async function getSvgFromExtractedNodes(nodes: ExtractedNode[]) {
const datas = await Promise.allSettled(
nodes.map(async (component) => {
const node = figma.getNodeById(component.id) as ComponentNode;
const description = component.description;
const regex = createRegexWithDelimiters("[", "]");
const metadatasRegexResult = regex.exec(description || "");

if (metadatasRegexResult && metadatasRegexResult.length === 2) {
return {
name: component.name,
svg: await node.exportAsync({
format: "SVG_STRING",
svgIdAttribute: true,
}),
metadatas: metadatasRegexResult[1].split(","),
};
}

return {
name: component.name,
svg: await node.exportAsync({
format: "SVG_STRING",
svgIdAttribute: true,
}),
metadatas: [],
};
}),
);
Expand Down
12 changes: 12 additions & 0 deletions figma-plugin/ui-src/pages/Deploy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
Flex,
Spinner,
Text,
Textarea,
Tooltip,
} from "@chakra-ui/react";
import { useJune } from "june-so-sandbox-react";
Expand Down Expand Up @@ -215,6 +216,17 @@ const Deploy = () => {
);
})}
</Box>

<Box marginTop={4} fontSize={14}>
<Text fontWeight="bold">Datas</Text>
</Box>

<Box>
<Textarea
value={JSON.stringify(iconPreview, null, 2)}
placeholder="icons data"
/>
</Box>
</Box>
);
};
Expand Down
4,602 changes: 4,602 additions & 0 deletions icons.json

Large diffs are not rendered by default.

40 changes: 35 additions & 5 deletions packages/generator/src/core/react.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { IconaIconData, ReactConfig } from "@icona/types";
import type {
IconaIconData,
ReactConfig,
TemplateContext,
TemplateVariables,
} from "@icona/types";
import type { Config } from "@svgr/core";
import { transform } from "@svgr/core";
import { writeFile } from "fs/promises";
Expand Down Expand Up @@ -28,7 +33,6 @@ export const generateReact = async (props: Props) => {
const { genIndexFile } = config;
const componentNames = [];
const targetPath = getTargetPath(config.path || "react");
const svgrConfig = config.svgrConfig || {};

if (!icons) {
throw new Error("There is no icons data");
Expand Down Expand Up @@ -63,9 +67,35 @@ export const generateReact = async (props: Props) => {

componentNames.push(componentName);

const component = await transform(svg, svgrConfig as Config, {
componentName,
});
const component = await transform(
svg,
{
...(config.svgrConfig as Config),
template: (variables: TemplateVariables, context: TemplateContext) => {
const { tpl } = context;
const customTemplate = config.template?.(data);

// Custom template
if (customTemplate) {
return customTemplate(variables, context);
}

// Default template
return tpl`
${variables.imports};
${variables.interfaces};
const ${variables.componentName} = (${variables.props}) => (
${variables.jsx}
);
${variables.exports};
`;
},
},
{},
);

const svgPath = resolve(targetPath, `${componentName}.tsx`);
const content = `${ignores}\n${component}`;
Expand Down
2 changes: 2 additions & 0 deletions packages/types/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"src"
],
"devDependencies": {
"@babel/core": "^7.25.8",
"@babel/template": "^7.25.7",
"@svgr/babel-preset": "^8.0.0",
"@svgr/cli": "^8.0.1",
"@types/babel__core": "^7.20.1",
Expand Down
51 changes: 51 additions & 0 deletions packages/types/src/babel.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { types as t } from "@babel/core";
import type { TemplateBuilder } from "@babel/template";

export interface TemplateVariables {
componentName: string;
interfaces: t.TSInterfaceDeclaration[];
props: (t.ObjectPattern | t.Identifier)[];
imports: t.ImportDeclaration[];
exports: (t.VariableDeclaration | t.ExportDeclaration | t.Statement)[];
jsx: t.JSXElement;
}

export interface TemplateContext {
options: TemplateContextOptions;
tpl: TemplateBuilder<TemplateReturn>["ast"];
}

export type TemplateReturn = t.Statement | t.Statement[];

export interface Template {
(variables: TemplateVariables, context: TemplateContext): TemplateReturn;
}

interface State {
componentName: string;
caller?: { previousExport?: string | null };
}

interface JSXRuntimeImport {
source: string;
namespace?: string;
defaultSpecifier?: string;
specifiers?: string[];
}

export interface TemplateContextOptions {
typescript?: boolean;
titleProp?: boolean;
descProp?: boolean;
expandProps?: boolean | "start" | "end";
ref?: boolean;
template?: Template;
state: State;
native?: boolean;
memo?: boolean;
exportType?: "named" | "default";
namedExport?: string;
jsxRuntime?: "automatic" | "classic";
jsxRuntimeImport?: JSXRuntimeImport;
importSource?: string;
}
1 change: 1 addition & 0 deletions packages/types/src/data.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface IconaIconData {
name: string;
style: SVGStyleOptions;
svg: string;
metadatas?: string[];
png: {
"1x": Base64 | null;
"2x": Base64 | null;
Expand Down
1 change: 1 addition & 0 deletions packages/types/src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./babel";
export * from "./data";
export * from "./lib";
18 changes: 17 additions & 1 deletion packages/types/src/lib.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import type { Config as SvgrConfig } from "@svgr/core";
import type { SVGtoPDFOptions as LibSVGtoPDFOptions } from "svg-to-pdfkit";
import type { Config as SvgoConfig } from "svgo";

import type {
TemplateContext,
TemplateReturn,
TemplateVariables,
} from "./babel";
import type { IconaIconData } from "./data";

/**
* @param overwrite overwrite existing files in folder
* @param recreate rm -rf all files and generate new files in folder
Expand Down Expand Up @@ -85,9 +92,18 @@ export interface ReactConfig extends BaseConfig {
* Config (@svgr/core)
* @see https://react-svgr.com/docs/options/
*/
svgrConfig?: SvgrConfig;
svgrConfig?: Omit<SvgrConfig, "template">;

genIndexFile?: boolean;

template?: (
iconData: IconaIconData,
) => (
variables: TemplateVariables,
context: TemplateContext,
) => TemplateReturn;
// template?: "with-jsdoc-size-prop" | Template;
// jsdocTemplate?: (iconData: IconaIconData) => string;
}

interface Svg2vectordrawableOptions {
Expand Down
Loading

0 comments on commit fc5f4ae

Please sign in to comment.