-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathlegacy.js
More file actions
197 lines (180 loc) · 7.37 KB
/
Copy pathlegacy.js
File metadata and controls
197 lines (180 loc) · 7.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
'use strict'
import { buildString } from './compiler.js'
import { splitPathMemoized } from './utilities/splitPath.js'
import chainingSupported from './utilities/chainingSupported.js'
import { Sync, OriginalImpl, Compiled } from './constants.js'
/** @type {Record<'get' | 'missing' | 'missing_some' | 'var', { method: (...args) => any }>} **/
const legacyMethods = {
get: {
[Sync]: true,
method: ([data, key, defaultValue], context, above, engine) => {
const notFound = defaultValue === undefined ? null : defaultValue
const subProps = splitPathMemoized(String(key))
for (let i = 0; i < subProps.length; i++) {
if (data === null || data === undefined) return notFound
// Descending into context
data = data[subProps[i]]
if (data === undefined) return notFound
}
if (engine.allowFunctions || typeof data[key] !== 'function') return data
return null
},
deterministic: true,
compile: (data, buildState) => {
let defaultValue = null
let key = data
let obj = null
if (Array.isArray(data) && data.length <= 3) {
obj = data[0]
key = data[1]
defaultValue = typeof data[2] === 'undefined' ? null : data[2]
// Bail out if the key is dynamic; dynamic keys are not really optimized by this block.
if (key && typeof key === 'object') return false
key = key.toString()
const pieces = splitPathMemoized(key)
if (!chainingSupported()) {
return `(((a,b) => (typeof a === 'undefined' || a === null) ? b : a)(${pieces.reduce(
(text, i) => `(${text}||0)[${JSON.stringify(i)}]`,
`(${buildString(obj, buildState)}||0)`
)}, ${buildString(defaultValue, buildState)}))`
}
return `((${buildString(obj, buildState)})${pieces
.map((i) => `?.[${buildString(i, buildState)}]`)
.join('')} ?? ${buildString(defaultValue, buildState)})`
}
return false
}
},
var: {
[OriginalImpl]: true,
[Sync]: true,
method: (key, context, above, engine) => {
let b
if (Array.isArray(key)) {
b = key[1]
key = key[0]
}
let iter = 0
while (typeof key === 'string' && key.startsWith('../') && iter < above.length) {
context = above[iter++]
key = key.substring(3)
// A performance optimization that allows you to pass the previous above array without spreading it as the last argument
if (iter === above.length && Array.isArray(context)) {
iter = 0
above = context
context = above[iter++]
}
}
const notFound = b === undefined ? null : b
if (typeof key === 'undefined' || key === '' || key === null) {
if (engine.allowFunctions || typeof context !== 'function') return context
return null
}
const subProps = splitPathMemoized(String(key))
for (let i = 0; i < subProps.length; i++) {
if (context === null || context === undefined) return notFound
// Descending into context
context = context[subProps[i]]
if (context === undefined) return notFound
}
if (engine.allowFunctions || typeof context !== 'function') return context
return null
},
deterministic: (data, buildState) => buildState.insideIterator && !String(data).includes('../../'),
optimizeUnary: true,
compile: (data, buildState) => {
let key = data
let defaultValue = null
if (
!key ||
typeof data === 'string' ||
typeof data === 'number' ||
(Array.isArray(data) && data.length <= 2)
) {
if (Array.isArray(data)) {
key = data[0]
defaultValue = typeof data[1] === 'undefined' ? null : data[1]
}
if (key === '../index' && buildState.iteratorCompile) return 'index'
// this counts the number of var accesses to determine if they're all just using this override.
// this allows for a small optimization :)
if (typeof key === 'undefined' || key === null || key === '') return 'context'
if (typeof key !== 'string' && typeof key !== 'number') return false
key = key.toString()
if (key.includes('../')) return false
const pieces = splitPathMemoized(key)
// support older versions of node
if (!chainingSupported()) {
const res = `((((a,b) => (typeof a === 'undefined' || a === null) ? b : a)(${pieces.reduce(
(text, i) => `(${text}||0)[${JSON.stringify(i)}]`,
'(context||0)'
)}, ${buildString(defaultValue, buildState)})))`
if (buildState.engine.allowFunctions) return res
return `(typeof (prev = ${res}) === 'function' ? null : prev)`
}
const res = `(context${pieces
.map((i) => `?.[${JSON.stringify(i)}]`)
.join('')} ?? ${buildString(defaultValue, buildState)})`
if (buildState.engine.allowFunctions) return res
return `(typeof (prev = ${res}) === 'function' ? null : prev)`
}
return false
}
},
missing: {
[Sync]: true,
optimizeUnary: false,
method: (checked, context) => {
if (!checked.length) return []
// Check every item in checked
const missing = []
for (let i = 0; i < checked.length; i++) {
// check context for the key, exiting early if any is null
const path = splitPathMemoized(String(checked[i]))
let data = context
let found = true
for (let j = 0; j < path.length; j++) {
if (!data) {
found = false
break
}
data = data[path[j]]
if (data === undefined) {
found = false
break
}
}
if (!found) missing.push(checked[i])
}
return missing
},
compile: (data, buildState) => {
if (!Array.isArray(data)) return false
if (data.length === 0) return buildState.compile`[]`
if (data.length === 1 && typeof data[0] === 'string' && !data[0].includes('.')) return buildState.compile`(context || 0)[${data[0]}] === undefined ? [${data[0]}] : []`
if (data.length === 2 && typeof data[0] === 'string' && typeof data[1] === 'string' && !data[0].includes('.') && !data[1].includes('.')) return buildState.compile`(context || 0)[${data[0]}] === undefined ? (context || 0)[${data[1]}] === undefined ? [${data[0]}, ${data[1]}] : [${data[0]}] : (context || 0)[${data[1]}] === undefined ? [${data[1]}] : []`
return false
},
deterministic: (data, buildState) => {
if (Array.isArray(data) && data.length === 0) return true
return false
}
},
missing_some: {
[Sync]: true,
optimizeUnary: false,
method: ([needCount, options], context) => {
const missing = legacyMethods.missing.method(options, context)
if (options.length - missing.length >= needCount) return []
return missing
},
compile: ([needCount, options], buildState) => {
if (!Array.isArray(options)) return false
let compilation = legacyMethods.missing.compile(options, buildState)
if (!compilation) compilation = buildState.compile`engine.methods.missing.method(${{ [Compiled]: JSON.stringify(options) }}, context)`
return buildState.compile`${options.length} - (prev = ${compilation}).length < ${needCount} ? prev : []`
},
deterministic: false
}
}
export default { ...legacyMethods }