@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.
Files changed (38) hide show
  1. package/__tests__/utils/parse-string-with-template-expressions.ts +82 -0
  2. package/__tests__/utils/route-utils.ts +118 -0
  3. package/__tests__/utils/template-expression-balance.ts +83 -0
  4. package/dist/cjs/node-handlers/node-to-jsx/utils.d.ts.map +1 -1
  5. package/dist/cjs/node-handlers/node-to-jsx/utils.js +30 -7
  6. package/dist/cjs/node-handlers/node-to-jsx/utils.js.map +1 -1
  7. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  8. package/dist/cjs/utils/ast-utils.d.ts.map +1 -1
  9. package/dist/cjs/utils/ast-utils.js +65 -16
  10. package/dist/cjs/utils/ast-utils.js.map +1 -1
  11. package/dist/cjs/utils/route-utils.d.ts +10 -0
  12. package/dist/cjs/utils/route-utils.d.ts.map +1 -1
  13. package/dist/cjs/utils/route-utils.js +9 -1
  14. package/dist/cjs/utils/route-utils.js.map +1 -1
  15. package/dist/cjs/utils/template-expression-balance.d.ts +58 -0
  16. package/dist/cjs/utils/template-expression-balance.d.ts.map +1 -0
  17. package/dist/cjs/utils/template-expression-balance.js +181 -0
  18. package/dist/cjs/utils/template-expression-balance.js.map +1 -0
  19. package/dist/esm/node-handlers/node-to-jsx/utils.d.ts.map +1 -1
  20. package/dist/esm/node-handlers/node-to-jsx/utils.js +31 -8
  21. package/dist/esm/node-handlers/node-to-jsx/utils.js.map +1 -1
  22. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  23. package/dist/esm/utils/ast-utils.d.ts.map +1 -1
  24. package/dist/esm/utils/ast-utils.js +66 -17
  25. package/dist/esm/utils/ast-utils.js.map +1 -1
  26. package/dist/esm/utils/route-utils.d.ts +10 -0
  27. package/dist/esm/utils/route-utils.d.ts.map +1 -1
  28. package/dist/esm/utils/route-utils.js +8 -0
  29. package/dist/esm/utils/route-utils.js.map +1 -1
  30. package/dist/esm/utils/template-expression-balance.d.ts +58 -0
  31. package/dist/esm/utils/template-expression-balance.d.ts.map +1 -0
  32. package/dist/esm/utils/template-expression-balance.js +174 -0
  33. package/dist/esm/utils/template-expression-balance.js.map +1 -0
  34. package/package.json +4 -4
  35. package/src/node-handlers/node-to-jsx/utils.ts +36 -10
  36. package/src/utils/ast-utils.ts +68 -17
  37. package/src/utils/route-utils.ts +9 -0
  38. 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
+ }