@teleporthq/teleport-uidl-resolver 0.43.43 → 0.43.46

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 (57) hide show
  1. package/__tests__/html-mapping/index.ts +28 -0
  2. package/__tests__/unbound-expressions/expression-fallback.ts +64 -0
  3. package/__tests__/unbound-expressions/expression-identifiers.ts +47 -4
  4. package/__tests__/unbound-expressions/index.ts +216 -24
  5. package/dist/cjs/html-mapping.d.ts.map +1 -1
  6. package/dist/cjs/html-mapping.js +8 -0
  7. package/dist/cjs/html-mapping.js.map +1 -1
  8. package/dist/cjs/resolvers/unbound-expressions/ambient-identifiers.d.ts +32 -10
  9. package/dist/cjs/resolvers/unbound-expressions/ambient-identifiers.d.ts.map +1 -1
  10. package/dist/cjs/resolvers/unbound-expressions/ambient-identifiers.js +34 -27
  11. package/dist/cjs/resolvers/unbound-expressions/ambient-identifiers.js.map +1 -1
  12. package/dist/cjs/resolvers/unbound-expressions/expression-fallback.d.ts +22 -0
  13. package/dist/cjs/resolvers/unbound-expressions/expression-fallback.d.ts.map +1 -0
  14. package/dist/cjs/resolvers/unbound-expressions/expression-fallback.js +129 -0
  15. package/dist/cjs/resolvers/unbound-expressions/expression-fallback.js.map +1 -0
  16. package/dist/cjs/resolvers/unbound-expressions/expression-identifiers.d.ts +30 -16
  17. package/dist/cjs/resolvers/unbound-expressions/expression-identifiers.d.ts.map +1 -1
  18. package/dist/cjs/resolvers/unbound-expressions/expression-identifiers.js +130 -144
  19. package/dist/cjs/resolvers/unbound-expressions/expression-identifiers.js.map +1 -1
  20. package/dist/cjs/resolvers/unbound-expressions/expression-lexer.d.ts +41 -0
  21. package/dist/cjs/resolvers/unbound-expressions/expression-lexer.d.ts.map +1 -0
  22. package/dist/cjs/resolvers/unbound-expressions/expression-lexer.js +158 -0
  23. package/dist/cjs/resolvers/unbound-expressions/expression-lexer.js.map +1 -0
  24. package/dist/cjs/resolvers/unbound-expressions/index.d.ts.map +1 -1
  25. package/dist/cjs/resolvers/unbound-expressions/index.js +119 -61
  26. package/dist/cjs/resolvers/unbound-expressions/index.js.map +1 -1
  27. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  28. package/dist/esm/html-mapping.d.ts.map +1 -1
  29. package/dist/esm/html-mapping.js +8 -0
  30. package/dist/esm/html-mapping.js.map +1 -1
  31. package/dist/esm/resolvers/unbound-expressions/ambient-identifiers.d.ts +32 -10
  32. package/dist/esm/resolvers/unbound-expressions/ambient-identifiers.d.ts.map +1 -1
  33. package/dist/esm/resolvers/unbound-expressions/ambient-identifiers.js +33 -26
  34. package/dist/esm/resolvers/unbound-expressions/ambient-identifiers.js.map +1 -1
  35. package/dist/esm/resolvers/unbound-expressions/expression-fallback.d.ts +22 -0
  36. package/dist/esm/resolvers/unbound-expressions/expression-fallback.d.ts.map +1 -0
  37. package/dist/esm/resolvers/unbound-expressions/expression-fallback.js +125 -0
  38. package/dist/esm/resolvers/unbound-expressions/expression-fallback.js.map +1 -0
  39. package/dist/esm/resolvers/unbound-expressions/expression-identifiers.d.ts +30 -16
  40. package/dist/esm/resolvers/unbound-expressions/expression-identifiers.d.ts.map +1 -1
  41. package/dist/esm/resolvers/unbound-expressions/expression-identifiers.js +125 -139
  42. package/dist/esm/resolvers/unbound-expressions/expression-identifiers.js.map +1 -1
  43. package/dist/esm/resolvers/unbound-expressions/expression-lexer.d.ts +41 -0
  44. package/dist/esm/resolvers/unbound-expressions/expression-lexer.d.ts.map +1 -0
  45. package/dist/esm/resolvers/unbound-expressions/expression-lexer.js +151 -0
  46. package/dist/esm/resolvers/unbound-expressions/expression-lexer.js.map +1 -0
  47. package/dist/esm/resolvers/unbound-expressions/index.d.ts.map +1 -1
  48. package/dist/esm/resolvers/unbound-expressions/index.js +121 -63
  49. package/dist/esm/resolvers/unbound-expressions/index.js.map +1 -1
  50. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +5 -5
  52. package/src/html-mapping.ts +8 -0
  53. package/src/resolvers/unbound-expressions/ambient-identifiers.ts +33 -26
  54. package/src/resolvers/unbound-expressions/expression-fallback.ts +141 -0
  55. package/src/resolvers/unbound-expressions/expression-identifiers.ts +160 -138
  56. package/src/resolvers/unbound-expressions/expression-lexer.ts +156 -0
  57. package/src/resolvers/unbound-expressions/index.ts +134 -65
@@ -0,0 +1,28 @@
1
+ import Resolver from '../../src/resolver'
2
+ import { HTMLMapping } from '../../src/html-mapping'
3
+ import { element, staticNode } from '@teleporthq/teleport-uidl-builders'
4
+
5
+ /**
6
+ * The Collapsible Text primitive decomposes to plain elements in the UIDL, but
7
+ * its root keeps the semantic `collapsible-text` elementType. Without a base
8
+ * mapping the codegen would emit a literal `<collapsible-text>` custom element,
9
+ * which renders inline (dropping the authored width/box styles) and breaks the
10
+ * `-webkit-line-clamp` overflow measurement the shipped `TqCollapsibleTextOverflow`
11
+ * helper relies on. This locks the primitive -> `div` decomposition in place.
12
+ */
13
+ describe('HTMLMapping — primitive decompositions', () => {
14
+ const resolveElementType = (elementType: string): string => {
15
+ const resolver = new Resolver()
16
+ resolver.addMapping(HTMLMapping)
17
+ return resolver.resolveElement(element(elementType, { dummy: staticNode('kept') })).elementType
18
+ }
19
+
20
+ it('maps the collapsible-text primitive to a div, not a custom element', () => {
21
+ expect(resolveElementType('collapsible-text')).toBe('div')
22
+ })
23
+
24
+ it('keeps the sibling block primitives mapped to div', () => {
25
+ expect(resolveElementType('markdown-node')).toBe('div')
26
+ expect(resolveElementType('rich-text-editor-node')).toBe('div')
27
+ })
28
+ })
@@ -0,0 +1,64 @@
1
+ import { extractFallbackLiteral } from '../../src/resolvers/unbound-expressions/expression-fallback'
2
+
3
+ describe('extractFallbackLiteral', () => {
4
+ it('returns the string literal an expression falls back to', () => {
5
+ expect(extractFallbackLiteral('company?.name || "N/A"')).toBe('N/A')
6
+ expect(extractFallbackLiteral("deal?.stage || 'Unassigned'")).toBe('Unassigned')
7
+ expect(extractFallbackLiteral('company?.logo ?? "https://cdn/placeholder.png"')).toBe(
8
+ 'https://cdn/placeholder.png'
9
+ )
10
+ })
11
+
12
+ it('returns the last fallback of a chain', () => {
13
+ expect(extractFallbackLiteral('a || b || "last"')).toBe('last')
14
+ expect(extractFallbackLiteral('a ?? b ?? "last"')).toBe('last')
15
+ })
16
+
17
+ it('returns numeric fallbacks verbatim', () => {
18
+ expect(extractFallbackLiteral('parseFloat(item.price) || 0')).toBe('0')
19
+ expect(extractFallbackLiteral('item.ratio || 1.5')).toBe('1.5')
20
+ expect(extractFallbackLiteral('item.delta || -1')).toBe('-1')
21
+ expect(extractFallbackLiteral('item.delta || +1')).toBe('1')
22
+ })
23
+
24
+ it('accepts a template literal with no interpolation', () => {
25
+ expect(extractFallbackLiteral('item.label || `Untitled`')).toBe('Untitled')
26
+ })
27
+
28
+ it('rejects a template literal that still reads scope', () => {
29
+ // tslint:disable-next-line no-invalid-template-strings
30
+ expect(extractFallbackLiteral('item.label || `${item.id}`')).toBeNull()
31
+ })
32
+
33
+ it('ignores operators that live inside literals', () => {
34
+ expect(extractFallbackLiteral(`company?.size || "size || '-'"`)).toBe(`size || '-'`)
35
+ expect(extractFallbackLiteral(`"a || b"`)).toBeNull()
36
+ })
37
+
38
+ it('ignores operators nested inside parentheses, brackets or braces', () => {
39
+ expect(extractFallbackLiteral('fn(a || "inner")')).toBeNull()
40
+ expect(extractFallbackLiteral('map[a || "inner"]')).toBeNull()
41
+ expect(extractFallbackLiteral('{ key: a || "inner" }')).toBeNull()
42
+ })
43
+
44
+ it('rejects a fallback that is not a standalone literal', () => {
45
+ expect(extractFallbackLiteral('company?.name || buildName(company)')).toBeNull()
46
+ expect(extractFallbackLiteral(`company?.name || (a ? 'x' : 'y')`)).toBeNull()
47
+ expect(extractFallbackLiteral('company?.flag || true')).toBeNull()
48
+ })
49
+
50
+ it('does not confuse optional chaining or a ternary with a nullish fallback', () => {
51
+ expect(extractFallbackLiteral(`company?.name`)).toBeNull()
52
+ expect(extractFallbackLiteral(`company ? 'yes' : 'no'`)).toBeNull()
53
+ })
54
+
55
+ it('decodes escape sequences in the literal', () => {
56
+ expect(extractFallbackLiteral(`a || 'It\\'s here'`)).toBe("It's here")
57
+ expect(extractFallbackLiteral('a || "line\\nbreak"')).toBe('line\nbreak')
58
+ })
59
+
60
+ it('returns null for empty or non-string input', () => {
61
+ expect(extractFallbackLiteral('')).toBeNull()
62
+ expect(extractFallbackLiteral(undefined as unknown as string)).toBeNull()
63
+ })
64
+ })
@@ -1,9 +1,11 @@
1
- import { extractRootIdentifiers } from '../../src/resolvers/unbound-expressions/expression-identifiers'
1
+ import { analyzeExpressionScope } from '../../src/resolvers/unbound-expressions/expression-identifiers'
2
2
 
3
3
  const roots = (expression: string): string[] =>
4
- Array.from(extractRootIdentifiers(expression)).sort()
4
+ Array.from(analyzeExpressionScope(expression).freeIdentifiers).sort()
5
5
 
6
- describe('extractRootIdentifiers', () => {
6
+ const isResolvable = (expression: string): boolean => analyzeExpressionScope(expression).resolvable
7
+
8
+ describe('analyzeExpressionScope', () => {
7
9
  it('returns the leading identifier of a member expression', () => {
8
10
  expect(roots('cat.name')).toEqual(['cat'])
9
11
  expect(roots('loc.location_id')).toEqual(['loc'])
@@ -56,6 +58,47 @@ describe('extractRootIdentifiers', () => {
56
58
 
57
59
  it('returns an empty set for empty or non-string input', () => {
58
60
  expect(roots('')).toEqual([])
59
- expect(Array.from(extractRootIdentifiers(undefined as unknown as string))).toEqual([])
61
+ expect(
62
+ Array.from(analyzeExpressionScope(undefined as unknown as string).freeIdentifiers)
63
+ ).toEqual([])
64
+ })
65
+
66
+ it('subtracts identifiers bound by an arrow function parameter list', () => {
67
+ expect(roots('(event) => event.target.value')).toEqual([])
68
+ expect(roots('item => item.id')).toEqual([])
69
+ expect(roots('rows.map((row, i) => row.name + i)').sort()).toEqual(['rows'])
70
+ expect(roots('(a, b) => a + b + outer').sort()).toEqual(['outer'])
71
+ })
72
+
73
+ it('subtracts identifiers bound by a function expression', () => {
74
+ expect(roots('(function (a) { return a })')).toEqual([])
75
+ expect(roots('(function total(a) { return total(a) })')).toEqual([])
76
+ expect(roots('(function (a) { return a + outer })')).toEqual(['outer'])
77
+ })
78
+
79
+ it('keeps nested arrow scopes independent of the outer expression', () => {
80
+ // tslint:disable-next-line no-invalid-template-strings
81
+ expect(roots('groups.map((group) => group.items.map((entry) => entry.id))')).toEqual(['groups'])
82
+ })
83
+
84
+ it('still sees references that only appear outside a callback', () => {
85
+ expect(roots('outer.list.filter((x) => x.ok)').sort()).toEqual(['outer'])
86
+ })
87
+
88
+ it('reports block-scoped declarations as unresolvable rather than guessing', () => {
89
+ expect(isResolvable('(() => { const total = 1; return total })()')).toBe(false)
90
+ expect(isResolvable('(() => { let total = 1; return total })()')).toBe(false)
91
+ expect(isResolvable('(() => { var total = 1; return total })()')).toBe(false)
92
+ expect(isResolvable('props.value')).toBe(true)
93
+ expect(isResolvable('(event) => event.target.value')).toBe(true)
94
+ })
95
+
96
+ it('does not mistake a property named like a keyword for a declaration', () => {
97
+ expect(isResolvable('props.constant')).toBe(true)
98
+ expect(roots('props.constant')).toEqual(['props'])
99
+ // Reserved words are legal property names.
100
+ expect(isResolvable('props.const')).toBe(true)
101
+ expect(roots('props.const')).toEqual(['props'])
102
+ expect(roots('props.function + Math.max(a, b)').sort()).toEqual(['Math', 'a', 'b', 'props'])
60
103
  })
61
104
  })
@@ -24,6 +24,23 @@ const buildComponent = (node: UIDLElementNode): ComponentUIDL =>
24
24
  },
25
25
  } as unknown as ComponentUIDL)
26
26
 
27
+ const containerWith = (children: UIDLNode[]): UIDLElementNode => ({
28
+ type: 'element',
29
+ content: { elementType: 'container', children },
30
+ })
31
+
32
+ // `console.warn` is the resolver's diagnostic channel; silence it so the suite
33
+ // output stays readable while still exercising the code path.
34
+ let warnSpy: jest.SpyInstance
35
+
36
+ beforeEach(() => {
37
+ warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined)
38
+ })
39
+
40
+ afterEach(() => {
41
+ warnSpy.mockRestore()
42
+ })
43
+
27
44
  describe('resolveUnboundExpressions', () => {
28
45
  it('neutralises an orphaned option expression that references an unbound iterator', () => {
29
46
  const option = optionWith(expr('cat.name'))
@@ -54,16 +71,65 @@ describe('resolveUnboundExpressions', () => {
54
71
  },
55
72
  },
56
73
  } as UIDLNode
57
- const uidl = buildComponent({
58
- type: 'element',
59
- content: { elementType: 'container', children: [repeater] },
60
- })
74
+ const uidl = buildComponent(containerWith([repeater]))
61
75
 
62
76
  resolveUnboundExpressions(uidl)
63
77
 
64
78
  expect(boundExpr).toEqual({ type: 'expr', content: 'item?.id' })
65
79
  })
66
80
 
81
+ it('keeps the `index` a list repeater binds alongside its render prop', () => {
82
+ const indexExpr = expr('index + 1')
83
+ const repeater: UIDLNode = {
84
+ type: 'cms-list-repeater',
85
+ content: {
86
+ elementType: 'fragment',
87
+ name: 'list',
88
+ key: 'list',
89
+ renderPropIdentifier: 'item',
90
+ nodes: {
91
+ list: {
92
+ type: 'element',
93
+ content: { elementType: 'div', children: [indexExpr] },
94
+ },
95
+ },
96
+ },
97
+ } as UIDLNode
98
+ const uidl = buildComponent(containerWith([repeater]))
99
+
100
+ resolveUnboundExpressions(uidl)
101
+
102
+ expect(indexExpr).toEqual({ type: 'expr', content: 'index + 1' })
103
+ })
104
+
105
+ it('keeps `index` inside a repeat that opts into it and drops it otherwise', () => {
106
+ const indexed: UIDLElementNode = {
107
+ type: 'element',
108
+ content: { elementType: 'div', children: [expr('index')] },
109
+ }
110
+ const notIndexed: UIDLElementNode = {
111
+ type: 'element',
112
+ content: { elementType: 'div', children: [expr('index')] },
113
+ }
114
+ const repeatNode = (child: UIDLElementNode, useIndex: boolean): UIDLNode =>
115
+ ({
116
+ type: 'repeat',
117
+ content: {
118
+ node: child,
119
+ dataSource: { type: 'dynamic', content: { referenceType: 'prop', id: 'inventoryItem' } },
120
+ meta: { iteratorName: 'row', useIndex },
121
+ },
122
+ } as UIDLNode)
123
+ const uidl = buildComponent(
124
+ containerWith([repeatNode(indexed, true), repeatNode(notIndexed, false)])
125
+ )
126
+
127
+ resolveUnboundExpressions(uidl)
128
+
129
+ expect(indexed.content.children[0]).toEqual({ type: 'expr', content: 'index' })
130
+ expect(notIndexed.content.children[0]).toEqual({ type: 'static', content: '' })
131
+ })
132
+
67
133
  it('flags a repeater iterator referenced outside of its subtree', () => {
68
134
  const escaped = optionWith(expr('item?.id'))
69
135
  const repeater: UIDLNode = {
@@ -78,35 +144,49 @@ describe('resolveUnboundExpressions', () => {
78
144
  },
79
145
  },
80
146
  } as UIDLNode
81
- const uidl = buildComponent({
82
- type: 'element',
83
- content: { elementType: 'container', children: [repeater, escaped] },
84
- })
147
+ const uidl = buildComponent(containerWith([repeater, escaped]))
85
148
 
86
149
  resolveUnboundExpressions(uidl)
87
150
 
88
151
  expect(escaped.content.children[0]).toEqual({ type: 'static', content: '' })
89
152
  })
90
153
 
91
- it('keeps expressions that reference props, state, globals and event', () => {
154
+ it('keeps expressions that reference props, state and globals', () => {
92
155
  const propsExpr = expr('props.inventoryItem?.name')
93
156
  const stateExpr = expr('categoryOptions')
94
- const globalExpr = expr('JSON.stringify(params)')
95
- const eventExpr = expr('event.target.value')
96
- const uidl = buildComponent({
97
- type: 'element',
98
- content: {
99
- elementType: 'container',
100
- children: [propsExpr, stateExpr, globalExpr, eventExpr],
101
- },
102
- })
157
+ const globalExpr = expr('JSON.stringify(props.inventoryItem)')
158
+ const uidl = buildComponent(containerWith([propsExpr, stateExpr, globalExpr]))
103
159
 
104
160
  resolveUnboundExpressions(uidl)
105
161
 
106
162
  expect(propsExpr).toEqual({ type: 'expr', content: 'props.inventoryItem?.name' })
107
163
  expect(stateExpr).toEqual({ type: 'expr', content: 'categoryOptions' })
108
- expect(globalExpr).toEqual({ type: 'expr', content: 'JSON.stringify(params)' })
109
- expect(eventExpr).toEqual({ type: 'expr', content: 'event.target.value' })
164
+ expect(globalExpr).toEqual({ type: 'expr', content: 'JSON.stringify(props.inventoryItem)' })
165
+ })
166
+
167
+ it('neutralises the renderer-only `state` object, which generated code never declares', () => {
168
+ // Regression: React state is destructured into bare variables, so a `state`
169
+ // reference that survived the editor export compiles to
170
+ // `ReferenceError: state is not defined` and fails `next build`.
171
+ const container = containerWith([expr('state'), expr('state.addressLine')])
172
+ const uidl = buildComponent(container)
173
+
174
+ resolveUnboundExpressions(uidl)
175
+
176
+ expect(container.content.children).toEqual([
177
+ { type: 'static', content: '' },
178
+ { type: 'static', content: '' },
179
+ ])
180
+ })
181
+
182
+ it('keeps a state named `state` when the component actually declares it', () => {
183
+ const container = containerWith([expr('state')])
184
+ const uidl = buildComponent(container)
185
+ uidl.stateDefinitions.state = { type: 'string', defaultValue: '' }
186
+
187
+ resolveUnboundExpressions(uidl)
188
+
189
+ expect(container.content.children).toEqual([{ type: 'expr', content: 'state' }])
110
190
  })
111
191
 
112
192
  it('neutralises an unbound expression used as an attribute value', () => {
@@ -147,6 +227,79 @@ describe('resolveUnboundExpressions', () => {
147
227
  ])
148
228
  })
149
229
 
230
+ it('keeps the literal fallback an unbound expression was authored with', () => {
231
+ const srcAttr: UIDLElementNode = {
232
+ type: 'element',
233
+ content: {
234
+ elementType: 'image',
235
+ attrs: { src: { type: 'expr', content: `company?.logo ?? 'https://cdn/placeholder.png'` } },
236
+ children: [],
237
+ },
238
+ }
239
+ const container = containerWith([expr(`company?.name || "N/A"`), srcAttr])
240
+ const uidl = buildComponent(container)
241
+
242
+ resolveUnboundExpressions(uidl)
243
+
244
+ expect(container.content.children[0]).toEqual({ type: 'static', content: 'N/A' })
245
+ expect(srcAttr.content.attrs!.src).toEqual({
246
+ type: 'static',
247
+ content: 'https://cdn/placeholder.png',
248
+ })
249
+ })
250
+
251
+ it('does not mistake a fallback-looking literal inside a string for a real fallback', () => {
252
+ const container = containerWith([expr(`company?.size || "size || '-'"`)])
253
+ const uidl = buildComponent(container)
254
+
255
+ resolveUnboundExpressions(uidl)
256
+
257
+ expect(container.content.children[0]).toEqual({ type: 'static', content: `size || '-'` })
258
+ })
259
+
260
+ it('blanks rather than guesses when the fallback is not a plain literal', () => {
261
+ const container = containerWith([expr('company?.name || buildName(company)')])
262
+ const uidl = buildComponent(container)
263
+
264
+ resolveUnboundExpressions(uidl)
265
+
266
+ expect(container.content.children[0]).toEqual({ type: 'static', content: '' })
267
+ })
268
+
269
+ it('keeps an inline callback whose parameter is declared by the expression itself', () => {
270
+ const callbackExpr = expr('(event) => event.target.value')
271
+ // tslint:disable-next-line no-invalid-template-strings
272
+ const arrowExpr = expr('props.inventoryItem.tags.map((tag, i) => `${i}:${tag}`).join()')
273
+ const container = containerWith([callbackExpr, arrowExpr])
274
+ const uidl = buildComponent(container)
275
+
276
+ resolveUnboundExpressions(uidl)
277
+
278
+ expect(container.content.children).toEqual([callbackExpr, arrowExpr])
279
+ })
280
+
281
+ it('leaves an expression alone when it declares bindings the analyser cannot resolve', () => {
282
+ const iife = expr('(() => { const total = 2; return total })()')
283
+ const container = containerWith([iife])
284
+ const uidl = buildComponent(container)
285
+
286
+ resolveUnboundExpressions(uidl)
287
+
288
+ expect(container.content.children).toEqual([iife])
289
+ })
290
+
291
+ it('does not treat quoted JSON payloads as references', () => {
292
+ const filterExpr = expr(
293
+ `JSON.stringify([{ "type": "condition", "source": "id", "operand": "=" }])`
294
+ )
295
+ const container = containerWith([filterExpr])
296
+ const uidl = buildComponent(container)
297
+
298
+ resolveUnboundExpressions(uidl)
299
+
300
+ expect(container.content.children).toEqual([filterExpr])
301
+ })
302
+
150
303
  it('keeps a navlink transitionTo that references a global-context identifier', () => {
151
304
  // Regression: the abilities resolver emits transitionTo exprs like
152
305
  // `/profile/${currentUser?.id}` for details navlinks bound to
@@ -163,10 +316,7 @@ describe('resolveUnboundExpressions', () => {
163
316
  children: [],
164
317
  },
165
318
  }
166
- const uidl = buildComponent({
167
- type: 'element',
168
- content: { elementType: 'container', children: [link] },
169
- })
319
+ const uidl = buildComponent(containerWith([link]))
170
320
 
171
321
  resolveUnboundExpressions(uidl)
172
322
 
@@ -176,4 +326,46 @@ describe('resolveUnboundExpressions', () => {
176
326
  content: '`/profile/${currentUser?.id}`',
177
327
  })
178
328
  })
329
+
330
+ it('neutralises an unbound reference inside a template literal', () => {
331
+ const link: UIDLElementNode = {
332
+ type: 'element',
333
+ content: {
334
+ elementType: 'navlink',
335
+ attrs: {
336
+ // tslint:disable-next-line no-invalid-template-strings
337
+ transitionTo: { type: 'expr', content: '`/deal-details/${deal?.id}`' },
338
+ },
339
+ children: [],
340
+ },
341
+ }
342
+ const uidl = buildComponent(containerWith([link]))
343
+
344
+ resolveUnboundExpressions(uidl)
345
+
346
+ expect(link.content.attrs.transitionTo).toEqual({ type: 'static', content: '' })
347
+ })
348
+
349
+ it('keeps the render prop a data-source node introduces for its children', () => {
350
+ const boundExpr = expr('dataSourceData?.title')
351
+ const dataSource: UIDLNode = {
352
+ type: 'data-source-item',
353
+ content: {
354
+ elementType: 'DataProvider',
355
+ renderPropIdentifier: 'dataSourceData',
356
+ resourceDefinition: {
357
+ type: 'external-data-source',
358
+ dataSourceId: 'ds-1',
359
+ tableName: 'items',
360
+ dataSourceType: 'postgresql',
361
+ },
362
+ children: [{ type: 'element', content: { elementType: 'div', children: [boundExpr] } }],
363
+ },
364
+ } as UIDLNode
365
+ const uidl = buildComponent(containerWith([dataSource]))
366
+
367
+ resolveUnboundExpressions(uidl)
368
+
369
+ expect(boundExpr).toEqual({ type: 'expr', content: 'dataSourceData?.title' })
370
+ })
179
371
  })
@@ -1 +1 @@
1
- {"version":3,"file":"html-mapping.d.ts","sourceRoot":"","sources":["../../src/html-mapping.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAEpD,eAAO,MAAM,WAAW,EAAE,OAqOzB,CAAA"}
1
+ {"version":3,"file":"html-mapping.d.ts","sourceRoot":"","sources":["../../src/html-mapping.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,4BAA4B,CAAA;AAEpD,eAAO,MAAM,WAAW,EAAE,OA6OzB,CAAA"}
@@ -40,6 +40,14 @@ exports.HTMLMapping = {
40
40
  'rich-text-editor-node': {
41
41
  elementType: 'div',
42
42
  },
43
+ // The Collapsible Text primitive decomposes to plain elements; its root is a
44
+ // generic block wrapper. Without this mapping the codegen would emit a literal
45
+ // `<collapsible-text>` custom element, which renders inline (dropping the
46
+ // authored width/box styles) and breaks the `-webkit-line-clamp` overflow
47
+ // measurement the shipped `TqCollapsibleTextOverflow` helper relies on.
48
+ 'collapsible-text': {
49
+ elementType: 'div',
50
+ },
43
51
  'date-time-node': {
44
52
  elementType: 'date-time-primitive',
45
53
  dependency: {
@@ -1 +1 @@
1
- {"version":3,"file":"html-mapping.js","sourceRoot":"","sources":["../../src/html-mapping.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"html-mapping.js","sourceRoot":"","sources":["../../src/html-mapping.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,39 @@
1
1
  /**
2
- * Root identifiers that are always in scope inside a generated component and
3
- * therefore can never be an "unbound" reference:
2
+ * Root identifiers that are always in scope where a UIDL `expr` node is
3
+ * rendered — i.e. in the component's RENDER BODY, which is the only place this
4
+ * resolver inspects (element children, element attributes and the link ability
5
+ * the abilities resolver has not yet folded into attributes).
4
6
  *
5
- * - values the code generator injects into the render scope (`props`, `state`,
6
- * `event`, `params`, `context`, `metadata`, and common map callback vars),
7
- * - standard JavaScript globals / built-ins.
7
+ * Membership here is a promise that the code generator declares the name. It is
8
+ * therefore limited to:
8
9
  *
9
- * Iterator variables introduced by repeaters (`item`, `kpi`, ...) are NOT
10
- * listed here — they are tracked positionally as the walker descends into a
11
- * repeater's subtree, so an iterator reference that escapes its repeater is
12
- * still correctly flagged as unbound.
10
+ * - `props`, the render function's own parameter,
11
+ * - the global-context values the JSX emitter destructures from
12
+ * `useGlobalContext()` whenever it sees them in an expression
13
+ * (`GLOBAL_EXPRESSION_IDENTIFIERS` in teleport-plugin-common node-to-jsx),
14
+ * - standard JavaScript / browser globals.
15
+ *
16
+ * Deliberately NOT here:
17
+ *
18
+ * - `state` — React state is destructured into bare variables
19
+ * (`dynamicReferencePrefixMap.state` is `''`), so there is no `state` object
20
+ * at runtime and `state` / `state.x` in an expression is always a
21
+ * `ReferenceError`. Individual state names come from `stateDefinitions`.
22
+ * - `params`, `index` and render-prop names — these ARE bound, but only inside
23
+ * the subtree of the node that introduces them (a repeater's `.map()`
24
+ * callback, a CMS/data-source `renderSuccess` prop). The walker tracks them
25
+ * positionally so a reference that escapes its owner is still caught.
26
+ * - `event` / `evt` / `e` — bound only inside event handlers, which this
27
+ * resolver does not visit. When one appears in a render-scope expression it
28
+ * is either an inline callback parameter (the scope analyser sees the
29
+ * binding) or genuinely undeclared.
30
+ * - `context`, `ctx`, `metadata`, `record`, `idx`, `key`, `i` — no generator
31
+ * ever declares these names in a render body.
32
+ *
33
+ * Reserved words and literals (`true`, `null`, `undefined`, …) are filtered out
34
+ * by the lexer itself and need no entry here.
13
35
  */
14
- export declare const AMBIENT_IDENTIFIERS: Set<string>;
36
+ export declare const RENDER_SCOPE_IDENTIFIERS: Set<string>;
15
37
  /**
16
38
  * Identifiers emitted by the code generator itself (hoisted constants, internal
17
39
  * helpers) are prefixed with `_` or `$`. Treat them as bound so we never touch
@@ -1 +1 @@
1
- {"version":3,"file":"ambient-identifiers.d.ts","sourceRoot":"","sources":["../../../../src/resolvers/unbound-expressions/ambient-identifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,aA4E9B,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,eAAgB,MAAM,KAAG,OACT,CAAA"}
1
+ {"version":3,"file":"ambient-identifiers.d.ts","sourceRoot":"","sources":["../../../../src/resolvers/unbound-expressions/ambient-identifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,wBAAwB,aA6DnC,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,eAAgB,MAAM,KAAG,OACT,CAAA"}
@@ -1,27 +1,44 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isGeneratorInternalIdentifier = exports.AMBIENT_IDENTIFIERS = void 0;
3
+ exports.isGeneratorInternalIdentifier = exports.RENDER_SCOPE_IDENTIFIERS = void 0;
4
4
  /**
5
- * Root identifiers that are always in scope inside a generated component and
6
- * therefore can never be an "unbound" reference:
5
+ * Root identifiers that are always in scope where a UIDL `expr` node is
6
+ * rendered — i.e. in the component's RENDER BODY, which is the only place this
7
+ * resolver inspects (element children, element attributes and the link ability
8
+ * the abilities resolver has not yet folded into attributes).
7
9
  *
8
- * - values the code generator injects into the render scope (`props`, `state`,
9
- * `event`, `params`, `context`, `metadata`, and common map callback vars),
10
- * - standard JavaScript globals / built-ins.
10
+ * Membership here is a promise that the code generator declares the name. It is
11
+ * therefore limited to:
11
12
  *
12
- * Iterator variables introduced by repeaters (`item`, `kpi`, ...) are NOT
13
- * listed here — they are tracked positionally as the walker descends into a
14
- * repeater's subtree, so an iterator reference that escapes its repeater is
15
- * still correctly flagged as unbound.
13
+ * - `props`, the render function's own parameter,
14
+ * - the global-context values the JSX emitter destructures from
15
+ * `useGlobalContext()` whenever it sees them in an expression
16
+ * (`GLOBAL_EXPRESSION_IDENTIFIERS` in teleport-plugin-common node-to-jsx),
17
+ * - standard JavaScript / browser globals.
18
+ *
19
+ * Deliberately NOT here:
20
+ *
21
+ * - `state` — React state is destructured into bare variables
22
+ * (`dynamicReferencePrefixMap.state` is `''`), so there is no `state` object
23
+ * at runtime and `state` / `state.x` in an expression is always a
24
+ * `ReferenceError`. Individual state names come from `stateDefinitions`.
25
+ * - `params`, `index` and render-prop names — these ARE bound, but only inside
26
+ * the subtree of the node that introduces them (a repeater's `.map()`
27
+ * callback, a CMS/data-source `renderSuccess` prop). The walker tracks them
28
+ * positionally so a reference that escapes its owner is still caught.
29
+ * - `event` / `evt` / `e` — bound only inside event handlers, which this
30
+ * resolver does not visit. When one appears in a render-scope expression it
31
+ * is either an inline callback parameter (the scope analyser sees the
32
+ * binding) or genuinely undeclared.
33
+ * - `context`, `ctx`, `metadata`, `record`, `idx`, `key`, `i` — no generator
34
+ * ever declares these names in a render body.
35
+ *
36
+ * Reserved words and literals (`true`, `null`, `undefined`, …) are filtered out
37
+ * by the lexer itself and need no entry here.
16
38
  */
17
- exports.AMBIENT_IDENTIFIERS = new Set([
18
- // Code-generator provided render-scope values
39
+ exports.RENDER_SCOPE_IDENTIFIERS = new Set([
40
+ // The render function's own parameter
19
41
  'props',
20
- 'state',
21
- 'context',
22
- 'ctx',
23
- 'metadata',
24
- 'record',
25
42
  // Global-context values: the JSX emitter scans expressions for these
26
43
  // (GLOBAL_EXPRESSION_IDENTIFIERS in teleport-plugin-common node-to-jsx) and
27
44
  // injects the matching `useGlobalContext()` destructuring, so they are
@@ -33,15 +50,6 @@ exports.AMBIENT_IDENTIFIERS = new Set([
33
50
  'cart',
34
51
  'locale',
35
52
  'locales',
36
- // Event handler / map callback parameters
37
- 'event',
38
- 'evt',
39
- 'e',
40
- 'params',
41
- 'index',
42
- 'idx',
43
- 'key',
44
- 'i',
45
53
  // Browser globals
46
54
  'window',
47
55
  'document',
@@ -57,7 +65,6 @@ exports.AMBIENT_IDENTIFIERS = new Set([
57
65
  'URLSearchParams',
58
66
  // Language built-ins
59
67
  'globalThis',
60
- 'undefined',
61
68
  'NaN',
62
69
  'Infinity',
63
70
  'JSON',
@@ -1 +1 @@
1
- {"version":3,"file":"ambient-identifiers.js","sourceRoot":"","sources":["../../../../src/resolvers/unbound-expressions/ambient-identifiers.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;GAYG;AACU,QAAA,mBAAmB,GAAG,IAAI,GAAG,CAAS;IACjD,8CAA8C;IAC9C,OAAO;IACP,OAAO;IACP,SAAS;IACT,KAAK;IACL,UAAU;IACV,QAAQ;IACR,qEAAqE;IACrE,4EAA4E;IAC5E,uEAAuE;IACvE,2EAA2E;IAC3E,gEAAgE;IAChE,aAAa;IACb,gBAAgB;IAChB,WAAW;IACX,MAAM;IACN,QAAQ;IACR,SAAS;IACT,0CAA0C;IAC1C,OAAO;IACP,KAAK;IACL,GAAG;IACH,QAAQ;IACR,OAAO;IACP,KAAK;IACL,KAAK;IACL,GAAG;IACH,kBAAkB;IAClB,QAAQ;IACR,UAAU;IACV,WAAW;IACX,UAAU;IACV,SAAS;IACT,QAAQ;IACR,cAAc;IACd,gBAAgB;IAChB,SAAS;IACT,OAAO;IACP,KAAK;IACL,iBAAiB;IACjB,qBAAqB;IACrB,YAAY;IACZ,WAAW;IACX,KAAK;IACL,UAAU;IACV,MAAM;IACN,MAAM;IACN,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,MAAM;IACN,QAAQ;IACR,KAAK;IACL,KAAK;IACL,SAAS;IACT,SAAS;IACT,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,WAAW;IACX,UAAU;IACV,OAAO;IACP,SAAS;IACT,MAAM;IACN,UAAU;IACV,YAAY;IACZ,OAAO;IACP,UAAU;IACV,oBAAoB;IACpB,oBAAoB;IACpB,WAAW;IACX,WAAW;CACZ,CAAC,CAAA;AAEF;;;;GAIG;AACI,IAAM,6BAA6B,GAAG,UAAC,UAAkB;IAC9D,OAAA,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC;AAAxD,CAAwD,CAAA;AAD7C,QAAA,6BAA6B,iCACgB"}
1
+ {"version":3,"file":"ambient-identifiers.js","sourceRoot":"","sources":["../../../../src/resolvers/unbound-expressions/ambient-identifiers.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACU,QAAA,wBAAwB,GAAG,IAAI,GAAG,CAAS;IACtD,sCAAsC;IACtC,OAAO;IACP,qEAAqE;IACrE,4EAA4E;IAC5E,uEAAuE;IACvE,2EAA2E;IAC3E,gEAAgE;IAChE,aAAa;IACb,gBAAgB;IAChB,WAAW;IACX,MAAM;IACN,QAAQ;IACR,SAAS;IACT,kBAAkB;IAClB,QAAQ;IACR,UAAU;IACV,WAAW;IACX,UAAU;IACV,SAAS;IACT,QAAQ;IACR,cAAc;IACd,gBAAgB;IAChB,SAAS;IACT,OAAO;IACP,KAAK;IACL,iBAAiB;IACjB,qBAAqB;IACrB,YAAY;IACZ,KAAK;IACL,UAAU;IACV,MAAM;IACN,MAAM;IACN,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,MAAM;IACN,QAAQ;IACR,KAAK;IACL,KAAK;IACL,SAAS;IACT,SAAS;IACT,SAAS;IACT,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,WAAW;IACX,UAAU;IACV,OAAO;IACP,SAAS;IACT,MAAM;IACN,UAAU;IACV,YAAY;IACZ,OAAO;IACP,UAAU;IACV,oBAAoB;IACpB,oBAAoB;IACpB,WAAW;IACX,WAAW;CACZ,CAAC,CAAA;AAEF;;;;GAIG;AACI,IAAM,6BAA6B,GAAG,UAAC,UAAkB;IAC9D,OAAA,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC;AAAxD,CAAwD,CAAA;AAD7C,QAAA,6BAA6B,iCACgB"}