@json-layout/core 0.21.0 → 0.22.0
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/package.json +1 -1
- package/src/compile/index.js +17 -9
- package/src/compile/serialize.js +2 -1
- package/src/compile/skeleton-node.js +88 -30
- package/src/compile/skeleton-tree.js +22 -2
- package/src/compile/types.ts +3 -2
- package/src/compile/utils/resolve-refs.js +99 -38
- package/src/state/index.js +11 -7
- package/src/state/state-node.js +12 -9
- package/src/state/types.ts +2 -2
- package/types/compile/index.d.ts +0 -16
- package/types/compile/index.d.ts.map +0 -1
- package/types/compile/serialize.d.ts +0 -6
- package/types/compile/serialize.d.ts.map +0 -1
- package/types/compile/skeleton-node.d.ts +0 -18
- package/types/compile/skeleton-node.d.ts.map +0 -1
- package/types/compile/skeleton-tree.d.ts +0 -13
- package/types/compile/skeleton-tree.d.ts.map +0 -1
- package/types/compile/types.d.ts +0 -57
- package/types/compile/types.d.ts.map +0 -1
- package/types/compile/utils/resolve-refs.d.ts +0 -9
- package/types/compile/utils/resolve-refs.d.ts.map +0 -1
- package/types/i18n/en.d.ts +0 -3
- package/types/i18n/en.d.ts.map +0 -1
- package/types/i18n/fr.d.ts +0 -3
- package/types/i18n/fr.d.ts.map +0 -1
- package/types/i18n/index.d.ts +0 -3
- package/types/i18n/index.d.ts.map +0 -1
- package/types/i18n/types.d.ts +0 -36
- package/types/i18n/types.d.ts.map +0 -1
- package/types/index.d.ts +0 -5
- package/types/index.d.ts.map +0 -1
- package/types/state/index.d.ts +0 -285
- package/types/state/index.d.ts.map +0 -1
- package/types/state/state-node.d.ts +0 -36
- package/types/state/state-node.d.ts.map +0 -1
- package/types/state/state-tree.d.ts +0 -13
- package/types/state/state-tree.d.ts.map +0 -1
- package/types/state/types.d.ts +0 -175
- package/types/state/types.d.ts.map +0 -1
- package/types/state/utils/display.d.ts +0 -51
- package/types/state/utils/display.d.ts.map +0 -1
- package/types/state/utils/immutable.d.ts +0 -21
- package/types/state/utils/immutable.d.ts.map +0 -1
- package/types/utils/build.d.ts +0 -5
- package/types/utils/build.d.ts.map +0 -1
- package/types/utils/clone.d.ts +0 -3
- package/types/utils/clone.d.ts.map +0 -1
- package/types/utils/doc-options.d.ts +0 -14
- package/types/utils/doc-options.d.ts.map +0 -1
package/package.json
CHANGED
package/src/compile/index.js
CHANGED
|
@@ -9,11 +9,11 @@ import MarkdownIt from 'markdown-it'
|
|
|
9
9
|
import { produce } from 'immer'
|
|
10
10
|
import i18n from '../i18n/index.js'
|
|
11
11
|
import { makeSkeletonTree } from './skeleton-tree.js'
|
|
12
|
-
import {
|
|
12
|
+
import { resolveLocaleRefs } from './utils/resolve-refs.js'
|
|
13
13
|
import clone from '../utils/clone.js'
|
|
14
14
|
import { standardComponents } from '@json-layout/vocabulary'
|
|
15
15
|
|
|
16
|
-
export {
|
|
16
|
+
export { resolveLocaleRefs } from './utils/resolve-refs.js'
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* @typedef {import('./types.js').SkeletonTree} SkeletonTree
|
|
@@ -103,9 +103,8 @@ export function compile (_schema, partialOptions = {}) {
|
|
|
103
103
|
const options = fillOptions(partialOptions)
|
|
104
104
|
|
|
105
105
|
const schema = /** @type {import('ajv').SchemaObject} */(clone(_schema))
|
|
106
|
-
|
|
107
106
|
schema.$id = schema.$id ?? '_jl'
|
|
108
|
-
|
|
107
|
+
const getJSONRef = resolveLocaleRefs(schema, options.ajv, options.locale)
|
|
109
108
|
|
|
110
109
|
/** @type {string[]} */
|
|
111
110
|
const validatePointers = []
|
|
@@ -115,18 +114,26 @@ export function compile (_schema, partialOptions = {}) {
|
|
|
115
114
|
const expressionsDefinitions = []
|
|
116
115
|
/** @type {Record<string, string[]>} */
|
|
117
116
|
const validationErrors = {}
|
|
118
|
-
|
|
119
|
-
const
|
|
117
|
+
/** @type {Record<string, import('./types.js').SkeletonTree>} */
|
|
118
|
+
const skeletonTrees = {}
|
|
119
|
+
|
|
120
|
+
// makeSkeletonTree also mutates the schema (adding some error messages)
|
|
121
|
+
const mainTreePointer = `${schema.$id}#`
|
|
122
|
+
// @ts-ignore
|
|
123
|
+
skeletonTrees[mainTreePointer] = 'recursing'
|
|
124
|
+
skeletonTrees[mainTreePointer] = makeSkeletonTree(
|
|
120
125
|
schema,
|
|
126
|
+
schema.$id,
|
|
121
127
|
options,
|
|
128
|
+
getJSONRef,
|
|
129
|
+
skeletonTrees,
|
|
122
130
|
validatePointers,
|
|
123
131
|
validationErrors,
|
|
124
132
|
normalizedLayouts,
|
|
125
133
|
expressionsDefinitions,
|
|
126
|
-
|
|
134
|
+
mainTreePointer,
|
|
127
135
|
'main'
|
|
128
136
|
)
|
|
129
|
-
|
|
130
137
|
options.ajv.addSchema(schema)
|
|
131
138
|
|
|
132
139
|
const uriResolver = options.ajv.opts.uriResolver
|
|
@@ -168,7 +175,8 @@ export function compile (_schema, partialOptions = {}) {
|
|
|
168
175
|
return {
|
|
169
176
|
options,
|
|
170
177
|
schema,
|
|
171
|
-
|
|
178
|
+
mainTree: mainTreePointer,
|
|
179
|
+
skeletonTrees,
|
|
172
180
|
validates,
|
|
173
181
|
validationErrors,
|
|
174
182
|
normalizedLayouts,
|
package/src/compile/serialize.js
CHANGED
|
@@ -79,7 +79,8 @@ export const exportLocalizeErrors = localizeErrors;\n` + code
|
|
|
79
79
|
|
|
80
80
|
const ast = parseModule(code)
|
|
81
81
|
ast.exports.compiledLayout = {
|
|
82
|
-
|
|
82
|
+
mainTree: compiledLayout.mainTree,
|
|
83
|
+
skeletonTrees: clone(compiledLayout.skeletonTrees),
|
|
83
84
|
normalizedLayouts: clone(compiledLayout.normalizedLayouts),
|
|
84
85
|
validates: {},
|
|
85
86
|
validationErrors: compiledLayout.validationErrors,
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
// import Debug from 'debug'
|
|
2
2
|
import { normalizeLayoutFragment, isSwitchStruct, isGetItemsExpression, isGetItemsFetch, isItemsLayout, getSchemaFragmentType } from '@json-layout/vocabulary'
|
|
3
3
|
import { makeSkeletonTree } from './skeleton-tree.js'
|
|
4
|
+
import { partialResolveRefs } from './utils/resolve-refs.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* @param {any}
|
|
7
|
+
* @param {any} rawSchema
|
|
8
|
+
* @param {string} sourceSchemaId
|
|
7
9
|
* @param {import('./index.js').CompileOptions} options
|
|
10
|
+
* @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
|
|
11
|
+
* @param {Record<string, import('./types.js').SkeletonTree>} skeletonTrees
|
|
8
12
|
* @param {string[]} validates
|
|
9
13
|
* @param {Record<string, string[]>} validationErrors
|
|
10
14
|
* @param {Record<string, import('@json-layout/vocabulary').NormalizedLayout>} normalizedLayouts
|
|
11
15
|
* @param {import('@json-layout/vocabulary').Expression[]} expressions
|
|
12
16
|
* @param {string | number} key
|
|
13
|
-
* @param {string}
|
|
17
|
+
* @param {string} currentPointer
|
|
14
18
|
* @param {string | null} parentPointer
|
|
15
19
|
* @param {boolean} required
|
|
16
20
|
* @param {string} [condition]
|
|
@@ -19,24 +23,37 @@ import { makeSkeletonTree } from './skeleton-tree.js'
|
|
|
19
23
|
* @returns {import('./types.js').SkeletonNode}
|
|
20
24
|
*/
|
|
21
25
|
export function makeSkeletonNode (
|
|
22
|
-
|
|
26
|
+
rawSchema,
|
|
27
|
+
sourceSchemaId,
|
|
23
28
|
options,
|
|
29
|
+
getJSONRef,
|
|
30
|
+
skeletonTrees,
|
|
24
31
|
validates,
|
|
25
32
|
validationErrors,
|
|
26
33
|
normalizedLayouts,
|
|
27
34
|
expressions,
|
|
28
35
|
key,
|
|
29
|
-
|
|
36
|
+
currentPointer,
|
|
30
37
|
parentPointer,
|
|
31
38
|
required,
|
|
32
39
|
condition,
|
|
33
40
|
dependent,
|
|
34
41
|
knownType
|
|
35
42
|
) {
|
|
43
|
+
let schemaId = sourceSchemaId
|
|
44
|
+
let schema = rawSchema
|
|
45
|
+
let pointer = currentPointer
|
|
46
|
+
let refFragment
|
|
47
|
+
if (schema.$ref) {
|
|
48
|
+
[refFragment, schemaId, pointer] = getJSONRef(sourceSchemaId, schema.$ref)
|
|
49
|
+
schema = {...rawSchema, ...refFragment}
|
|
50
|
+
delete schema.$ref
|
|
51
|
+
}
|
|
52
|
+
schema = partialResolveRefs(schema, schemaId, getJSONRef)
|
|
36
53
|
const { type, nullable } = knownType ? { type: knownType, nullable: false } : getSchemaFragmentType(schema)
|
|
37
54
|
|
|
38
55
|
// improve on ajv error messages based on ajv-errors (https://ajv.js.org/packages/ajv-errors.html)
|
|
39
|
-
|
|
56
|
+
rawSchema.errorMessage = rawSchema.errorMessage ?? {}
|
|
40
57
|
if (!normalizedLayouts[pointer]) {
|
|
41
58
|
const normalizationResult = normalizeLayoutFragment(
|
|
42
59
|
/** @type {import('@json-layout/vocabulary').SchemaFragment} */(schema),
|
|
@@ -138,7 +155,10 @@ export function makeSkeletonNode (
|
|
|
138
155
|
const dependent = schema.dependentRequired && Object.values(schema.dependentRequired).some(dependentProperties => dependentProperties.includes(propertyKey))
|
|
139
156
|
node.children.push(makeSkeletonNode(
|
|
140
157
|
schema.properties[propertyKey],
|
|
158
|
+
schemaId,
|
|
141
159
|
options,
|
|
160
|
+
getJSONRef,
|
|
161
|
+
skeletonTrees,
|
|
142
162
|
validates,
|
|
143
163
|
validationErrors,
|
|
144
164
|
normalizedLayouts,
|
|
@@ -150,20 +170,16 @@ export function makeSkeletonNode (
|
|
|
150
170
|
undefined,
|
|
151
171
|
dependent
|
|
152
172
|
))
|
|
153
|
-
if (schema?.required?.includes(propertyKey)) {
|
|
154
|
-
schema.errorMessage.required = schema.errorMessage.required ?? {}
|
|
155
|
-
schema.errorMessage.required[propertyKey] = options.messages.errorRequired
|
|
156
|
-
}
|
|
157
|
-
if (schema.dependentRequired && Object.keys(schema.dependentRequired).includes(propertyKey)) {
|
|
158
|
-
schema.errorMessage.dependentRequired = options.messages.errorRequired
|
|
159
|
-
}
|
|
160
173
|
|
|
161
174
|
if (schema.dependentSchemas?.[propertyKey] || (schema.dependencies?.[propertyKey] && !Array.isArray(schema.dependencies[propertyKey]))) {
|
|
162
175
|
const dependentSchema = schema.dependentSchemas?.[propertyKey] ?? schema.dependencies[propertyKey]
|
|
163
176
|
const dependentPointer = schema.dependentSchemas?.[propertyKey] ? `${pointer}/dependentSchemas/${propertyKey}` : `${pointer}/dependencies/${propertyKey}`
|
|
164
177
|
node.children.push(makeSkeletonNode(
|
|
165
178
|
dependentSchema,
|
|
179
|
+
schemaId,
|
|
166
180
|
options,
|
|
181
|
+
getJSONRef,
|
|
182
|
+
skeletonTrees,
|
|
167
183
|
validates,
|
|
168
184
|
validationErrors,
|
|
169
185
|
normalizedLayouts,
|
|
@@ -184,7 +200,10 @@ export function makeSkeletonNode (
|
|
|
184
200
|
for (let i = 0; i < schema.allOf.length; i++) {
|
|
185
201
|
const allOfNode = makeSkeletonNode(
|
|
186
202
|
schema.allOf[i],
|
|
203
|
+
schemaId,
|
|
187
204
|
options,
|
|
205
|
+
getJSONRef,
|
|
206
|
+
skeletonTrees,
|
|
188
207
|
validates,
|
|
189
208
|
validationErrors,
|
|
190
209
|
normalizedLayouts,
|
|
@@ -220,23 +239,32 @@ export function makeSkeletonNode (
|
|
|
220
239
|
validationErrors[oneOfPointer.replace('_jl#', '/')] = normalizationResult.errors
|
|
221
240
|
}
|
|
222
241
|
}
|
|
223
|
-
/** @type {
|
|
242
|
+
/** @type {string[]} */
|
|
224
243
|
const childrenTrees = []
|
|
225
244
|
/** @type {string[]} */
|
|
226
245
|
for (let i = 0; i < schema.oneOf.length; i++) {
|
|
227
246
|
if (!schema.oneOf[i].type) schema.oneOf[i].type = type
|
|
228
247
|
const title = schema.oneOf[i].title ?? `option ${i}`
|
|
229
248
|
delete schema.oneOf[i].title
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
249
|
+
const childTreePointer = `${oneOfPointer}/${i}`
|
|
250
|
+
if (!skeletonTrees[childTreePointer]) {
|
|
251
|
+
// @ts-ignore
|
|
252
|
+
skeletonTrees[childTreePointer] = 'recursing'
|
|
253
|
+
skeletonTrees[childTreePointer] = makeSkeletonTree(
|
|
254
|
+
schema.oneOf[i],
|
|
255
|
+
schemaId,
|
|
256
|
+
options,
|
|
257
|
+
getJSONRef,
|
|
258
|
+
skeletonTrees,
|
|
259
|
+
validates,
|
|
260
|
+
validationErrors,
|
|
261
|
+
normalizedLayouts,
|
|
262
|
+
expressions,
|
|
263
|
+
childTreePointer,
|
|
264
|
+
title
|
|
265
|
+
)
|
|
266
|
+
}
|
|
267
|
+
childrenTrees.push(childTreePointer)
|
|
240
268
|
}
|
|
241
269
|
node.children = node.children ?? []
|
|
242
270
|
node.children.push({
|
|
@@ -244,7 +272,7 @@ export function makeSkeletonNode (
|
|
|
244
272
|
pointer: `${pointer}/oneOf`,
|
|
245
273
|
parentPointer: pointer,
|
|
246
274
|
childrenTrees,
|
|
247
|
-
pure: childrenTrees[0]
|
|
275
|
+
pure: skeletonTrees[childrenTrees[0]]?.root.pure,
|
|
248
276
|
propertyKeys: [],
|
|
249
277
|
roPropertyKeys: []
|
|
250
278
|
})
|
|
@@ -257,7 +285,10 @@ export function makeSkeletonNode (
|
|
|
257
285
|
node.children = node.children ?? []
|
|
258
286
|
node.children.push(makeSkeletonNode(
|
|
259
287
|
schema.then,
|
|
288
|
+
schemaId,
|
|
260
289
|
options,
|
|
290
|
+
getJSONRef,
|
|
291
|
+
skeletonTrees,
|
|
261
292
|
validates,
|
|
262
293
|
validationErrors,
|
|
263
294
|
normalizedLayouts,
|
|
@@ -275,7 +306,10 @@ export function makeSkeletonNode (
|
|
|
275
306
|
node.children = node.children ?? []
|
|
276
307
|
node.children.push(makeSkeletonNode(
|
|
277
308
|
schema.else,
|
|
309
|
+
schemaId,
|
|
278
310
|
options,
|
|
311
|
+
getJSONRef,
|
|
312
|
+
skeletonTrees,
|
|
279
313
|
validates,
|
|
280
314
|
validationErrors,
|
|
281
315
|
normalizedLayouts,
|
|
@@ -290,6 +324,16 @@ export function makeSkeletonNode (
|
|
|
290
324
|
))
|
|
291
325
|
}
|
|
292
326
|
}
|
|
327
|
+
|
|
328
|
+
for (const propertyKey of node.propertyKeys) {
|
|
329
|
+
if (schema?.required?.includes(propertyKey)) {
|
|
330
|
+
schema.errorMessage.required = schema.errorMessage.required ?? {}
|
|
331
|
+
schema.errorMessage.required[propertyKey] = options.messages.errorRequired
|
|
332
|
+
}
|
|
333
|
+
if (schema.dependentRequired && Object.keys(schema.dependentRequired).includes(propertyKey)) {
|
|
334
|
+
schema.errorMessage.dependentRequired = options.messages.errorRequired
|
|
335
|
+
}
|
|
336
|
+
}
|
|
293
337
|
}
|
|
294
338
|
|
|
295
339
|
if (type === 'array' && schema.items) {
|
|
@@ -297,7 +341,10 @@ export function makeSkeletonNode (
|
|
|
297
341
|
node.children = schema.items.map((/** @type {any} */ itemSchema, /** @type {number} */ i) => {
|
|
298
342
|
return makeSkeletonNode(
|
|
299
343
|
itemSchema,
|
|
344
|
+
schemaId,
|
|
300
345
|
options,
|
|
346
|
+
getJSONRef,
|
|
347
|
+
skeletonTrees,
|
|
301
348
|
validates,
|
|
302
349
|
validationErrors,
|
|
303
350
|
normalizedLayouts,
|
|
@@ -309,23 +356,34 @@ export function makeSkeletonNode (
|
|
|
309
356
|
)
|
|
310
357
|
})
|
|
311
358
|
} else {
|
|
312
|
-
|
|
313
|
-
|
|
359
|
+
const childTreePointer = `${pointer}/items`
|
|
360
|
+
if (!skeletonTrees[childTreePointer]) {
|
|
361
|
+
// @ts-ignore
|
|
362
|
+
skeletonTrees[childTreePointer] = 'recursing'
|
|
363
|
+
skeletonTrees[childTreePointer] = makeSkeletonTree(
|
|
314
364
|
schema.items,
|
|
365
|
+
schemaId,
|
|
315
366
|
options,
|
|
367
|
+
getJSONRef,
|
|
368
|
+
skeletonTrees,
|
|
316
369
|
validates,
|
|
317
370
|
validationErrors,
|
|
318
371
|
normalizedLayouts,
|
|
319
372
|
expressions,
|
|
320
|
-
|
|
373
|
+
childTreePointer,
|
|
321
374
|
schema.items.title
|
|
322
375
|
)
|
|
323
|
-
|
|
376
|
+
}
|
|
377
|
+
node.childrenTrees = [childTreePointer]
|
|
324
378
|
}
|
|
325
379
|
}
|
|
326
380
|
|
|
327
|
-
for (const child of node.children || [])
|
|
328
|
-
|
|
381
|
+
for (const child of node.children || []) {
|
|
382
|
+
if (!child.pure) node.pure = false
|
|
383
|
+
}
|
|
384
|
+
for (const childTree of node.childrenTrees || []) {
|
|
385
|
+
if (!skeletonTrees[childTree]?.root?.pure) node.pure = false
|
|
386
|
+
}
|
|
329
387
|
|
|
330
388
|
return node
|
|
331
389
|
}
|
|
@@ -5,7 +5,10 @@ import { makeSkeletonNode } from './skeleton-node.js'
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* @param {any} schema
|
|
8
|
+
* @param {string} schemaId
|
|
8
9
|
* @param {import('./index.js').CompileOptions} options
|
|
10
|
+
* @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
|
|
11
|
+
* @param {Record<string, import('./types.js').SkeletonTree>} skeletonTrees
|
|
9
12
|
* @param {string[]} validates
|
|
10
13
|
* @param {Record<string, string[]>} validationErrors
|
|
11
14
|
* @param {Record<string, import('@json-layout/vocabulary').NormalizedLayout>} normalizedLayouts
|
|
@@ -16,7 +19,10 @@ import { makeSkeletonNode } from './skeleton-node.js'
|
|
|
16
19
|
*/
|
|
17
20
|
export function makeSkeletonTree (
|
|
18
21
|
schema,
|
|
22
|
+
schemaId,
|
|
19
23
|
options,
|
|
24
|
+
getJSONRef,
|
|
25
|
+
skeletonTrees,
|
|
20
26
|
validates,
|
|
21
27
|
validationErrors,
|
|
22
28
|
normalizedLayouts,
|
|
@@ -24,7 +30,21 @@ export function makeSkeletonTree (
|
|
|
24
30
|
pointer,
|
|
25
31
|
title
|
|
26
32
|
) {
|
|
27
|
-
const root = makeSkeletonNode(
|
|
28
|
-
|
|
33
|
+
const root = makeSkeletonNode(
|
|
34
|
+
schema,
|
|
35
|
+
schemaId,
|
|
36
|
+
options,
|
|
37
|
+
getJSONRef,
|
|
38
|
+
skeletonTrees,
|
|
39
|
+
validates,
|
|
40
|
+
validationErrors,
|
|
41
|
+
normalizedLayouts,
|
|
42
|
+
expressions,
|
|
43
|
+
'',
|
|
44
|
+
pointer,
|
|
45
|
+
null,
|
|
46
|
+
true
|
|
47
|
+
)
|
|
48
|
+
validates.push(root.pointer)
|
|
29
49
|
return { title, root }
|
|
30
50
|
}
|
package/src/compile/types.ts
CHANGED
|
@@ -41,7 +41,8 @@ export type PartialCompileOptions = Partial<Omit<CompileOptions, 'messages'>> &
|
|
|
41
41
|
export interface CompiledLayout {
|
|
42
42
|
options?: CompileOptions
|
|
43
43
|
schema?: SchemaObject
|
|
44
|
-
|
|
44
|
+
mainTree: string,
|
|
45
|
+
skeletonTrees: Record<string, SkeletonTree>
|
|
45
46
|
validates: Record<string, ValidateFunction>
|
|
46
47
|
validationErrors: Record<string, string[]>
|
|
47
48
|
normalizedLayouts: Record<string, NormalizedLayout>
|
|
@@ -70,7 +71,7 @@ export interface SkeletonNode {
|
|
|
70
71
|
roPropertyKeys: string[]
|
|
71
72
|
condition?: Expression
|
|
72
73
|
children?: SkeletonNode[] // optional children in the case of arrays and object nodes
|
|
73
|
-
childrenTrees?:
|
|
74
|
+
childrenTrees?: string[] // other trees that can be instantiated with separate validation (for example in the case of new array items of oneOfs, etc)
|
|
74
75
|
required?: boolean
|
|
75
76
|
nullable?: boolean
|
|
76
77
|
}
|
|
@@ -1,69 +1,130 @@
|
|
|
1
1
|
import ajvModule from 'ajv/dist/2019.js'
|
|
2
|
-
|
|
3
|
-
const Ajv = ajvModule.default
|
|
2
|
+
import clone from '../../utils/clone.js'
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* @param {Record<string, import('ajv').SchemaObject>} schemas
|
|
7
|
-
* @param {string} ref
|
|
8
6
|
* @param {ajvModule.default} ajv
|
|
9
|
-
* @returns {[any, string]}
|
|
7
|
+
* @returns {(schemaId: string, ref: string) => [any, string, string]}
|
|
10
8
|
*/
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
9
|
+
const prepareGetJSONRef = (schemas, ajv) =>
|
|
10
|
+
{
|
|
11
|
+
return (sourceSchemaId, ref) => {
|
|
12
|
+
const fullRef = ajv.opts.uriResolver.resolve(sourceSchemaId, ref)
|
|
13
|
+
const [schemaId, pointer] = fullRef.split('#')
|
|
14
|
+
schemas[schemaId] = schemas[schemaId] ?? (ajv.getSchema(schemaId)?.schema)
|
|
15
|
+
if (!schemas[schemaId]) throw new Error(`reference not found ${schemaId}`)
|
|
16
|
+
const pointerParts = pointer.split('/').filter(p => !!p)
|
|
17
|
+
const { value: fragment } = pointerParts.reduce((a, pointerPart) => {
|
|
18
|
+
a.path.push(pointerPart)
|
|
19
|
+
if (!(pointerPart in a.value)) throw new Error(`reference not found ${schemaId}#${a.path.join('/')}`)
|
|
20
|
+
a.value = a.value[pointerPart]
|
|
21
|
+
return a
|
|
22
|
+
}, { path: /** @type {string[]} */([]), value: schemas[schemaId] })
|
|
23
|
+
return [fragment, schemaId, fullRef]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* mutates a schema by replacing ~$locale~ in all refs
|
|
29
|
+
* @param {import('ajv').SchemaObject} schema
|
|
30
|
+
* @param {ajvModule.default} ajv
|
|
31
|
+
* @param {string} locale
|
|
32
|
+
* @returns {(schemaId: string, ref: string) => [any, string, string]}
|
|
33
|
+
*/
|
|
34
|
+
export function resolveLocaleRefs (schema, ajv, locale = 'en') {
|
|
35
|
+
if (!schema.$id) throw new Error('missing schema id')
|
|
36
|
+
const getJSONRef = prepareGetJSONRef({ [schema.$id]: schema }, ajv)
|
|
37
|
+
/** @type {any[]} */
|
|
38
|
+
const recursed = []
|
|
39
|
+
recurseResolveLocale(schema, schema.$id, getJSONRef, locale, recursed)
|
|
40
|
+
return getJSONRef
|
|
23
41
|
}
|
|
24
42
|
|
|
25
43
|
/**
|
|
26
|
-
* @param {Record<string, import('ajv').SchemaObject>} schemas
|
|
27
44
|
* @param {import('ajv').SchemaObject} schemaFragment
|
|
28
45
|
* @param {string} schemaId
|
|
29
|
-
* @param {
|
|
46
|
+
* @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
|
|
30
47
|
* @param {string} locale
|
|
31
|
-
* @
|
|
48
|
+
* @param {any[]} recursed
|
|
32
49
|
*/
|
|
33
|
-
|
|
50
|
+
|
|
51
|
+
const recurseResolveLocale = (schemaFragment, schemaId, getJSONRef, locale, recursed) => {
|
|
52
|
+
if (recursed.includes(schemaFragment)) return
|
|
53
|
+
recursed.push(schemaFragment)
|
|
34
54
|
for (const key of Object.keys(schemaFragment)) {
|
|
35
55
|
if (schemaFragment[key] && typeof schemaFragment[key] === 'object') {
|
|
36
56
|
if ('$ref' in schemaFragment[key]) {
|
|
37
|
-
const
|
|
38
|
-
const
|
|
57
|
+
const ref = schemaFragment[key].$ref.replace('~$locale~', locale)
|
|
58
|
+
const refDefaultLocale = schemaFragment[key].$ref.replace('~$locale~', 'en')
|
|
39
59
|
let refFragment, refSchemaId
|
|
40
60
|
try {
|
|
41
|
-
[refFragment, refSchemaId] = getJSONRef(
|
|
61
|
+
[refFragment, refSchemaId] = getJSONRef(schemaId, ref)
|
|
62
|
+
schemaFragment[key].$ref = ref
|
|
42
63
|
} catch (err) {
|
|
43
|
-
[refFragment, refSchemaId] = getJSONRef(
|
|
64
|
+
[refFragment, refSchemaId] = getJSONRef(schemaId, refDefaultLocale)
|
|
65
|
+
schemaFragment[key].$ref = refDefaultLocale
|
|
44
66
|
}
|
|
45
|
-
if (typeof refFragment === '
|
|
46
|
-
schemaFragment[key] = { ...refFragment, ...schemaFragment[key] }
|
|
47
|
-
delete schemaFragment[key].$ref
|
|
48
|
-
} else {
|
|
67
|
+
if (typeof refFragment === 'string') {
|
|
49
68
|
schemaFragment[key] = refFragment
|
|
69
|
+
} else {
|
|
70
|
+
recurseResolveLocale(refFragment, refSchemaId, getJSONRef, locale, recursed)
|
|
50
71
|
}
|
|
51
|
-
recurse(schemas, schemaFragment[key], refSchemaId, ajv, locale)
|
|
52
72
|
} else {
|
|
53
|
-
|
|
73
|
+
recurseResolveLocale(schemaFragment[key], schemaId, getJSONRef, locale, recursed)
|
|
54
74
|
}
|
|
55
75
|
}
|
|
56
76
|
}
|
|
57
|
-
return schemaFragment
|
|
58
77
|
}
|
|
59
78
|
|
|
60
79
|
/**
|
|
80
|
+
* partially resolve a schema but not recursively, used for layout normalization
|
|
61
81
|
* @param {import('ajv').SchemaObject} schema
|
|
62
|
-
* @param {
|
|
63
|
-
* @param {string}
|
|
64
|
-
* @returns {import('ajv').SchemaObject}
|
|
82
|
+
* @param {string} schemaId
|
|
83
|
+
* @param {(schemaId: string, ref: string) => [any, string, string]} getJSONRef
|
|
65
84
|
*/
|
|
66
|
-
export function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
85
|
+
export function partialResolveRefs (schema, schemaId, getJSONRef) {
|
|
86
|
+
let clonedSchema = null
|
|
87
|
+
if (schema.items && schema.items.$ref) {
|
|
88
|
+
const [refFragment] = getJSONRef(schemaId, schema.items.$ref)
|
|
89
|
+
clonedSchema = clonedSchema ?? clone(schema)
|
|
90
|
+
clonedSchema.items = { ...refFragment, ... schema.items }
|
|
91
|
+
}
|
|
92
|
+
if (schema.properties) {
|
|
93
|
+
for (const key in schema.properties) {
|
|
94
|
+
if (schema.properties[key].$ref) {
|
|
95
|
+
const [refFragment] = getJSONRef(schemaId, schema.properties[key].$ref)
|
|
96
|
+
clonedSchema = clonedSchema ?? clone(schema)
|
|
97
|
+
clonedSchema.properties[key] = { ...refFragment, ... schema.properties[key] }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (schema.oneOf) {
|
|
102
|
+
for (let i = 0; i < schema.oneOf.length; i++) {
|
|
103
|
+
if (schema.oneOf[i].$ref) {
|
|
104
|
+
const [refFragment] = getJSONRef(schemaId, schema.oneOf[i].$ref)
|
|
105
|
+
clonedSchema = clonedSchema ?? clone(schema)
|
|
106
|
+
clonedSchema.oneOf[i] = { ...refFragment, ... schema.oneOf[i] }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (schema.anyOf) {
|
|
111
|
+
for (let i = 0; i < schema.anyOf.length; i++) {
|
|
112
|
+
if (schema.anyOf[i].$ref) {
|
|
113
|
+
const [refFragment] = getJSONRef(schemaId, schema.anyOf[i].$ref)
|
|
114
|
+
clonedSchema = clonedSchema ?? clone(schema)
|
|
115
|
+
clonedSchema.anyOf[i] = { ...refFragment, ... schema.anyOf[i] }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (schema.allOf) {
|
|
120
|
+
for (let i = 0; i < schema.allOf.length; i++) {
|
|
121
|
+
if (schema.allOf[i].$ref) {
|
|
122
|
+
const [refFragment] = getJSONRef(schemaId, schema.allOf[i].$ref)
|
|
123
|
+
clonedSchema = clonedSchema ?? clone(schema)
|
|
124
|
+
clonedSchema.allOf[i] = { ...refFragment, ... schema.allOf[i] }
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return clonedSchema ?? schema
|
|
130
|
+
}
|
package/src/state/index.js
CHANGED
|
@@ -227,7 +227,7 @@ export class StatefulLayout {
|
|
|
227
227
|
this._previousAutofocusTarget = null
|
|
228
228
|
this._data = data
|
|
229
229
|
this.initValidationState()
|
|
230
|
-
this.
|
|
230
|
+
this.activatedItems = {}
|
|
231
231
|
this.updateState()
|
|
232
232
|
this.handleAutofocus()
|
|
233
233
|
}
|
|
@@ -295,7 +295,7 @@ export class StatefulLayout {
|
|
|
295
295
|
createStateTree (rehydrate = false) {
|
|
296
296
|
/** @type {CreateStateTreeContext} */
|
|
297
297
|
const createStateTreeContext = {
|
|
298
|
-
|
|
298
|
+
activatedItems: this.activatedItems,
|
|
299
299
|
autofocusTarget: this._autofocusTarget,
|
|
300
300
|
initial: !this._lastCreateStateTreeContext,
|
|
301
301
|
rehydrate,
|
|
@@ -322,7 +322,6 @@ export class StatefulLayout {
|
|
|
322
322
|
validatedChildren: createStateTreeContext.nodes.filter(n => n.validated).map(n => n.fullKey)
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
|
-
this.activeItems = createStateTreeContext.activeItems
|
|
326
325
|
this.files = shallowProduceArray(this.files, createStateTreeContext.files)
|
|
327
326
|
}
|
|
328
327
|
|
|
@@ -416,7 +415,7 @@ export class StatefulLayout {
|
|
|
416
415
|
}
|
|
417
416
|
|
|
418
417
|
if (activateKey !== undefined) {
|
|
419
|
-
this.
|
|
418
|
+
this.activatedItems = produce(this.activatedItems, draft => { draft[node.fullKey] = activateKey })
|
|
420
419
|
this._autofocusTarget = node.fullKey + '/' + activateKey
|
|
421
420
|
}
|
|
422
421
|
if (node.parentFullKey === null) {
|
|
@@ -625,14 +624,14 @@ export class StatefulLayout {
|
|
|
625
624
|
/**
|
|
626
625
|
* @type {Record<string, number>}
|
|
627
626
|
*/
|
|
628
|
-
|
|
627
|
+
activatedItems
|
|
629
628
|
|
|
630
629
|
/**
|
|
631
630
|
* @param {StateNode} node
|
|
632
631
|
* @param {number} key
|
|
633
632
|
*/
|
|
634
633
|
activateItem (node, key) {
|
|
635
|
-
this.
|
|
634
|
+
this.activatedItems = produce(this.activatedItems, draft => { draft[node.fullKey] = key })
|
|
636
635
|
this._autofocusTarget = node.fullKey + '/' + key
|
|
637
636
|
if (node.key === '$oneOf') {
|
|
638
637
|
this.input(node, undefined)
|
|
@@ -646,7 +645,12 @@ export class StatefulLayout {
|
|
|
646
645
|
* @param {StateNode} node
|
|
647
646
|
*/
|
|
648
647
|
deactivateItem (node) {
|
|
649
|
-
|
|
648
|
+
// also deactivate children oneOf for example
|
|
649
|
+
this.activatedItems = produce(this.activatedItems, draft => {
|
|
650
|
+
for (const key in draft) {
|
|
651
|
+
if (key.startsWith(node.fullKey)) delete draft[key]
|
|
652
|
+
}
|
|
653
|
+
})
|
|
650
654
|
this.updateState()
|
|
651
655
|
}
|
|
652
656
|
|