Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env sh

bun run lint
bun run test
#!/usr/bin/env sh
bun run lint
bun run test
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "rspack dev",
"build": "rspack build",
"watch": "rspack build --watch",
"test": "bun test --coverage",
"test": "tsc --noEmit && bun test --coverage",
"lint": "biome check --error-on-warnings",
"lint:fix": "biome check --fix",
"prepare": "husky"
Expand Down
91 changes: 55 additions & 36 deletions src/__tests__/__snapshots__/code.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,36 +1,55 @@
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots

exports[`registerCodegen should register codegen 1`] = `
[
{
"code":
"export function Test() {
return <Box boxSize="100%" />
}"
,
"language": "TYPESCRIPT",
"title": "Test - Components",
},
{
"code":
"echo 'export function Test() {
return <Box boxSize="100%" />
}' > Test.tsx"
,
"language": "BASH",
"title": "Test - Components CLI",
},
]
`;

exports[`registerCodegen should register codegen 2`] = `
[
{
"code": "<Box boxSize="100%" />",
"language": "TYPESCRIPT",
"title": "Main",
},
]
`;

exports[`registerCodegen should register codegen 3`] = `[]`;
// Bun Snapshot v1, https://bun.sh/docs/test/snapshots

exports[`registerCodegen should register codegen 1`] = `
[
{
"code":
"export function Test() {
return <Box boxSize="100%" />
}"
,
"language": "TYPESCRIPT",
"title": "Test - Components",
},
{
"code":
"mkdir -p src/components

echo 'import { Box } from \\'@devup-ui/react\\'

export function Test() {
return <Box boxSize="100%" />
}' > src/components/Test.tsx"
,
"language": "BASH",
"title": "Test - Components CLI (Bash)",
},
{
"code":
"New-Item -ItemType Directory -Force -Path src\\components | Out-Null

@'
import { Box } from '@devup-ui/react'

export function Test() {
return <Box boxSize="100%" />
}
'@ | Out-File -FilePath src\\components\\Test.tsx -Encoding UTF8"
,
"language": "BASH",
"title": "Test - Components CLI (PowerShell)",
},
]
`;

exports[`registerCodegen should register codegen 2`] = `
[
{
"code": "<Box boxSize="100%" />",
"language": "TYPESCRIPT",
"title": "Main",
},
]
`;

exports[`registerCodegen should register codegen 3`] = `[]`;
7 changes: 3 additions & 4 deletions src/__tests__/code-responsive.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const originalGenerateResponsiveCode =

describe('registerCodegen responsive error handling', () => {
beforeEach(() => {
Codegen.prototype.run = runMock
Codegen.prototype.run = runMock as unknown as typeof Codegen.prototype.run
Codegen.prototype.getComponentsCodes = getComponentsCodesMock
Codegen.prototype.getCode = getCodeMock
ResponsiveCodegen.prototype.generateResponsiveCode =
Expand All @@ -48,9 +48,8 @@ describe('registerCodegen responsive error handling', () => {
})

test('swallows responsive errors and still returns base code', async () => {
const handlerCalls: Parameters<
Parameters<typeof registerCodegen>[0]['codegen']['on']
>[1][] = []
const handlerCalls: ((event: CodegenEvent) => Promise<CodegenResult[]>)[] =
[]
const ctx = {
editorType: 'dev',
mode: 'codegen',
Expand Down
32 changes: 32 additions & 0 deletions src/__tests__/code.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ describe('registerCodegen', () => {
node: {
type: 'COMPONENT',
name: 'Test',
visible: true,
},
language: 'devup-ui',
},
Expand All @@ -109,6 +110,7 @@ describe('registerCodegen', () => {
node: {
type: 'FRAME',
name: 'Main',
visible: true,
},
language: 'devup-ui',
},
Expand All @@ -123,6 +125,7 @@ describe('registerCodegen', () => {
node: {
type: 'FRAME',
name: 'Other',
visible: true,
},
language: 'other',
},
Expand Down Expand Up @@ -182,3 +185,32 @@ it('auto-runs on module load when figma is present', async () => {

expect(codegenOn).toHaveBeenCalledWith('generate', expect.any(Function))
})

describe('extractImports', () => {
it('should extract keyframes import when code contains keyframes(', () => {
const result = codeModule.extractImports([
[
'AnimatedBox',
'<Box animationName={keyframes({ "0%": { opacity: 0 } })} />',
],
])
expect(result).toContain('keyframes')
expect(result).toContain('Box')
})

it('should extract keyframes import when code contains keyframes`', () => {
const result = codeModule.extractImports([
['AnimatedBox', '<Box animationName={keyframes`from { opacity: 0 }`} />'],
])
expect(result).toContain('keyframes')
expect(result).toContain('Box')
})

it('should not extract keyframes when not present', () => {
const result = codeModule.extractImports([
['SimpleBox', '<Box w="100px" />'],
])
expect(result).not.toContain('keyframes')
expect(result).toContain('Box')
})
})
89 changes: 81 additions & 8 deletions src/code-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,80 @@ import { exportDevup, importDevup } from './commands/devup'
import { exportAssets } from './commands/exportAssets'
import { exportComponents } from './commands/exportComponents'

export function extractImports(
componentsCodes: ReadonlyArray<readonly [string, string]>,
): string[] {
const allCode = componentsCodes.map(([_, code]) => code).join('\n')
const imports = new Set<string>()

const devupComponents = [
'Center',
'VStack',
'Flex',
'Grid',
'Box',
'Text',
'Image',
]

for (const component of devupComponents) {
const regex = new RegExp(`<${component}[\\s/>]`, 'g')
if (regex.test(allCode)) {
imports.add(component)
}
}

// keyframes 함수 체크
if (/keyframes\s*\(|keyframes`/.test(allCode)) {
imports.add('keyframes')
}

return Array.from(imports).sort()
}

function generateBashCLI(
componentsCodes: ReadonlyArray<readonly [string, string]>,
): string {
const imports = extractImports(componentsCodes)
const importStatement =
imports.length > 0
? `import { ${imports.join(', ')} } from '@devup-ui/react'\n\n`
: ''

const commands = [
'mkdir -p src/components',
'',
...componentsCodes.map(([componentName, code]) => {
const fullCode = importStatement + code
const escapedCode = fullCode.replace(/'/g, "\\'")
return `echo '${escapedCode}' > src/components/${componentName}.tsx`
}),
]

return commands.join('\n')
}

function generatePowerShellCLI(
componentsCodes: ReadonlyArray<readonly [string, string]>,
): string {
const imports = extractImports(componentsCodes)
const importStatement =
imports.length > 0
? `import { ${imports.join(', ')} } from '@devup-ui/react'\n\n`
: ''

const commands = [
'New-Item -ItemType Directory -Force -Path src\\components | Out-Null',
'',
...componentsCodes.map(([componentName, code]) => {
const fullCode = importStatement + code
return `@'\n${fullCode}\n'@ | Out-File -FilePath src\\components\\${componentName}.tsx -Encoding UTF8`
}),
]

return commands.join('\n')
}

export function registerCodegen(ctx: typeof figma) {
if (ctx.editorType === 'dev' && ctx.mode === 'codegen') {
ctx.codegen.on('generate', async ({ node, language }) => {
Expand All @@ -15,7 +89,6 @@ export function registerCodegen(ctx: typeof figma) {
const componentsCodes = codegen.getComponentsCodes()
console.info(`[benchmark] devup-ui end ${Date.now() - time}ms`)

// 반응형 코드 생성 (부모가 Section인 경우)
const parentSection = ResponsiveCodegen.hasParentSection(node)
let responsiveResult: {
title: string
Expand Down Expand Up @@ -60,14 +133,14 @@ export function registerCodegen(ctx: typeof figma) {
code: componentsCodes.map((code) => code[1]).join('\n\n'),
},
{
title: `${node.name} - Components CLI`,
title: `${node.name} - Components CLI (Bash)`,
language: 'BASH',
code: generateBashCLI(componentsCodes),
},
{
title: `${node.name} - Components CLI (PowerShell)`,
language: 'BASH',
code: componentsCodes
.map(
([componentName, code]) =>
`echo '${code}' > ${componentName}.tsx`,
)
.join('\n'),
code: generatePowerShellCLI(componentsCodes),
},
] as const)
: []),
Expand Down
Loading