Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 43 additions & 25 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
*/
import process from 'node:process';

import { sendWebhookMessage } from '../src/integrations/slack/api.js';
import { sendWebhookMessage } from '~/integrations/slack/api';

const {
SLACK_GDS_ALARM_WEBHOOK_URL,
Expand Down Expand Up @@ -68,10 +68,5 @@ const message = {
],
};

try {
await sendWebhookMessage(SLACK_GDS_ALARM_WEBHOOK_URL, message);
console.log('✅ Slack 알림이 성공적으로 전송되었습니다.');
} catch (error) {
console.error('❌ Slack 알림 전송 중 오류 발생:', error.message);
process.exit(1);
}
await sendWebhookMessage(SLACK_GDS_ALARM_WEBHOOK_URL, message);
console.log('✅ Slack 알림이 성공적으로 전송되었습니다.');
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
* Sync icons from Figma to local React components
*
* Usage:
* node --env-file=.env ./commands/sync-icons.mjs
* tsx --env-file=.env ./commands/sync-icons.ts
*
* Environment variables required:
* - FIGMA_TOKEN: Your Figma personal access token
* - TYPE: Icon type to sync ('basic' or 'symbol')
*
* Note: Node 20.6+ required for --env-file flag
*/
import { camelCase, startCase } from 'lodash-es';
import { createHash } from 'node:crypto';
Expand All @@ -26,20 +25,17 @@ import {
FIGMA_ICONS_SYMBOL_COLOR_COUNTRY_NODE_ID,
FIGMA_ICONS_SYMBOL_COLOR_NODE_ID,
FIGMA_NODE_TYPES,
} from '../src/icons/constants.js';
import { ICON_TYPES } from '../src/icons/icon-types.js';
import getIconComponentIndex from '../src/icons/templates/icon/icon-component-index.js';
import getIconComponent from '../src/icons/templates/icon/icon-component.js';
import getIconsIndex from '../src/icons/templates/icon/icons-index.js';
import {
filterDocumentByNodeType,
getIconJsx,
getNodesWithUrl,
} from '../src/integrations/figma/lib.js';
} from '~/icons/constants';
import { ICON_TYPES } from '~/icons/icon-types';
import getIconComponent from '~/icons/templates/icon/icon-component';
import getIconComponentIndex from '~/icons/templates/icon/icon-component-index';
import getIconsIndex from '~/icons/templates/icon/icons-index';
import type { IconNode } from '~/integrations/figma/lib';
import { filterDocumentByNodeType, getIconJsx, getNodesWithUrl } from '~/integrations/figma/lib';

const TYPE = process.env.TYPE;

function findRoot(dir) {
function findRoot(dir: string): string {
if (existsSync(path.join(dir, 'pnpm-workspace.yaml'))) return dir;
const parent = path.dirname(dir);
if (parent === dir)
Expand All @@ -56,7 +52,7 @@ const PRETTIER_OPTIONS = {
printWidth: 100,
};

function normalizeIconName(name) {
function normalizeIconName(name: string) {
return startCase(camelCase(name.replace(FIGMA_EMOJI_PREFIX_PATTERN, ''))).replace(/ /g, '');
}

Expand All @@ -67,31 +63,30 @@ if (!process.env.FIGMA_TOKEN) {
process.exit(1);
}

if (!TYPE || !Object.hasOwn(ICON_TYPES, TYPE)) {
console.error(
pc.red(
` GDS FIGMA EXPORT ERROR: TYPE must be one of ${Object.keys(ICON_TYPES).join(', ')}.`,
),
);
process.exit(1);
}

try {
const { nodeIds, targetPath } = ICON_TYPES[TYPE];
let FILE_KEY = FIGMA_ICONS_FILE_KEY;
const FILE_KEY = FIGMA_ICONS_FILE_KEY;

// Get nodes (icons) set as COMPONENT in the file.
let components = [];
if (TYPE === 'basic' || TYPE === 'symbol') {
FILE_KEY = FIGMA_ICONS_FILE_KEY;
// Basic icons are composed of 2 frames, so nodeIds are in array form
for (const nodeId of nodeIds) {
const nodeComponents = await filterDocumentByNodeType({
nodeType: FIGMA_NODE_TYPES.Component,
fileKey: FILE_KEY,
nodeIds: nodeId,
depth: 1,
});
components = components.concat(nodeComponents);
}
} else {
components = await filterDocumentByNodeType({
// Each icon type spans several frames, so nodeIds are fetched one frame at a time.
let components: IconNode[] = [];
for (const nodeId of nodeIds) {
const nodeComponents = await filterDocumentByNodeType({
nodeType: FIGMA_NODE_TYPES.Component,
fileKey: FILE_KEY,
nodeIds,
nodeIds: nodeId,
depth: 1,
});
components = components.concat(nodeComponents);
}

const componentsInfo = {
Expand Down Expand Up @@ -121,10 +116,10 @@ try {
// Convert svg code to React components through image URLs and save locally.
console.log(pc.yellow(` GDS FIGMA EXPORT: `) + `Converting to React components...`);
const parentIconPath = path.join(CURRENT_DIRECTORY, targetPath);
const newIconNameArr = [];
const updatedIconNameArr = [];
const newIconNameArr: string[] = [];
const updatedIconNameArr: string[] = [];
const limit = pLimit(10);
const md5 = (str) => createHash('md5').update(str).digest('hex');
const md5 = (str: string) => createHash('md5').update(str).digest('hex');

const promiseCreateIcons = componentsWithUrl.map(({ name, url, parentId }) =>
limit(async () => {
Expand Down Expand Up @@ -153,7 +148,7 @@ try {
await fs.mkdir(saveTargetPath, { recursive: true });
newIconNameArr.push(iconName);
} else {
let existingContent = null;
let existingContent: string | null = null;
try {
existingContent = await fs.readFile(iconFilePath, 'utf8');
} catch {
Expand Down Expand Up @@ -184,7 +179,7 @@ try {

// Detect and remove deleted icons
console.log(pc.yellow(` GDS FIGMA EXPORT: `) + `Checking for deleted icons...`);
const deletedIconNameArr = [];
const deletedIconNameArr: string[] = [];
const figmaIconNames = new Set(componentsInfo.nameArr);

// Get existing icon directories
Expand Down
21 changes: 15 additions & 6 deletions scripts/sync-figma/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,24 @@
"description": "Internal CLI scripts for syncing assets from Figma",
"type": "module",
"scripts": {
"format": "prettier --write \"./src/**/*.{js,mjs,md}\"",
"format:check": "prettier --check \"./src/**/*.{js,mjs,md}\"",
"notify:slack": "node ./commands/notify-slack.mjs",
"sync-icons:basic": "TYPE=basic node --env-file-if-exists=.env ./commands/sync-icons.mjs",
"sync-icons:symbol": "TYPE=symbol node --env-file-if-exists=.env ./commands/sync-icons.mjs"
"format": "prettier --write \"./{commands,src}/**/*.{ts,md}\"",
"format:check": "prettier --check \"./{commands,src}/**/*.{ts,md}\"",
"notify:slack": "tsx ./commands/notify-slack.ts",
"sync-icons:basic": "TYPE=basic tsx --env-file-if-exists=.env ./commands/sync-icons.ts",
"sync-icons:symbol": "TYPE=symbol tsx --env-file-if-exists=.env ./commands/sync-icons.ts",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"lodash-es": "^4.18.1",
"p-limit": "^6.2.0",
"picocolors": "^1.1.1"
"picocolors": "^1.1.1",
"prettier": "^3.9.6"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prettier는 루트에서 공용으로 관리하기로 했던 것으로 기억합니다! 확인 한 번 부탁드려요.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prettier script만 제거했습니다.!

icon 추출하면서 file write 작업 전 prettier를 돌려서 파일 변경 사항을 확인하고 있어서 런타임에 prettier를 돌리는 코드가 있어서 의존성은 남겨두었습니다.

},
"devDependencies": {
"@repo/typescript-config": "workspace:*",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eslint도 추가해서 문법 검사를 같이 해주는 게 어떨까요??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 누락되었었네요! eslint 추가했습니다.~

"@types/lodash-es": "^4.17.12",
"@types/node": "^22.20.1",
"tsx": "^4.23.12",
"typescript": "catalog:"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ const FIGMA_NODE_TYPES = {
Frame: 'FRAME',
Component: 'COMPONENT',
ComponentSet: 'COMPONENT_SET',
};
} as const;

type FigmaNodeType = (typeof FIGMA_NODE_TYPES)[keyof typeof FIGMA_NODE_TYPES];

export type { FigmaNodeType };

export {
FIGMA_ICONS_FILE_KEY,
Expand Down
Loading
Loading