Skip to content

Commit 60b9be4

Browse files
author
figma-bot
committed
Code Connect v1.2.1
1 parent 659795a commit 60b9be4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+437
-167
lines changed

CHANGELOG.md

+19
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
# Code Connect v1.2.1 (23rd October 2024)
2+
3+
## Fixed
4+
5+
### React
6+
- Fixed a bug introduced in 1.2.0 where `nestedProps` referencing a hidden layer would result in an error rendering Code Connect
7+
8+
### SwiftUI
9+
- Fixed potential "index is out of bounds" error.
10+
11+
### General
12+
- Changed how the extension makes HTTP requests to resolve issues when connecting through a proxy. Please [submit a support ticket](https://help.figma.com/hc/en-us/requests/new?ticket_form_id=360001744374) if you continue to have connection issues after this update.
13+
14+
### Compose
15+
16+
- Fixed some parsing errors when running the `create` and `publish` commands
17+
118
# Code Connect v1.2.0
219

320
## Features
@@ -19,6 +36,8 @@
1936
- Case of attribute names is now preserved to support Angular (fixes https://github.com/figma/code-connect/issues/172)
2037
- Fixed a bug with `nestedProps` (fixes https://github.com/figma/code-connect/issues/176)
2138

39+
## Fixed
40+
2241
# Code Connect v1.1.4 (26th September 2024)
2342

2443
## Fixed

cli/package.json

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@figma/code-connect",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "A tool for connecting your design system components in code with your design system in Figma",
55
"keywords": [],
66
"author": "Figma",
@@ -61,6 +61,7 @@
6161
"benchmarking:run": "npx tsx ./src/connect/wizard/__test__/prop_mapping/prop_mapping_benchmarking.ts"
6262
},
6363
"devDependencies": {
64+
6465
"@types/cross-spawn": "^6.0.6",
6566
"@types/jest": "^29.5.13",
6667
"@types/jsdom": "^21.1.7",
@@ -80,13 +81,12 @@
8081
"webpack-cli": "^5.1.4"
8182
},
8283
"dependencies": {
83-
"@babel/core": "^7.25.2",
84-
"@babel/generator": "^7.25.2",
85-
"@babel/parser": "^7.25.2",
86-
"@babel/types": "^7.25.2",
84+
"@babel/core": "7.25.8",
85+
"@babel/generator": "7.25.7",
86+
"@babel/parser": "7.25.8",
87+
"@babel/types": "7.25.8",
8788

8889
"@storybook/csf-tools": "^7.6.7",
89-
"axios": "^1.7.4",
9090
"boxen": "5.1.1",
9191
"chalk": "^4.1.2",
9292
"commander": "^11.1.0",
@@ -106,7 +106,8 @@
106106
"strip-ansi": "^6.0.0",
107107
"ts-morph": "^23.0.0",
108108
"typescript": "5.4.2",
109-
"zod": "^3.23.6",
109+
"undici": "^5.28.4",
110+
"zod": "^3.23.8",
110111
"zod-validation-error": "^3.2.0"
111112
}
112113
}

cli/src/cli.ts

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import * as commander from 'commander'
44
import { addConnectCommandToProgram } from './commands/connect'
5-
import { updateCli } from './common/updates'
65
import { maybePrefillWizardQuestionsForTesting } from './connect/wizard/helpers'
76

87
require('dotenv').config()

cli/src/commands/connect.ts

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { createCodeConnectFromUrl } from '../connect/create'
66
import {
77
CodeConnectConfig,
88
CodeConnectExecutableParserConfig,
9-
CodeConnectHtmlConfig,
109
CodeConnectReactConfig,
1110
ProjectInfo,
1211
getProjectInfo,

cli/src/common/fetch.ts

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
2+
import { ProxyAgent, setGlobalDispatcher } from 'undici'
3+
4+
type Method = 'POST' | 'GET' | 'PUT' | 'DELETE' | 'post' | 'get' | 'put' | 'delete'
5+
6+
type Options<OptionsT extends RequestInit = RequestInit> = OptionsT & {
7+
/* Set the query string of the request from an object */
8+
query?: Record<string, any>
9+
}
10+
11+
class FetchError extends Error {
12+
constructor(
13+
public response: Response,
14+
public data: Record<any, any> | undefined,
15+
) {
16+
super()
17+
}
18+
}
19+
20+
export const isFetchError = (error: unknown): error is FetchError => {
21+
return error instanceof FetchError
22+
}
23+
24+
export function getProxyUrl() {
25+
return (
26+
process.env.HTTPS_PROXY ||
27+
process.env.HTTP_PROXY ||
28+
process.env.https_proxy ||
29+
process.env.http_proxy
30+
)
31+
}
32+
33+
/**
34+
* Creates a ProxyAgent and sets it as the global dispatcher via unidici (which
35+
* affects fetch calls) if a proxy is set either in VS Code settings or as an
36+
* environment variable.
37+
*/
38+
const proxyUrl = getProxyUrl()
39+
const agent = proxyUrl ? new ProxyAgent({ uri: proxyUrl }) : undefined
40+
if (agent) {
41+
setGlobalDispatcher(agent)
42+
}
43+
44+
/**
45+
* Makes a request to the Figma API. This is used by other functions to make
46+
* various types of requests. We return both the response object, and the data
47+
* parsed as JSON, to make it easier to work with the response.
48+
*/
49+
async function makeRequestInternal<ResponseT = unknown>(
50+
url: string,
51+
method: Method,
52+
options: Options = {},
53+
body?: Record<any, any>,
54+
) {
55+
const urlObj = new URL(url)
56+
if (options?.query) {
57+
Object.entries(options.query).forEach(([key, value]) => {
58+
urlObj.searchParams.append(key, value as string)
59+
})
60+
}
61+
url = urlObj.toString()
62+
63+
if (body) {
64+
options.body = JSON.stringify(body)
65+
}
66+
67+
const response = await fetch(url, { ...options, method })
68+
if (!response.ok) {
69+
let data
70+
try {
71+
data = await response.json()
72+
} catch (e) {
73+
data = undefined
74+
}
75+
throw new FetchError(response, data)
76+
}
77+
78+
const text = await response.text()
79+
const data = text ? (JSON.parse(text) as ResponseT) : ({} as ResponseT)
80+
81+
return { response, data }
82+
}
83+
84+
export const request = {
85+
get: <MetaT>(url: string, options: Options = {}) => {
86+
return makeRequestInternal<MetaT>(url, 'GET', options)
87+
},
88+
post: <MetaT>(url: string, body: Record<any, any>, options: Options = {}) => {
89+
return makeRequestInternal<MetaT>(url, 'POST', options, body)
90+
},
91+
put: <MetaT>(url: string, body: Record<any, any>, options: Options = {}) => {
92+
return makeRequestInternal<MetaT>(url, 'PUT', options, body)
93+
},
94+
delete: <MetaT>(url: string, body?: Record<any, any>, options: Options = {}) => {
95+
return makeRequestInternal<MetaT>(url, 'DELETE', options, body)
96+
},
97+
}

cli/src/common/updates.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import chalk from 'chalk'
22
import { logger } from './logging'
3-
import { execSync, spawnSync } from 'child_process'
4-
import axios from 'axios'
3+
import { execSync } from 'child_process'
54
import { compareVersions } from 'compare-versions'
65
import { BaseCommand } from '../commands/connect'
76
import { Command } from 'commander'
7+
import { request } from './fetch'
88

99
let updatedVersionAvailable: string | false | undefined = undefined
1010
let message: string | undefined = undefined
@@ -44,8 +44,8 @@ export function withUpdateCheck<T extends BaseCommand>(
4444
// Start checking for updates in the background. We don't wait for this before
4545
// running the action, as we will show the result at the end
4646
function startUpdateCheck() {
47-
axios
48-
.get('https://api.github.com/repos/figma/code-connect/releases/latest')
47+
request
48+
.get<{ tag_name: string }>('https://api.github.com/repos/figma/code-connect/releases/latest')
4949
.then((response) => {
5050
const latestVersion = response.data.tag_name.replace(/^v/, '')
5151
const currentVersion = require('../../package.json').version

cli/src/connect/create.ts

+12-10
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@ import {
44
parseFileKey,
55
parseNodeIds,
66
} from './helpers'
7-
import axios, { isAxiosError } from 'axios'
87
import fs from 'fs'
9-
import { getApiUrl, getHeaders } from './figma_rest_api'
8+
import { FigmaRestApi, getApiUrl, getHeaders } from './figma_rest_api'
109
import { exitWithError, logger } from '../common/logging'
1110
import { callParser, handleMessages } from './parser_executables'
1211
import { CodeConnectExecutableParserConfig, ProjectInfo } from './project'
@@ -15,6 +14,7 @@ import { z } from 'zod'
1514
import { CreateRequestPayload, CreateResponsePayload } from './parser_executable_types'
1615
import { fromError } from 'zod-validation-error'
1716
import { createHtmlCodeConnect } from '../html/create'
17+
import { isFetchError, request } from '../common/fetch'
1818
interface GenerateDocsArgs {
1919
accessToken: string
2020
figmaNodeUrl: string
@@ -60,16 +60,16 @@ export async function createCodeConnectFromUrl({
6060
logger.info('Fetching component information from Figma...')
6161
const response = process.env.CODE_CONNECT_MOCK_CREATE_API_RESPONSE
6262
? {
63-
status: 200,
63+
response: { status: 200 },
6464
data: JSON.parse(
6565
fs.readFileSync(process.env.CODE_CONNECT_MOCK_CREATE_API_RESPONSE, 'utf-8'),
66-
),
66+
) as { document: FigmaRestApi.Node },
6767
}
68-
: await axios.get(apiUrl, {
68+
: await request.get<{ document: FigmaRestApi.Node }>(apiUrl, {
6969
headers: getHeaders(accessToken),
7070
})
7171

72-
if (response.status === 200) {
72+
if (response.response.status === 200) {
7373
logger.info('Parsing response')
7474
const component = findComponentsInDocument(response.data.document, nodeIds)[0]
7575
if (component === undefined) {
@@ -127,19 +127,21 @@ export async function createCodeConnectFromUrl({
127127
logger.info(`${file.filePath}`)
128128
})
129129
} else {
130-
logger.error(`Failed to get node information from Figma with status: ${response.status}`)
130+
logger.error(
131+
`Failed to get node information from Figma with status: ${response.response.status}`,
132+
)
131133
logger.debug('Failed to get node information from Figma with Body:', response.data)
132134
}
133135
} catch (err) {
134-
if (isAxiosError(err)) {
136+
if (isFetchError(err)) {
135137
if (err.response) {
136138
logger.error(
137-
`Failed to get node data from Figma (${err.code}): ${err.response?.status} ${err.response?.data?.err ?? err.response?.data?.message}`,
139+
`Failed to get node data from Figma (${err.response.status}): ${err.response.status} ${err.data?.err ?? err.data?.message}`,
138140
)
139141
} else {
140142
logger.error(`Failed to get node data from Figma: ${err.message}`)
141143
}
142-
logger.debug(JSON.stringify(err.response?.data))
144+
logger.debug(JSON.stringify(err.data))
143145
} else {
144146
logger.error(`Failed to create: ${err}`)
145147
}

cli/src/connect/delete_docs.ts

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import { CodeConnectJSON } from '../connect/figma_connect'
2-
import { logger, underline, highlight } from '../common/logging'
3-
import axios, { isAxiosError } from 'axios'
1+
import { isFetchError, request } from '../common/fetch'
2+
import { logger } from '../common/logging'
43
import { getApiUrl, getHeaders } from './figma_rest_api'
5-
import { get } from 'lodash'
64
import { exitWithFeedbackMessage } from './helpers'
75

86
interface NodesToDeleteInfo {
@@ -21,24 +19,27 @@ export async function delete_docs({ accessToken, docs }: Args) {
2119
try {
2220
logger.info(`Unpublishing Code Connect files from Figma...`)
2321

24-
const response = await axios.delete(apiUrl, {
25-
headers: getHeaders(accessToken),
26-
data: { nodes_to_delete: docs },
27-
})
22+
await request.delete(
23+
apiUrl,
24+
{ nodes_to_delete: docs },
25+
{
26+
headers: getHeaders(accessToken),
27+
},
28+
)
2829

2930
logger.info(
3031
`Successfully deleted:\n${docs.map((doc) => `-> ${doc.figmaNode} (${doc.label})`).join('\n')}`,
3132
)
3233
} catch (err) {
33-
if (isAxiosError(err)) {
34+
if (isFetchError(err)) {
3435
if (err.response) {
3536
logger.error(
36-
`Failed to upload to Figma (${err.code}): ${err.response?.status} ${err.response?.data?.err ?? err.response?.data?.message}`,
37+
`Failed to upload to Figma (${err.response.status}): ${err.response.status} ${err.data?.err ?? err.data?.message}`,
3738
)
3839
} else {
3940
logger.error(`Failed to upload to Figma: ${err.message}`)
4041
}
41-
logger.debug(JSON.stringify(err.response?.data))
42+
logger.debug(JSON.stringify(err.data))
4243
} else {
4344
logger.error(`Failed to delete docs: ${err}`)
4445
}

cli/src/connect/figma_rest_api.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import axios, { isAxiosError } from 'axios'
1+
import { isFetchError, request } from '../common/fetch'
22
import { logger } from '../common/logging'
33

44
const version = require('../../package.json').version
@@ -54,28 +54,30 @@ export namespace FigmaRestApi {
5454
export async function getDocument(url: string, accessToken: string): Promise<FigmaRestApi.Node> {
5555
try {
5656
logger.info('Fetching component information from Figma...')
57-
const response = await axios.get(url, {
57+
const response = await request.get<{ document: FigmaRestApi.Node }>(url, {
5858
headers: getHeaders(accessToken),
5959
})
6060

61-
if (response.status === 200) {
61+
if (response.response.status === 200) {
6262
logger.info('Successfully fetched component information from Figma')
6363
return response.data.document
6464
} else {
65-
logger.error(`Failed to get node information from Figma with status: ${response.status}`)
65+
logger.error(
66+
`Failed to get node information from Figma with status: ${response.response.status}`,
67+
)
6668
logger.debug('Failed to get node information from Figma with Body:', response.data)
6769
return Promise.reject()
6870
}
6971
} catch (err) {
70-
if (isAxiosError(err)) {
72+
if (isFetchError(err)) {
7173
if (err.response) {
7274
logger.error(
73-
`Failed to get node data from Figma (${err.code}): ${err.response?.status} ${err.response?.data?.err ?? err.response?.data?.message}`,
75+
`Failed to get node data from Figma (${err.response.status}): ${err.response.status} ${err.data?.err ?? err.data?.message}`,
7476
)
7577
} else {
7678
logger.error(`Failed to get node data from Figma: ${err.message}`)
7779
}
78-
logger.debug(JSON.stringify(err.response?.data))
80+
logger.debug(JSON.stringify(err.data))
7981
} else {
8082
logger.error(`Failed to create: ${err}`)
8183
}

cli/src/connect/helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FigmaRestApi } from './figma_rest_api'
22
import * as url from 'url'
3-
import { exitWithError, logger } from '../common/logging'
3+
import { logger } from '../common/logging'
44

55
const guidRegex = /^I?[0-9]+:[0-9]+(;[0-9]+:[0-9]+)*$/
66

cli/src/connect/intrinsics.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as ts from 'typescript'
2-
import { InternalError, ParserContext, ParserError, findDescendants } from './parser_common'
2+
import { InternalError, ParserContext, ParserError } from './parser_common'
33
import {
44
assertIsArrayLiteralExpression,
55
assertIsStringLiteral,

0 commit comments

Comments
 (0)