Skip to content

Commit e1a20d4

Browse files
committed
Optimize
1 parent 058aa31 commit e1a20d4

File tree

13 files changed

+49
-139
lines changed

13 files changed

+49
-139
lines changed

src/code-impl.ts

Lines changed: 9 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -18,60 +18,16 @@ import { wrapComponent } from './codegen/utils/wrap-component'
1818
import { exportDevup, importDevup } from './commands/devup'
1919
import { exportAssets } from './commands/exportAssets'
2020
import { exportComponents } from './commands/exportComponents'
21-
import { exportPagesAndComponents } from './commands/exportPagesAndComponents'
21+
import {
22+
exportPagesAndComponents,
23+
extractCustomComponentImports,
24+
extractImports,
25+
} from './commands/exportPagesAndComponents'
26+
export { extractCustomComponentImports, extractImports }
27+
2228
import { getComponentName, resetTextStyleCache } from './utils'
2329
import { toPascal } from './utils/to-pascal'
2430

25-
const DEVUP_COMPONENTS = [
26-
'Center',
27-
'VStack',
28-
'Flex',
29-
'Grid',
30-
'Box',
31-
'Text',
32-
'Image',
33-
]
34-
35-
export function extractImports(
36-
componentsCodes: ReadonlyArray<readonly [string, string]>,
37-
): string[] {
38-
const allCode = componentsCodes.map(([_, code]) => code).join('\n')
39-
const imports = new Set<string>()
40-
41-
for (const component of DEVUP_COMPONENTS) {
42-
const regex = new RegExp(`<${component}[\\s/>]`, 'g')
43-
if (regex.test(allCode)) {
44-
imports.add(component)
45-
}
46-
}
47-
48-
if (/\bkeyframes\s*(\(|`)/.test(allCode)) {
49-
imports.add('keyframes')
50-
}
51-
52-
return Array.from(imports).sort()
53-
}
54-
55-
export function extractCustomComponentImports(
56-
componentsCodes: ReadonlyArray<readonly [string, string]>,
57-
): string[] {
58-
const allCode = componentsCodes.map(([_, code]) => code).join('\n')
59-
const customImports = new Set<string>()
60-
61-
// Find all component usages in JSX: <ComponentName or <ComponentName>
62-
const componentUsageRegex = /<([A-Z][a-zA-Z0-9]*)/g
63-
const matches = allCode.matchAll(componentUsageRegex)
64-
for (const match of matches) {
65-
const componentName = match[1]
66-
// Skip devup-ui components and components defined in this code
67-
if (!DEVUP_COMPONENTS.includes(componentName)) {
68-
customImports.add(componentName)
69-
}
70-
}
71-
72-
return Array.from(customImports).sort()
73-
}
74-
7531
function generateImportStatements(
7632
componentsCodes: ReadonlyArray<readonly [string, string]>,
7733
): string {
@@ -357,9 +313,6 @@ export function registerCodegen(ctx: typeof figma) {
357313
componentsResponsiveCodes = responsiveResults
358314
}
359315

360-
console.info(`[benchmark] devup-ui end ${Date.now() - time}ms`)
361-
console.info(perfReport())
362-
363316
// Check if node itself is SECTION or has a parent SECTION
364317
const isNodeSection = ResponsiveCodegen.canGenerateResponsive(node)
365318
const parentSection = ResponsiveCodegen.hasParentSection(node)
@@ -416,6 +369,8 @@ export function registerCodegen(ctx: typeof figma) {
416369
}
417370
}
418371
if (debug) {
372+
console.info(`[benchmark] devup-ui end ${Date.now() - time}ms`)
373+
console.info(perfReport())
419374
// Track AFTER codegen — collects all node properties for test case
420375
// generation without Proxy overhead during the hot codegen path.
421376
nodeProxyTracker.trackTree(node)

src/codegen/props/auto-layout.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ function getAlignItems(node: SceneNode & BaseFrameMixin): string | undefined {
5959
function getGridProps(
6060
node: GridLayoutMixin,
6161
): Record<string, boolean | undefined | string | number | null> {
62-
const sameGap = node.gridRowGap === node.gridColumnGap
62+
// Round to 2 decimal places to handle Figma floating-point imprecision
63+
const sameGap =
64+
Math.round(node.gridRowGap * 100) / 100 ===
65+
Math.round(node.gridColumnGap * 100) / 100
6366
return {
6467
display: 'grid',
6568
gridTemplateColumns: `repeat(${node.gridColumnCount}, 1fr)`,

src/codegen/props/border.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export async function getBorderProps(
4343
const paintCssList = []
4444
for (let i = 0; i < node.strokes.length; i++) {
4545
const paint = node.strokes[node.strokes.length - 1 - i]
46-
if (paint.visible && paint.opacity !== 0) {
46+
if (paint.visible !== false && paint.opacity !== 0) {
4747
paintCssList.push(
4848
paintToCSSSyncIfPossible(paint, node, i === node.strokes.length - 1) ??
4949
(await paintToCSS(paint, node, i === node.strokes.length - 1)),

src/codegen/props/selector.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,27 +62,27 @@ function toTransitionPropertyName(key: string): string {
6262
return toKebabCase(mapped)
6363
}
6464

65-
// 속성 이름을 유효한 TypeScript 식별자로 변환
65+
// Convert property names to valid TypeScript identifiers
6666
const toUpperCase = (_: string, chr: string) => chr.toUpperCase()
6767

6868
export function sanitizePropertyName(name: string): string {
6969
// 0. Strip Figma's internal "#nodeId:uniqueId" suffix (e.g., "leftIcon#60:123" → "leftIcon")
7070
const stripped = name.replace(/#\d+:\d+$/, '')
7171

72-
// 1. 한글 '속성'을 'property'로 변환 (공백 포함 처리: "속성1" → "property1")
73-
const normalized = stripped.trim().replace(/\s*/g, 'property') // 한글 '속성' + 뒤따르는 공백을 'property'로 변환
72+
// 1. Replace Korean word for "property" with English equivalent (e.g., "속성1" → "property1")
73+
const normalized = stripped.trim().replace(/\s*/g, 'property')
7474

75-
// 2. 공백과 특수문자를 처리하여 camelCase로 변환
75+
// 2. Convert spaces and special characters to camelCase
7676
const result = normalized
77-
// 공백이나 특수문자 뒤의 문자를 대문자로 (camelCase 변환)
77+
// Capitalize the character after spaces/hyphens/underscores (camelCase)
7878
.replace(/[\s\-_]+(.)/g, toUpperCase)
79-
// 숫자로 시작하면 앞에 _ 추가
79+
// Prefix leading digits with underscore
8080
.replace(/^(\d)/, '_$1')
8181

82-
// 3. 유효하지 않은 문자 제거 (한글, 특수문자 등)
82+
// 3. Remove invalid characters (Korean, special chars, etc.)
8383
const cleaned = result.replace(/[^\w$]/g, '')
8484

85-
// 4. 완전히 비어있거나 숫자로만 구성된 경우 기본값 사용
85+
// 4. Fall back to default name if empty or digits-only
8686
if (!cleaned || /^\d+$/.test(cleaned)) {
8787
return 'variant'
8888
}
@@ -143,9 +143,6 @@ async function computeSelectorProps(node: ComponentSetNode): Promise<{
143143
}> {
144144
const hasEffect = !!node.componentPropertyDefinitions.effect
145145
const tSelector = perfStart()
146-
console.info(
147-
`[perf] getSelectorProps: processing ${node.children.length} children`,
148-
)
149146
// Pre-filter: only call expensive getProps() on children with non-default effects.
150147
// The effect/trigger check is a cheap property read — skip children that would be
151148
// discarded later anyway (effect === undefined or effect === 'default').
@@ -303,9 +300,6 @@ async function computeSelectorPropsForGroup(
303300
if (!defaultComponent) return {}
304301

305302
const tGroup = perfStart()
306-
console.info(
307-
`[perf] getSelectorPropsForGroup: processing ${matchingComponents.length} matching components`,
308-
)
309303
const defaultProps = await getProps(defaultComponent)
310304
const result: Record<string, object | string> = {}
311305
const diffKeys = new Set<string>()

src/codegen/props/text-shadow.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export function getTextShadowProps(
77
): Record<string, string> | undefined {
88
if (node.type !== 'TEXT') return
99

10-
const effects = node.effects.filter((effect) => effect.visible)
10+
const effects = node.effects.filter((effect) => effect.visible !== false)
1111
if (effects.length === 0) return
1212
const dropShadows = effects.filter((effect) => effect.type === 'DROP_SHADOW')
1313
if (dropShadows.length === 0) return

src/codegen/props/text-stroke.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export async function getTextStrokeProps(
55
): Promise<Record<string, string> | undefined> {
66
if (node.type !== 'TEXT') return
77

8-
const strokes = node.strokes.filter((stroke) => stroke.visible)
8+
const strokes = node.strokes.filter((stroke) => stroke.visible !== false)
99
if (strokes.length === 0) return
1010
const solidStrokes = strokes.filter((stroke) => stroke.type === 'SOLID')
1111
// @todo support gradient stroke

src/codegen/responsive/ResponsiveCodegen.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,6 @@ export class ResponsiveCodegen {
547547
componentSet: ComponentSetNode,
548548
componentName: string,
549549
): Promise<ReadonlyArray<readonly [string, string]>> {
550-
console.info(
551-
`[perf] generateVariantResponsiveComponents: ${componentName}, ${componentSet.children.length} children, ${Object.keys(componentSet.componentPropertyDefinitions).length} variant keys`,
552-
)
553550
const tTotal = perfStart()
554551

555552
// Find viewport and effect variant keys

src/codegen/utils/check-asset-node.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function checkAssetNode(
4545
// if node has tile, it is not an Image, it just has a tile background
4646
node.fills.find(
4747
(fill: Paint) =>
48-
fill.visible &&
48+
fill.visible !== false &&
4949
(fill.type === 'PATTERN' ||
5050
(fill.type === 'IMAGE' && fill.scaleMode === 'TILE')),
5151
)
@@ -55,7 +55,7 @@ export function checkAssetNode(
5555
? 'fills' in node && Array.isArray(node.fills)
5656
? node.fills.some(
5757
(fill: Paint) =>
58-
fill.visible &&
58+
fill.visible !== false &&
5959
fill.type === 'IMAGE' &&
6060
fill.scaleMode !== 'TILE',
6161
)
@@ -75,7 +75,7 @@ export function checkAssetNode(
7575
Array.isArray(node.fills) &&
7676
!node.fills.some(
7777
(fill: Paint) =>
78-
fill.visible &&
78+
fill.visible !== false &&
7979
(fill.type === 'IMAGE' ||
8080
fill.type === 'VIDEO' ||
8181
fill.type === 'PATTERN'),
@@ -93,18 +93,15 @@ export function checkAssetNode(
9393
node.paddingBottom > 0)) ||
9494
('fills' in node &&
9595
(Array.isArray(node.fills)
96-
? node.fills.find((fill) => fill.visible)
96+
? node.fills.find((fill) => fill.visible !== false)
9797
: true))
9898
)
9999
return null
100100
return checkAssetNode(children[0], true)
101101
}
102-
const fillterdChildren = children.filter((child) => child.visible)
102+
const filteredChildren = children.filter((child) => child.visible)
103103

104-
// return children.every((child) => child.visible && checkAssetNode(child))
105-
// ? 'svg'
106-
// : null
107-
return fillterdChildren.every((child) => {
104+
return filteredChildren.every((child) => {
108105
const result = checkAssetNode(child, true)
109106
if (result === null) return false
110107
return result === 'svg'

src/codegen/utils/four-value-shortcut.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import { addPx } from './add-px'
22

33
export function fourValueShortcut(
4-
first: number,
5-
second: number,
6-
third: number,
7-
fourth: number,
4+
_first: number,
5+
_second: number,
6+
_third: number,
7+
_fourth: number,
88
): string {
9+
// Round to 2 decimal places to handle Figma floating-point imprecision
10+
const first = Math.round(_first * 100) / 100
11+
const second = Math.round(_second * 100) / 100
12+
const third = Math.round(_third * 100) / 100
13+
const fourth = Math.round(_fourth * 100) / 100
14+
915
if (first === second && second === third && third === fourth)
1016
return addPx(first, '0')
1117
if (first === third && second === fourth)

src/codegen/utils/get-component-name.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)