@teleporthq/teleport-plugin-common 0.43.46 → 0.43.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/utils/parse-string-with-template-expressions.ts +82 -0
- package/__tests__/utils/route-utils.ts +118 -0
- package/__tests__/utils/template-expression-balance.ts +83 -0
- package/dist/cjs/node-handlers/node-to-jsx/utils.d.ts.map +1 -1
- package/dist/cjs/node-handlers/node-to-jsx/utils.js +30 -7
- package/dist/cjs/node-handlers/node-to-jsx/utils.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils/ast-utils.d.ts.map +1 -1
- package/dist/cjs/utils/ast-utils.js +65 -16
- package/dist/cjs/utils/ast-utils.js.map +1 -1
- package/dist/cjs/utils/route-utils.d.ts +10 -0
- package/dist/cjs/utils/route-utils.d.ts.map +1 -1
- package/dist/cjs/utils/route-utils.js +9 -1
- package/dist/cjs/utils/route-utils.js.map +1 -1
- package/dist/cjs/utils/template-expression-balance.d.ts +58 -0
- package/dist/cjs/utils/template-expression-balance.d.ts.map +1 -0
- package/dist/cjs/utils/template-expression-balance.js +181 -0
- package/dist/cjs/utils/template-expression-balance.js.map +1 -0
- package/dist/esm/node-handlers/node-to-jsx/utils.d.ts.map +1 -1
- package/dist/esm/node-handlers/node-to-jsx/utils.js +31 -8
- package/dist/esm/node-handlers/node-to-jsx/utils.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils/ast-utils.d.ts.map +1 -1
- package/dist/esm/utils/ast-utils.js +66 -17
- package/dist/esm/utils/ast-utils.js.map +1 -1
- package/dist/esm/utils/route-utils.d.ts +10 -0
- package/dist/esm/utils/route-utils.d.ts.map +1 -1
- package/dist/esm/utils/route-utils.js +8 -0
- package/dist/esm/utils/route-utils.js.map +1 -1
- package/dist/esm/utils/template-expression-balance.d.ts +58 -0
- package/dist/esm/utils/template-expression-balance.d.ts.map +1 -0
- package/dist/esm/utils/template-expression-balance.js +174 -0
- package/dist/esm/utils/template-expression-balance.js.map +1 -0
- package/package.json +4 -4
- package/src/node-handlers/node-to-jsx/utils.ts +36 -10
- package/src/utils/ast-utils.ts +68 -17
- package/src/utils/route-utils.ts +9 -0
- package/src/utils/template-expression-balance.ts +196 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delimiter balancing for `{{ … }}` binding expressions.
|
|
3
|
+
*
|
|
4
|
+
* THE DEFECT THIS EXISTS FOR. `parseStringWithTemplateExpressions` converted a
|
|
5
|
+
* UIDL string into a template literal and then handed the result straight to
|
|
6
|
+
* Babel. When the binding inside was unbalanced the produced source did not
|
|
7
|
+
* parse, `parse()` threw, and — because the inline-style caller has no
|
|
8
|
+
* try/catch — the SyntaxError escaped all the way out and killed the ENTIRE
|
|
9
|
+
* project build:
|
|
10
|
+
*
|
|
11
|
+
* const x = `translateX(${-(cameraX || ''}px) }px)`
|
|
12
|
+
* ^ Unexpected token, expected ","
|
|
13
|
+
*
|
|
14
|
+
* Two separate faults produced that line. The interpolated expression
|
|
15
|
+
* `-(cameraX || ''` was missing its closing paren, and the "re-close the CSS
|
|
16
|
+
* function" step counted parentheses across the WHOLE string — including the
|
|
17
|
+
* ones inside `${…}` — decided one was missing, and appended `px)` to the end,
|
|
18
|
+
* which could never help because the imbalance was inside the interpolation.
|
|
19
|
+
*
|
|
20
|
+
* One bad binding in one style property must never cost a project its build.
|
|
21
|
+
* These helpers make the imbalance repairable where it actually is, and let the
|
|
22
|
+
* caller degrade a single value instead of aborting.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Closing delimiter for each opener that nests inside an expression. */
|
|
26
|
+
const CLOSER_FOR_OPENER: Record<string, string> = {
|
|
27
|
+
'(': ')',
|
|
28
|
+
'[': ']',
|
|
29
|
+
'{': '}',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const CLOSERS = new Set(Object.values(CLOSER_FOR_OPENER))
|
|
33
|
+
const QUOTES = new Set(["'", '"', '`'])
|
|
34
|
+
|
|
35
|
+
export interface ExpressionScan {
|
|
36
|
+
/** Openers still waiting for their closer, outermost first. */
|
|
37
|
+
unclosed: string[]
|
|
38
|
+
/** Closers that never had a matching opener. */
|
|
39
|
+
strayCloserCount: number
|
|
40
|
+
/** The quote character still open at the end of the scan, if any. */
|
|
41
|
+
unterminatedQuote: string | null
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Single left-to-right pass over an expression, quote and escape aware.
|
|
46
|
+
* Characters inside a string literal never affect nesting.
|
|
47
|
+
*/
|
|
48
|
+
export const scanExpression = (expression: string): ExpressionScan => {
|
|
49
|
+
const unclosed: string[] = []
|
|
50
|
+
let strayCloserCount = 0
|
|
51
|
+
let quote: string | null = null
|
|
52
|
+
|
|
53
|
+
for (let index = 0; index < expression.length; index++) {
|
|
54
|
+
const char = expression[index]
|
|
55
|
+
|
|
56
|
+
if (char === '\\') {
|
|
57
|
+
index++
|
|
58
|
+
continue
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (quote !== null) {
|
|
62
|
+
if (char === quote) {
|
|
63
|
+
quote = null
|
|
64
|
+
}
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (QUOTES.has(char)) {
|
|
69
|
+
quote = char
|
|
70
|
+
continue
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (CLOSER_FOR_OPENER[char]) {
|
|
74
|
+
unclosed.push(char)
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (CLOSERS.has(char)) {
|
|
79
|
+
const expected = unclosed.length > 0 ? CLOSER_FOR_OPENER[unclosed[unclosed.length - 1]] : null
|
|
80
|
+
if (expected === char) {
|
|
81
|
+
unclosed.pop()
|
|
82
|
+
} else {
|
|
83
|
+
strayCloserCount++
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return { unclosed, strayCloserCount, unterminatedQuote: quote }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** True when every quote and bracket closes and no closer is stray. */
|
|
92
|
+
export const isBalancedExpression = (expression: string): boolean => {
|
|
93
|
+
const scan = scanExpression(expression)
|
|
94
|
+
return (
|
|
95
|
+
scan.unclosed.length === 0 && scan.strayCloserCount === 0 && scan.unterminatedQuote === null
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Close an expression that ends mid-quote or mid-bracket:
|
|
101
|
+
* `-(cameraX || ''` → `-(cameraX || '')`.
|
|
102
|
+
*
|
|
103
|
+
* Returns the expression unchanged when it is already balanced, and when the
|
|
104
|
+
* imbalance is a STRAY CLOSER — appending cannot fix a missing opener, and
|
|
105
|
+
* inventing one would be a guess.
|
|
106
|
+
*/
|
|
107
|
+
export const balanceExpression = (expression: string): string => {
|
|
108
|
+
const scan = scanExpression(expression)
|
|
109
|
+
if (scan.strayCloserCount > 0) {
|
|
110
|
+
return expression
|
|
111
|
+
}
|
|
112
|
+
if (scan.unclosed.length === 0 && scan.unterminatedQuote === null) {
|
|
113
|
+
return expression
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const suffix = [scan.unterminatedQuote || '', ...unclosedInReverse(scan.unclosed)].join('')
|
|
117
|
+
|
|
118
|
+
return expression + suffix
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const unclosedInReverse = (unclosed: string[]): string[] =>
|
|
122
|
+
unclosed
|
|
123
|
+
.slice()
|
|
124
|
+
.reverse()
|
|
125
|
+
.map((opener) => CLOSER_FOR_OPENER[opener])
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Parenthesis balance of the STATIC parts of a template string — everything
|
|
129
|
+
* outside `${…}`.
|
|
130
|
+
*
|
|
131
|
+
* This is the number the "re-close the CSS function" step needs. Counting over
|
|
132
|
+
* the whole string mixes in the expressions' own parentheses, so an imbalance
|
|
133
|
+
* INSIDE an interpolation reads as a missing close on the CSS function and the
|
|
134
|
+
* step appends a unit and a `)` that belong nowhere.
|
|
135
|
+
*/
|
|
136
|
+
export const countUnclosedStaticParens = (templateStr: string): number => {
|
|
137
|
+
let depth = 0
|
|
138
|
+
let index = 0
|
|
139
|
+
|
|
140
|
+
while (index < templateStr.length) {
|
|
141
|
+
if (templateStr[index] === '$' && templateStr[index + 1] === '{') {
|
|
142
|
+
index = skipInterpolation(templateStr, index + 2)
|
|
143
|
+
continue
|
|
144
|
+
}
|
|
145
|
+
if (templateStr[index] === '(') {
|
|
146
|
+
depth++
|
|
147
|
+
} else if (templateStr[index] === ')') {
|
|
148
|
+
depth--
|
|
149
|
+
}
|
|
150
|
+
index++
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return depth > 0 ? depth : 0
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Index just past the `}` that closes an interpolation opened at `start`.
|
|
158
|
+
* Tracks nested braces and string literals so `${ {a:'}'} }` is skipped whole.
|
|
159
|
+
* An unterminated interpolation consumes the rest of the string.
|
|
160
|
+
*/
|
|
161
|
+
const skipInterpolation = (templateStr: string, start: number): number => {
|
|
162
|
+
let depth = 1
|
|
163
|
+
let quote: string | null = null
|
|
164
|
+
|
|
165
|
+
for (let index = start; index < templateStr.length; index++) {
|
|
166
|
+
const char = templateStr[index]
|
|
167
|
+
|
|
168
|
+
if (char === '\\') {
|
|
169
|
+
index++
|
|
170
|
+
continue
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (quote !== null) {
|
|
174
|
+
if (char === quote) {
|
|
175
|
+
quote = null
|
|
176
|
+
}
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (QUOTES.has(char)) {
|
|
181
|
+
quote = char
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (char === '{') {
|
|
186
|
+
depth++
|
|
187
|
+
} else if (char === '}') {
|
|
188
|
+
depth--
|
|
189
|
+
if (depth === 0) {
|
|
190
|
+
return index + 1
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return templateStr.length
|
|
196
|
+
}
|