Skip to content

Commit 75cd319

Browse files
committed
Add wrap url
1 parent e077905 commit 75cd319

File tree

5 files changed

+46
-9
lines changed

5 files changed

+46
-9
lines changed

src/code.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ import { exportDevup, importDevup } from './commands/devup'
33
import { exportAssets } from './commands/exportAssets'
44
import { exportComponents } from './commands/exportComponents'
55

6-
export function registerCodegen(ctx: typeof figma = figma) {
6+
export function registerCodegen(ctx: typeof figma) {
77
if (ctx.editorType === 'dev' && ctx.mode === 'codegen') {
8-
ctx.codegen.on('generate', async ({ node, language }) => {
8+
ctx.codegen.on('generate', async ({ node, language, ...rest }) => {
9+
console.info(rest, node)
910
switch (language) {
1011
case 'devup-ui': {
1112
const time = Date.now()
@@ -88,4 +89,6 @@ export function run(ctx: typeof figma) {
8889
}
8990
}
9091

91-
run((globalThis as { figma?: unknown }).figma as typeof figma)
92+
if (typeof figma !== 'undefined') {
93+
run(figma)
94+
}

src/codegen/Codegen.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
getDevupComponentByNode,
1010
getDevupComponentByProps,
1111
} from './utils/get-devup-component'
12+
import { buildCssUrl } from './utils/wrap-url'
1213

1314
export class Codegen {
1415
components: Map<
@@ -80,7 +81,7 @@ export class Codegen {
8081
const maskColor = await checkSameColor(node)
8182
if (maskColor) {
8283
// support mask image icon
83-
props.maskImage = `url(${props.src})`
84+
props.maskImage = buildCssUrl(props.src)
8485
props.maskRepeat = 'no-repeat'
8586
props.maskSize = 'contain'
8687
props.bg = maskColor
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, test } from 'bun:test'
2+
import { buildCssUrl } from '../wrap-url'
3+
4+
describe('buildCssUrl', () => {
5+
test('keeps simple paths unquoted', () => {
6+
expect(buildCssUrl('/icons/logo.svg')).toBe('url(/icons/logo.svg)')
7+
})
8+
9+
test('wraps paths with spaces', () => {
10+
expect(buildCssUrl('/icons/logo icon.svg')).toBe(
11+
"url('/icons/logo icon.svg')",
12+
)
13+
})
14+
15+
test('escapes single quotes inside path', () => {
16+
expect(buildCssUrl("/icons/John's icon.svg")).toBe(
17+
"url('/icons/John\\'s icon.svg')",
18+
)
19+
})
20+
})

src/codegen/utils/paint-to-css.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { rgbaToHex } from '../../utils/rgba-to-hex'
33
import { checkAssetNode } from './check-asset-node'
44
import { fmtPct } from './fmtPct'
55
import { solidToString } from './solid-to-string'
6+
import { buildCssUrl } from './wrap-url'
67

78
interface Point {
89
x: number
@@ -48,13 +49,13 @@ function convertImage(fill: ImagePaint): string {
4849

4950
switch (fill.scaleMode) {
5051
case 'FILL':
51-
return `url(/icons/${imageName}) center/cover no-repeat`
52+
return `${buildCssUrl(`/icons/${imageName}`)} center/cover no-repeat`
5253
case 'FIT':
53-
return `url(/icons/${imageName}) center/contain no-repeat`
54+
return `${buildCssUrl(`/icons/${imageName}`)} center/contain no-repeat`
5455
case 'CROP':
55-
return `url(/icons/${imageName}) center/cover no-repeat`
56+
return `${buildCssUrl(`/icons/${imageName}`)} center/cover no-repeat`
5657
case 'TILE':
57-
return `url(/icons/${imageName}) repeat`
58+
return `${buildCssUrl(`/icons/${imageName}`)} repeat`
5859
}
5960
}
6061

@@ -195,7 +196,8 @@ async function convertPattern(fill: PatternPaint): Promise<string> {
195196
const position = [horizontalPosition, verticalPosition]
196197
.filter(Boolean)
197198
.join(' ')
198-
return `url(/icons/${imageName}.${imageExtension})${position ? ` ${position}` : ''} repeat`
199+
const url = buildCssUrl(`/icons/${imageName}.${imageExtension}`)
200+
return `${url}${position ? ` ${position}` : ''} repeat`
199201
}
200202

201203
function convertPosition(

src/codegen/utils/wrap-url.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Build a CSS url() value. If the path contains whitespace or characters
3+
* that commonly require quoting, wrap it in single quotes and escape any
4+
* existing single quotes.
5+
*/
6+
export function buildCssUrl(path: string): string {
7+
const normalized = path.trim()
8+
const needsQuotes = /[\s'"()]/.test(normalized)
9+
const escaped = normalized.replace(/'/g, "\\'")
10+
return `url(${needsQuotes ? `'${escaped}'` : escaped})`
11+
}

0 commit comments

Comments
 (0)