@teleporthq/teleport-uidl-resolver 0.43.22 → 0.43.29
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__/style-set-definitions/index.ts +81 -0
- package/__tests__/unbound-expressions/expression-identifiers.ts +61 -0
- package/__tests__/unbound-expressions/index.ts +149 -0
- package/dist/cjs/resolver.d.ts.map +1 -1
- package/dist/cjs/resolver.js +6 -0
- package/dist/cjs/resolver.js.map +1 -1
- package/dist/cjs/resolvers/style-set-definitions/index.d.ts.map +1 -1
- package/dist/cjs/resolvers/style-set-definitions/index.js +14 -5
- package/dist/cjs/resolvers/style-set-definitions/index.js.map +1 -1
- package/dist/cjs/resolvers/unbound-expressions/ambient-identifiers.d.ts +21 -0
- package/dist/cjs/resolvers/unbound-expressions/ambient-identifiers.d.ts.map +1 -0
- package/dist/cjs/resolvers/unbound-expressions/ambient-identifiers.js +92 -0
- package/dist/cjs/resolvers/unbound-expressions/ambient-identifiers.js.map +1 -0
- package/dist/cjs/resolvers/unbound-expressions/expression-identifiers.d.ts +23 -0
- package/dist/cjs/resolvers/unbound-expressions/expression-identifiers.d.ts.map +1 -0
- package/dist/cjs/resolvers/unbound-expressions/expression-identifiers.js +205 -0
- package/dist/cjs/resolvers/unbound-expressions/expression-identifiers.js.map +1 -0
- package/dist/cjs/resolvers/unbound-expressions/index.d.ts +3 -0
- package/dist/cjs/resolvers/unbound-expressions/index.d.ts.map +1 -0
- package/dist/cjs/resolvers/unbound-expressions/index.js +198 -0
- package/dist/cjs/resolvers/unbound-expressions/index.js.map +1 -0
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/resolver.d.ts.map +1 -1
- package/dist/esm/resolver.js +6 -0
- package/dist/esm/resolver.js.map +1 -1
- package/dist/esm/resolvers/style-set-definitions/index.d.ts.map +1 -1
- package/dist/esm/resolvers/style-set-definitions/index.js +14 -5
- package/dist/esm/resolvers/style-set-definitions/index.js.map +1 -1
- package/dist/esm/resolvers/unbound-expressions/ambient-identifiers.d.ts +21 -0
- package/dist/esm/resolvers/unbound-expressions/ambient-identifiers.d.ts.map +1 -0
- package/dist/esm/resolvers/unbound-expressions/ambient-identifiers.js +88 -0
- package/dist/esm/resolvers/unbound-expressions/ambient-identifiers.js.map +1 -0
- package/dist/esm/resolvers/unbound-expressions/expression-identifiers.d.ts +23 -0
- package/dist/esm/resolvers/unbound-expressions/expression-identifiers.d.ts.map +1 -0
- package/dist/esm/resolvers/unbound-expressions/expression-identifiers.js +201 -0
- package/dist/esm/resolvers/unbound-expressions/expression-identifiers.js.map +1 -0
- package/dist/esm/resolvers/unbound-expressions/index.d.ts +3 -0
- package/dist/esm/resolvers/unbound-expressions/index.d.ts.map +1 -0
- package/dist/esm/resolvers/unbound-expressions/index.js +194 -0
- package/dist/esm/resolvers/unbound-expressions/index.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/resolver.ts +7 -0
- package/src/resolvers/style-set-definitions/index.ts +14 -5
- package/src/resolvers/unbound-expressions/ambient-identifiers.ts +87 -0
- package/src/resolvers/unbound-expressions/expression-identifiers.ts +211 -0
- package/src/resolvers/unbound-expressions/index.ts +209 -0
|
@@ -75,6 +75,87 @@ describe('Resolves style-sheet', () => {
|
|
|
75
75
|
)
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
+
it('Keeps valid CSS selector keys (incl. attribute selectors) and drops JS-fragment keys', () => {
|
|
79
|
+
const styleSet: Record<string, UIDLStyleSetDefinition> = {
|
|
80
|
+
'input-group': {
|
|
81
|
+
type: 'reusable-project-style-map' as const,
|
|
82
|
+
content: { display: staticNode('block') },
|
|
83
|
+
className: 'input-group',
|
|
84
|
+
},
|
|
85
|
+
'input-group label': {
|
|
86
|
+
type: 'reusable-project-style-map' as const,
|
|
87
|
+
content: { display: staticNode('block') },
|
|
88
|
+
className: 'input-group',
|
|
89
|
+
subselectors: ' label',
|
|
90
|
+
},
|
|
91
|
+
'input-group input[type="number"]': {
|
|
92
|
+
type: 'reusable-project-style-map' as const,
|
|
93
|
+
content: { width: staticNode('100%') },
|
|
94
|
+
className: 'input-group',
|
|
95
|
+
subselectors: ' input[type="number"]',
|
|
96
|
+
},
|
|
97
|
+
'input-group input[type="range"]': {
|
|
98
|
+
type: 'reusable-project-style-map' as const,
|
|
99
|
+
content: { width: staticNode('100%') },
|
|
100
|
+
className: 'input-group',
|
|
101
|
+
subselectors: ' input[type="range"]',
|
|
102
|
+
},
|
|
103
|
+
// Combinators, pseudo-functions, universal and id selectors are valid CSS
|
|
104
|
+
'card > span': {
|
|
105
|
+
type: 'reusable-project-style-map' as const,
|
|
106
|
+
content: { display: staticNode('block') },
|
|
107
|
+
className: 'card',
|
|
108
|
+
subselectors: ' > span',
|
|
109
|
+
},
|
|
110
|
+
'card + .card': {
|
|
111
|
+
type: 'reusable-project-style-map' as const,
|
|
112
|
+
content: { display: staticNode('block') },
|
|
113
|
+
className: 'card',
|
|
114
|
+
subselectors: ' + .card',
|
|
115
|
+
},
|
|
116
|
+
'list li:nth-child(2n+1)': {
|
|
117
|
+
type: 'reusable-project-style-map' as const,
|
|
118
|
+
content: { display: staticNode('block') },
|
|
119
|
+
className: 'list',
|
|
120
|
+
subselectors: ' li:nth-child(2n+1)',
|
|
121
|
+
},
|
|
122
|
+
'card:not(.active)': {
|
|
123
|
+
type: 'reusable-project-style-map' as const,
|
|
124
|
+
content: { display: staticNode('block') },
|
|
125
|
+
className: 'card',
|
|
126
|
+
subselectors: ':not(.active)',
|
|
127
|
+
},
|
|
128
|
+
'#main .card': {
|
|
129
|
+
type: 'reusable-project-style-map' as const,
|
|
130
|
+
content: { display: staticNode('block') },
|
|
131
|
+
className: 'main',
|
|
132
|
+
subselectors: ' .card',
|
|
133
|
+
},
|
|
134
|
+
// JS-fragment / invalid keys that must be dropped
|
|
135
|
+
"'coin'": { type: 'reusable-project-style-map' as const, content: {} },
|
|
136
|
+
'{{': { type: 'reusable-project-style-map' as const, content: {} },
|
|
137
|
+
'||': { type: 'reusable-project-style-map' as const, content: {} },
|
|
138
|
+
"t('coin')": { type: 'reusable-project-style-map' as const, content: {} },
|
|
139
|
+
['$' + '{expr}']: { type: 'reusable-project-style-map' as const, content: {} },
|
|
140
|
+
'a && b': { type: 'reusable-project-style-map' as const, content: {} },
|
|
141
|
+
'<div>': { type: 'reusable-project-style-map' as const, content: {} },
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const result = resolveStyleSetDefinitions(styleSet, {})
|
|
145
|
+
|
|
146
|
+
expect(Object.keys(result)).toEqual([
|
|
147
|
+
'input-group',
|
|
148
|
+
'input-group label',
|
|
149
|
+
'input-group input[type="number"]',
|
|
150
|
+
'input-group input[type="range"]',
|
|
151
|
+
'card > span',
|
|
152
|
+
'card + .card',
|
|
153
|
+
'list li:nth-child(2n+1)',
|
|
154
|
+
'card:not(.active)',
|
|
155
|
+
'#main .card',
|
|
156
|
+
])
|
|
157
|
+
})
|
|
158
|
+
|
|
78
159
|
it(`Resolver doesn't throw any error even if conditions are not passed`, () => {
|
|
79
160
|
const styleSet: Record<string, UIDLStyleSetDefinition> = {
|
|
80
161
|
primaryButton: {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { extractRootIdentifiers } from '../../src/resolvers/unbound-expressions/expression-identifiers'
|
|
2
|
+
|
|
3
|
+
const roots = (expression: string): string[] =>
|
|
4
|
+
Array.from(extractRootIdentifiers(expression)).sort()
|
|
5
|
+
|
|
6
|
+
describe('extractRootIdentifiers', () => {
|
|
7
|
+
it('returns the leading identifier of a member expression', () => {
|
|
8
|
+
expect(roots('cat.name')).toEqual(['cat'])
|
|
9
|
+
expect(roots('loc.location_id')).toEqual(['loc'])
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('ignores property names accessed via dot and optional chaining', () => {
|
|
13
|
+
expect(roots('item?.id')).toEqual(['item'])
|
|
14
|
+
expect(roots('props.inventoryItem?.name')).toEqual(['props'])
|
|
15
|
+
expect(roots('event.target.value')).toEqual(['event'])
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('does not treat words inside string literals as identifiers', () => {
|
|
19
|
+
expect(roots('item?.name || "Item"')).toEqual(['item'])
|
|
20
|
+
expect(roots("tx?.transaction_type || 'Adjustment'")).toEqual(['tx'])
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('handles quoted JSON blobs without leaking string keys/values', () => {
|
|
24
|
+
const expression =
|
|
25
|
+
'JSON.stringify([{"type":"condition","source":"id","destination":params[\'id\'],"operand":"="}])'
|
|
26
|
+
expect(roots(expression)).toEqual(['JSON', 'params'])
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('extracts identifiers from template-literal interpolations only', () => {
|
|
30
|
+
// tslint:disable no-invalid-template-strings
|
|
31
|
+
expect(roots('`/edit-inventory-item/${item?.id}`')).toEqual(['item'])
|
|
32
|
+
expect(roots('`/edit-inventory-item/[id]`')).toEqual([])
|
|
33
|
+
expect(roots('`${cat.name} - ${loc.city}`').sort()).toEqual(['cat', 'loc'])
|
|
34
|
+
// tslint:enable no-invalid-template-strings
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('collects call-argument references but not property method names', () => {
|
|
38
|
+
expect(roots('Math.max(a, b)').sort()).toEqual(['Math', 'a', 'b'])
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('does not treat unquoted object-literal keys as references', () => {
|
|
42
|
+
expect(roots('{ active: item.active }')).toEqual(['item'])
|
|
43
|
+
expect(roots('{ a: cat.name, b: loc.id }').sort()).toEqual(['cat', 'loc'])
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('keeps ternary branches and object shorthand values as references', () => {
|
|
47
|
+
expect(roots('flag ? yes : no').sort()).toEqual(['flag', 'no', 'yes'])
|
|
48
|
+
expect(roots('{ item, index }').sort()).toEqual(['index', 'item'])
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('ignores keywords and literals', () => {
|
|
52
|
+
expect(roots('typeof cat === "object"')).toEqual(['cat'])
|
|
53
|
+
expect(roots('true')).toEqual([])
|
|
54
|
+
expect(roots('null')).toEqual([])
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('returns an empty set for empty or non-string input', () => {
|
|
58
|
+
expect(roots('')).toEqual([])
|
|
59
|
+
expect(Array.from(extractRootIdentifiers(undefined as unknown as string))).toEqual([])
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { ComponentUIDL, UIDLElementNode, UIDLNode } from '@teleporthq/teleport-types'
|
|
2
|
+
import { resolveUnboundExpressions } from '../../src/resolvers/unbound-expressions'
|
|
3
|
+
|
|
4
|
+
const expr = (content: string): UIDLNode => ({ type: 'expr', content } as UIDLNode)
|
|
5
|
+
|
|
6
|
+
const optionWith = (child: UIDLNode): UIDLElementNode => ({
|
|
7
|
+
type: 'element',
|
|
8
|
+
content: {
|
|
9
|
+
elementType: 'option',
|
|
10
|
+
attrs: { value: { type: 'static', content: '' } },
|
|
11
|
+
children: [child],
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
const buildComponent = (node: UIDLElementNode): ComponentUIDL =>
|
|
16
|
+
({
|
|
17
|
+
name: 'AddInventoryItem',
|
|
18
|
+
node,
|
|
19
|
+
propDefinitions: {
|
|
20
|
+
inventoryItem: { type: 'object', defaultValue: {} },
|
|
21
|
+
},
|
|
22
|
+
stateDefinitions: {
|
|
23
|
+
categoryOptions: { type: 'array', defaultValue: [] },
|
|
24
|
+
},
|
|
25
|
+
} as unknown as ComponentUIDL)
|
|
26
|
+
|
|
27
|
+
describe('resolveUnboundExpressions', () => {
|
|
28
|
+
it('neutralises an orphaned option expression that references an unbound iterator', () => {
|
|
29
|
+
const option = optionWith(expr('cat.name'))
|
|
30
|
+
const uidl = buildComponent({
|
|
31
|
+
type: 'element',
|
|
32
|
+
content: { elementType: 'select', children: [option] },
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
resolveUnboundExpressions(uidl)
|
|
36
|
+
|
|
37
|
+
expect(option.content.children[0]).toEqual({ type: 'static', content: '' })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('keeps expressions that reference an in-scope repeater iterator', () => {
|
|
41
|
+
const boundExpr = expr('item?.id')
|
|
42
|
+
const repeater: UIDLNode = {
|
|
43
|
+
type: 'cms-list-repeater',
|
|
44
|
+
content: {
|
|
45
|
+
elementType: 'fragment',
|
|
46
|
+
name: 'list',
|
|
47
|
+
key: 'list',
|
|
48
|
+
renderPropIdentifier: 'item',
|
|
49
|
+
nodes: {
|
|
50
|
+
list: {
|
|
51
|
+
type: 'element',
|
|
52
|
+
content: { elementType: 'div', children: [boundExpr] },
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
} as UIDLNode
|
|
57
|
+
const uidl = buildComponent({
|
|
58
|
+
type: 'element',
|
|
59
|
+
content: { elementType: 'container', children: [repeater] },
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
resolveUnboundExpressions(uidl)
|
|
63
|
+
|
|
64
|
+
expect(boundExpr).toEqual({ type: 'expr', content: 'item?.id' })
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('flags a repeater iterator referenced outside of its subtree', () => {
|
|
68
|
+
const escaped = optionWith(expr('item?.id'))
|
|
69
|
+
const repeater: UIDLNode = {
|
|
70
|
+
type: 'cms-list-repeater',
|
|
71
|
+
content: {
|
|
72
|
+
elementType: 'fragment',
|
|
73
|
+
name: 'list',
|
|
74
|
+
key: 'list',
|
|
75
|
+
renderPropIdentifier: 'item',
|
|
76
|
+
nodes: {
|
|
77
|
+
list: { type: 'element', content: { elementType: 'div', children: [] } },
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
} as UIDLNode
|
|
81
|
+
const uidl = buildComponent({
|
|
82
|
+
type: 'element',
|
|
83
|
+
content: { elementType: 'container', children: [repeater, escaped] },
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
resolveUnboundExpressions(uidl)
|
|
87
|
+
|
|
88
|
+
expect(escaped.content.children[0]).toEqual({ type: 'static', content: '' })
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('keeps expressions that reference props, state, globals and event', () => {
|
|
92
|
+
const propsExpr = expr('props.inventoryItem?.name')
|
|
93
|
+
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
|
+
})
|
|
103
|
+
|
|
104
|
+
resolveUnboundExpressions(uidl)
|
|
105
|
+
|
|
106
|
+
expect(propsExpr).toEqual({ type: 'expr', content: 'props.inventoryItem?.name' })
|
|
107
|
+
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' })
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('neutralises an unbound expression used as an attribute value', () => {
|
|
113
|
+
const element: UIDLElementNode = {
|
|
114
|
+
type: 'element',
|
|
115
|
+
content: {
|
|
116
|
+
elementType: 'div',
|
|
117
|
+
attrs: {
|
|
118
|
+
'data-item-id': { type: 'expr', content: 'cat.id' },
|
|
119
|
+
title: { type: 'static', content: 'ok' },
|
|
120
|
+
},
|
|
121
|
+
children: [],
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
const uidl = buildComponent(element)
|
|
125
|
+
|
|
126
|
+
resolveUnboundExpressions(uidl)
|
|
127
|
+
|
|
128
|
+
expect(element.content.attrs!['data-item-id']).toEqual({ type: 'static', content: '' })
|
|
129
|
+
expect(element.content.attrs!.title).toEqual({ type: 'static', content: 'ok' })
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('preserves surrounding static text when neutralising mixed content', () => {
|
|
133
|
+
const span: UIDLElementNode = {
|
|
134
|
+
type: 'element',
|
|
135
|
+
content: {
|
|
136
|
+
elementType: 'span',
|
|
137
|
+
children: [{ type: 'static', content: 'Hello ' }, expr('cat.name')],
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
const uidl = buildComponent(span)
|
|
141
|
+
|
|
142
|
+
resolveUnboundExpressions(uidl)
|
|
143
|
+
|
|
144
|
+
expect(span.content.children).toEqual([
|
|
145
|
+
{ type: 'static', content: 'Hello ' },
|
|
146
|
+
{ type: 'static', content: '' },
|
|
147
|
+
])
|
|
148
|
+
})
|
|
149
|
+
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,aAAa,EACb,WAAW,EACX,OAAO,EACP,gBAAgB,EAEjB,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,aAAa,EACb,WAAW,EACX,OAAO,EACP,gBAAgB,EAEjB,MAAM,4BAA4B,CAAA;AAOnC;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,QAAQ;IAC3B,OAAO,CAAC,OAAO,CAMd;gBAEW,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,EAAE;IAQlC,UAAU,CAAC,OAAO,EAAE,OAAO;IAI3B,WAAW,CAAC,KAAK,EAAE,aAAa,EAAE,OAAO,GAAE,gBAA6C;IAyCxF,cAAc,CACnB,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,gBAA6C;CAYzD"}
|
package/dist/cjs/resolver.js
CHANGED
|
@@ -40,6 +40,7 @@ var abilities_1 = require("./resolvers/abilities");
|
|
|
40
40
|
var style_set_definitions_1 = require("./resolvers/style-set-definitions");
|
|
41
41
|
var referenced_styles_1 = require("./resolvers/referenced-styles");
|
|
42
42
|
var embed_node_1 = require("./resolvers/embed-node");
|
|
43
|
+
var unbound_expressions_1 = require("./resolvers/unbound-expressions");
|
|
43
44
|
/**
|
|
44
45
|
* The resolver takes the input UIDL and converts all the abstract node types into
|
|
45
46
|
* concrete node types, based on the mappings you provide
|
|
@@ -80,6 +81,11 @@ var Resolver = /** @class */ (function () {
|
|
|
80
81
|
(0, embed_node_1.resolveHtmlNode)(uidl, newOptions);
|
|
81
82
|
utils.resolveNode(uidl.node, newOptions);
|
|
82
83
|
utils.resolveNodeInPropDefinitions(uidl, newOptions);
|
|
84
|
+
// Guard against `expr` nodes that reference an out-of-scope identifier
|
|
85
|
+
// (e.g. an orphaned `<option>{{ cat.name }}</option>` whose repeater was
|
|
86
|
+
// never generated). Left untouched they compile to `ReferenceError`s that
|
|
87
|
+
// fail the static export / `next build`.
|
|
88
|
+
(0, unbound_expressions_1.resolveUnboundExpressions)(uidl);
|
|
83
89
|
utils.createNodesLookup(uidl, lookup);
|
|
84
90
|
utils.generateUniqueKeys(uidl, lookup);
|
|
85
91
|
utils.ensureDataSourceUniqueness(uidl.node);
|
package/dist/cjs/resolver.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/resolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,6CAAgC;AAChC,+DAAuD;AAQvD,mDAAwD;AACxD,2EAA8E;AAC9E,mEAAsE;AACtE,qDAAwD;
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/resolvers/style-set-definitions/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EAGvB,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/resolvers/style-set-definitions/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EAGvB,MAAM,4BAA4B,CAAA;AA+BnC,eAAO,MAAM,0BAA0B,cAC1B,OAAO,MAAM,EAAE,sBAAsB,CAAC,WACxC,gBAAgB,KACxB,OAAO,MAAM,EAAE,sBAAsB,CA8CvC,CAAA"}
|
|
@@ -32,11 +32,20 @@ var isValidStyleSetKey = function (key) {
|
|
|
32
32
|
if (sanitized === null || sanitized.length === 0) {
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
|
-
// Allow
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
|
|
35
|
+
// Allow real CSS selector syntax used in style-set keys:
|
|
36
|
+
// - compound selectors: "base-class.modifier", "base-class.modifier .child-class"
|
|
37
|
+
// - combinators: "card > span", "card + span", "card ~ span", "list *"
|
|
38
|
+
// - pseudo-selectors / functions: "class:hover", "row:nth-child(2n+1)", "card:not(.active)"
|
|
39
|
+
// - id selectors: "#main .card"
|
|
40
|
+
// - attribute selectors: 'input-group input[type="number"]'
|
|
41
|
+
// Attribute-selector brackets can legitimately contain quotes and "=", so they are stripped
|
|
42
|
+
// before the JS-fragment check below. The remaining blacklist still rejects expression/template
|
|
43
|
+
// fragments such as "'coin'", "{{", "||", "${x}" or "a && b".
|
|
44
|
+
var keyWithoutAttributeSelectors = key.replace(/\[[^\]]*\]/g, '');
|
|
45
|
+
// Reject characters that indicate JS expression fragments or are truly invalid CSS selectors.
|
|
46
|
+
// Note: ( ) * + > # are intentionally NOT rejected so pseudo-functions, combinators, the
|
|
47
|
+
// universal selector and id selectors keep working.
|
|
48
|
+
if (/['"{}|!@$%^&=<?/\\]/.test(keyWithoutAttributeSelectors)) {
|
|
40
49
|
return false;
|
|
41
50
|
}
|
|
42
51
|
// Reject keys that start with a digit
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/resolvers/style-set-definitions/index.ts"],"names":[],"mappings":";AAAA;;;EAGE;;;;;;;;;;;;;;;;;;;;;;;AAEF,+DAAyD;AAOzD,qCAA6C;AAE7C,IAAM,kBAAkB,GAAG,UAAC,GAAW;IACrC,IAAM,SAAS,GAAG,6BAAW,CAAC,uBAAuB,CAAC,GAAG,CAAC,CAAA;IAC1D,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE;QAChD,OAAO,KAAK,CAAA;KACb;IACD,gGAAgG;IAChG
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/resolvers/style-set-definitions/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root identifiers that are always in scope inside a generated component and
|
|
3
|
+
* therefore can never be an "unbound" reference:
|
|
4
|
+
*
|
|
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.
|
|
8
|
+
*
|
|
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.
|
|
13
|
+
*/
|
|
14
|
+
export declare const AMBIENT_IDENTIFIERS: Set<string>;
|
|
15
|
+
/**
|
|
16
|
+
* Identifiers emitted by the code generator itself (hoisted constants, internal
|
|
17
|
+
* helpers) are prefixed with `_` or `$`. Treat them as bound so we never touch
|
|
18
|
+
* generator-authored expressions.
|
|
19
|
+
*/
|
|
20
|
+
export declare const isGeneratorInternalIdentifier: (identifier: string) => boolean;
|
|
21
|
+
//# sourceMappingURL=ambient-identifiers.d.ts.map
|
|
@@ -0,0 +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,aAiE9B,CAAA;AAEF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,eAAgB,MAAM,KAAG,OACT,CAAA"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isGeneratorInternalIdentifier = exports.AMBIENT_IDENTIFIERS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Root identifiers that are always in scope inside a generated component and
|
|
6
|
+
* therefore can never be an "unbound" reference:
|
|
7
|
+
*
|
|
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.
|
|
11
|
+
*
|
|
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.
|
|
16
|
+
*/
|
|
17
|
+
exports.AMBIENT_IDENTIFIERS = new Set([
|
|
18
|
+
// Code-generator provided render-scope values
|
|
19
|
+
'props',
|
|
20
|
+
'state',
|
|
21
|
+
'context',
|
|
22
|
+
'ctx',
|
|
23
|
+
'metadata',
|
|
24
|
+
'record',
|
|
25
|
+
// Event handler / map callback parameters
|
|
26
|
+
'event',
|
|
27
|
+
'evt',
|
|
28
|
+
'e',
|
|
29
|
+
'params',
|
|
30
|
+
'index',
|
|
31
|
+
'idx',
|
|
32
|
+
'key',
|
|
33
|
+
'i',
|
|
34
|
+
// Browser globals
|
|
35
|
+
'window',
|
|
36
|
+
'document',
|
|
37
|
+
'navigator',
|
|
38
|
+
'location',
|
|
39
|
+
'history',
|
|
40
|
+
'screen',
|
|
41
|
+
'localStorage',
|
|
42
|
+
'sessionStorage',
|
|
43
|
+
'console',
|
|
44
|
+
'fetch',
|
|
45
|
+
'URL',
|
|
46
|
+
'URLSearchParams',
|
|
47
|
+
// Language built-ins
|
|
48
|
+
'globalThis',
|
|
49
|
+
'undefined',
|
|
50
|
+
'NaN',
|
|
51
|
+
'Infinity',
|
|
52
|
+
'JSON',
|
|
53
|
+
'Math',
|
|
54
|
+
'Object',
|
|
55
|
+
'Array',
|
|
56
|
+
'String',
|
|
57
|
+
'Number',
|
|
58
|
+
'Boolean',
|
|
59
|
+
'Date',
|
|
60
|
+
'RegExp',
|
|
61
|
+
'Map',
|
|
62
|
+
'Set',
|
|
63
|
+
'WeakMap',
|
|
64
|
+
'WeakSet',
|
|
65
|
+
'Promise',
|
|
66
|
+
'Symbol',
|
|
67
|
+
'BigInt',
|
|
68
|
+
'Error',
|
|
69
|
+
'TypeError',
|
|
70
|
+
'Function',
|
|
71
|
+
'Proxy',
|
|
72
|
+
'Reflect',
|
|
73
|
+
'Intl',
|
|
74
|
+
'parseInt',
|
|
75
|
+
'parseFloat',
|
|
76
|
+
'isNaN',
|
|
77
|
+
'isFinite',
|
|
78
|
+
'encodeURIComponent',
|
|
79
|
+
'decodeURIComponent',
|
|
80
|
+
'encodeURI',
|
|
81
|
+
'decodeURI',
|
|
82
|
+
]);
|
|
83
|
+
/**
|
|
84
|
+
* Identifiers emitted by the code generator itself (hoisted constants, internal
|
|
85
|
+
* helpers) are prefixed with `_` or `$`. Treat them as bound so we never touch
|
|
86
|
+
* generator-authored expressions.
|
|
87
|
+
*/
|
|
88
|
+
var isGeneratorInternalIdentifier = function (identifier) {
|
|
89
|
+
return identifier.startsWith('_') || identifier.startsWith('$');
|
|
90
|
+
};
|
|
91
|
+
exports.isGeneratorInternalIdentifier = isGeneratorInternalIdentifier;
|
|
92
|
+
//# sourceMappingURL=ambient-identifiers.js.map
|
|
@@ -0,0 +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,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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts the "root" reference identifiers used by a UIDL `expr` node so the
|
|
3
|
+
* resolver can tell whether an expression references a variable that is not in
|
|
4
|
+
* scope (and would therefore throw a `ReferenceError` at render time).
|
|
5
|
+
*
|
|
6
|
+
* A root reference is an identifier that is READ as a variable — e.g. `cat` in
|
|
7
|
+
* `cat.name`, `item` in `` `/edit/${item?.id}` ``, or `params` in
|
|
8
|
+
* `params['id']`. Property names (the `name` in `cat.name`), string-literal
|
|
9
|
+
* contents, and language keywords are NOT roots.
|
|
10
|
+
*
|
|
11
|
+
* The extractor is a small hand-written lexer rather than a full parser so the
|
|
12
|
+
* resolver package stays dependency-free. It only needs to be correct enough to
|
|
13
|
+
* find the leading identifier of every member/call/index expression while
|
|
14
|
+
* ignoring string and template-literal text — which is exactly what
|
|
15
|
+
* distinguishes a genuine unbound reference from an incidental word inside a
|
|
16
|
+
* quoted JSON blob such as
|
|
17
|
+
* `JSON.stringify([{ "type": "condition", "source": "id" }])`.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Returns the set of root reference identifiers read by an expression string.
|
|
21
|
+
*/
|
|
22
|
+
export declare const extractRootIdentifiers: (expression: string) => Set<string>;
|
|
23
|
+
//# sourceMappingURL=expression-identifiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expression-identifiers.d.ts","sourceRoot":"","sources":["../../../../src/resolvers/unbound-expressions/expression-identifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAwLH;;GAEG;AACH,eAAO,MAAM,sBAAsB,eAAgB,MAAM,KAAG,IAAI,MAAM,CAMrE,CAAA"}
|