Skip to content

Commit f3e11fe

Browse files
author
figma-bot
committed
Code Connect v1.0.5
1 parent c890f16 commit f3e11fe

File tree

8 files changed

+79
-36
lines changed

8 files changed

+79
-36
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# Code Connect v1.0.5 (13th August 2024)
2+
3+
## Fixed
4+
5+
### React
6+
- Fixed an issue around creation of Code Connect files from the CLI assistant (fixes https://github.com/figma/code-connect/issues/125)
7+
18
# Code Connect v1.0.4 (7th August 2024)
29

310
## Fixed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
// no exports

cli/src/__test__/e2e_connect_command/react_wizard/components/PrimaryButton.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ interface ButtonProps {
1111
* @param disabled disable the button
1212
* @returns JSX element
1313
*/
14-
export const PrimaryButton = ({ children, disabled = false }: ButtonProps) => {
14+
const InnerComponent = ({ children, disabled = false }: ButtonProps) => {
1515
return <button disabled={disabled}>{children}</button>
1616
}
17+
18+
// Aliases not yet supported by wizard, signature gen should fail gracefully
19+
export const PrimaryButton = InnerComponent

cli/src/connect/wizard/__test__/helpers.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ describe('getFilepathExportsFromFiles', () => {
8686
})
8787

8888
it('generates list of file component keys from ProjectInfo', () => {
89-
const result = getFilepathExportsFromFiles(projectInfo)
89+
const result = getFilepathExportsFromFiles(projectInfo, {} as any)
9090
expect(result.map((filepath) => path.parse(filepath).base)).toEqual([
9191
'MyComponent.tsx~MyComponent',
9292
'MyComponent.tsx~MyComponentProps', // TODO ideally we'd filter out by type here

cli/src/connect/wizard/__test__/prop_mapping/prop_mapping.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ describe('Prop mapping', () => {
9999
},
100100
},
101101
},
102+
cmd: {} as any,
102103
})
103104
expect(result).toEqual({
104105
'Has Icon': {

cli/src/connect/wizard/helpers.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { logger, success } from '../../common/logging'
1111
import path from 'path'
1212
import prompts, { Choice } from 'prompts'
1313
import { isFigmaConnectFile } from '../../react/parser'
14+
import { BaseCommand } from '../../commands/connect'
1415

1516

1617
/**
@@ -118,7 +119,7 @@ export function getComponentOptionsMap(filepathExports: string[]) {
118119
* @param projectInfo
119120
* @returns an array of components in the format `${filepath}~${componentName}
120121
*/
121-
export function getFilepathExportsFromFiles(projectInfo: ProjectInfo) {
122+
export function getFilepathExportsFromFiles(projectInfo: ProjectInfo, cmd: BaseCommand) {
122123
return projectInfo.files.reduce((options, filepath) => {
123124
if (projectInfo.config.parser === 'react') {
124125
const { tsProgram } = projectInfo as ReactProjectInfo
@@ -128,12 +129,19 @@ export function getFilepathExportsFromFiles(projectInfo: ProjectInfo) {
128129
if (!sourceFile) {
129130
throw new Error(`Could not parse file ${filepath}`)
130131
}
131-
const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile)!
132-
const exports = checker.getExportsOfModule(sourceFileSymbol)
132+
try {
133+
const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile)!
134+
const exports = checker.getExportsOfModule(sourceFileSymbol)
133135

134-
exports.forEach((exp) => {
135-
options.push(getFilepathExport(filepath, exp.getName()))
136-
})
136+
exports.forEach((exp) => {
137+
options.push(getFilepathExport(filepath, exp.getName()))
138+
})
139+
} catch (e) {
140+
if (cmd.verbose) {
141+
logger.warn(`Could not get exports from ${filepath}`)
142+
}
143+
// ignore invalid files
144+
}
137145
}
138146
} else {
139147
options.push(filepath)

cli/src/connect/wizard/prop_mapping.ts

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { ComponentTypeSignature, extractComponentTypeSignature } from '../../rea
44
import { FigmaRestApi } from '../figma_rest_api'
55
import { PropMapping, SupportedMappingType } from '../parser_executable_types'
66
import { MatchData, Searcher } from 'fast-fuzzy'
7+
import { BaseCommand } from '../../commands/connect'
8+
import { logger } from '../../common/logging'
79

810
const PROP_MINIMUM_MATCH_THRESHOLD = 0.8
911

@@ -97,17 +99,28 @@ export function generatePropMapping({
9799
filepath,
98100
projectInfo,
99101
component,
102+
cmd,
100103
}: {
101104
exportName: string
102105
filepath: string
103106
projectInfo: ReactProjectInfo
104107
component: FigmaRestApi.Component
105-
}): PropMapping {
106-
const signature = extractSignature({
107-
nameToFind: exportName,
108-
sourceFilePath: filepath,
109-
projectInfo,
110-
})
108+
cmd: BaseCommand
109+
}): PropMapping | undefined {
110+
let signature: ComponentTypeSignature
111+
112+
try {
113+
signature = extractSignature({
114+
nameToFind: exportName,
115+
sourceFilePath: filepath,
116+
projectInfo,
117+
})
118+
} catch (e) {
119+
if (cmd.verbose) {
120+
logger.warn(`Could not extract signature for "${exportName}" in ${filepath}`)
121+
}
122+
return undefined
123+
}
111124

112125
const matchableCodeProps = Object.keys(signature).reduce((acc, key) => {
113126
acc[getMatchableStr(key)] = key
@@ -123,28 +136,31 @@ export function generatePropMapping({
123136
const searchSpace = Object.keys(matchableCodeProps)
124137
const searcher = new Searcher(searchSpace)
125138

126-
Object.entries(component.componentPropertyDefinitions).forEach(
127-
([propertyName, componentPropertyDefinition]) => {
128-
const results = searcher.search(getMatchableStr(propertyName), { returnMatchData: true })
129-
const bestMatch = results[0]
130-
const matchingCodeProp = matchableCodeProps[bestMatch?.item]
131-
132-
if (
133-
bestMatch &&
134-
bestMatch.score > PROP_MINIMUM_MATCH_THRESHOLD &&
135-
getComponentPropertyTypeFromSignature(signature[matchingCodeProp]) ===
136-
componentPropertyDefinition.type &&
137-
isBestMatchForProp(bestMatch)
138-
) {
139-
matchedPropScores[bestMatch.item] = {
140-
codePropName: matchingCodeProp,
141-
figmaPropName: propertyName,
142-
figmaPropType: componentPropertyDefinition.type,
143-
score: bestMatch.score,
139+
if (component.componentPropertyDefinitions) {
140+
Object.entries(component.componentPropertyDefinitions).forEach(
141+
([propertyName, componentPropertyDefinition]) => {
142+
const results = searcher.search(getMatchableStr(propertyName), { returnMatchData: true })
143+
const bestMatch = results[0]
144+
const matchingCodeProp = matchableCodeProps[bestMatch?.item]
145+
146+
if (
147+
bestMatch &&
148+
bestMatch.score > PROP_MINIMUM_MATCH_THRESHOLD &&
149+
getComponentPropertyTypeFromSignature(signature[matchingCodeProp]) ===
150+
componentPropertyDefinition.type &&
151+
isBestMatchForProp(bestMatch)
152+
) {
153+
matchedPropScores[bestMatch.item] = {
154+
codePropName: matchingCodeProp,
155+
figmaPropName: propertyName,
156+
figmaPropType: componentPropertyDefinition.type,
157+
score: bestMatch.score,
158+
}
144159
}
145-
}
146-
},
147-
)
160+
},
161+
)
162+
}
163+
148164
return Object.entries(matchedPropScores).reduce(
149165
(acc, [_, { codePropName, figmaPropName, figmaPropType }]) => {
150166
acc[figmaPropName] = {

cli/src/connect/wizard/run_wizard.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,12 +436,14 @@ async function createCodeConnectFiles({
436436
unconnectedComponentsMap,
437437
outDir: outDirArg,
438438
projectInfo,
439+
cmd,
439440
}: {
440441
figmaFileUrl: string
441442
linkedNodeIdsToFilepathExports: Record<string, string>
442443
unconnectedComponentsMap: Record<string, FigmaRestApi.Component>
443444
outDir: string | null
444445
projectInfo: ProjectInfo
446+
cmd: BaseCommand
445447
}) {
446448
for (const [nodeId, filepathExport] of Object.entries(linkedNodeIdsToFilepathExports)) {
447449
const urlObj = new URL(figmaFileUrl)
@@ -465,6 +467,7 @@ async function createCodeConnectFiles({
465467
exportName,
466468
projectInfo: projectInfo as ReactProjectInfo,
467469
component: unconnectedComponentsMap[nodeId],
470+
cmd,
468471
})
469472
: undefined,
470473
component: {
@@ -585,10 +588,12 @@ async function askForTopLevelDirectoryOrDetermineFromConfig({
585588
dir,
586589
hasConfigFile,
587590
config,
591+
cmd,
588592
}: {
589593
dir: string
590594
hasConfigFile: boolean
591595
config: CodeConnectConfig
596+
cmd: BaseCommand
592597
}) {
593598
let componentDirectory: string | null = null
594599

@@ -639,7 +644,7 @@ async function askForTopLevelDirectoryOrDetermineFromConfig({
639644
projectInfo = getReactProjectInfo(projectInfo as ReactProjectInfo)
640645
}
641646

642-
const filepathExports = getFilepathExportsFromFiles(projectInfo)
647+
const filepathExports = getFilepathExportsFromFiles(projectInfo, cmd)
643648
spinner.stop()
644649

645650
if (!filepathExports.length) {
@@ -709,6 +714,7 @@ export async function runWizard(cmd: BaseCommand) {
709714
dir,
710715
hasConfigFile,
711716
config,
717+
cmd,
712718
})
713719

714720
const { figmaFileUrl } = await askQuestionOrExit({
@@ -815,5 +821,6 @@ export async function runWizard(cmd: BaseCommand) {
815821
figmaFileUrl,
816822
outDir,
817823
projectInfo,
824+
cmd,
818825
})
819826
}

0 commit comments

Comments
 (0)