@json-layout/core 0.21.1 → 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 +78 -23
- 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,
|
|
@@ -156,7 +176,10 @@ export function makeSkeletonNode (
|
|
|
156
176
|
const dependentPointer = schema.dependentSchemas?.[propertyKey] ? `${pointer}/dependentSchemas/${propertyKey}` : `${pointer}/dependencies/${propertyKey}`
|
|
157
177
|
node.children.push(makeSkeletonNode(
|
|
158
178
|
dependentSchema,
|
|
179
|
+
schemaId,
|
|
159
180
|
options,
|
|
181
|
+
getJSONRef,
|
|
182
|
+
skeletonTrees,
|
|
160
183
|
validates,
|
|
161
184
|
validationErrors,
|
|
162
185
|
normalizedLayouts,
|
|
@@ -177,7 +200,10 @@ export function makeSkeletonNode (
|
|
|
177
200
|
for (let i = 0; i < schema.allOf.length; i++) {
|
|
178
201
|
const allOfNode = makeSkeletonNode(
|
|
179
202
|
schema.allOf[i],
|
|
203
|
+
schemaId,
|
|
180
204
|
options,
|
|
205
|
+
getJSONRef,
|
|
206
|
+
skeletonTrees,
|
|
181
207
|
validates,
|
|
182
208
|
validationErrors,
|
|
183
209
|
normalizedLayouts,
|
|
@@ -213,23 +239,32 @@ export function makeSkeletonNode (
|
|
|
213
239
|
validationErrors[oneOfPointer.replace('_jl#', '/')] = normalizationResult.errors
|
|
214
240
|
}
|
|
215
241
|
}
|
|
216
|
-
/** @type {
|
|
242
|
+
/** @type {string[]} */
|
|
217
243
|
const childrenTrees = []
|
|
218
244
|
/** @type {string[]} */
|
|
219
245
|
for (let i = 0; i < schema.oneOf.length; i++) {
|
|
220
246
|
if (!schema.oneOf[i].type) schema.oneOf[i].type = type
|
|
221
247
|
const title = schema.oneOf[i].title ?? `option ${i}`
|
|
222
248
|
delete schema.oneOf[i].title
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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)
|
|
233
268
|
}
|
|
234
269
|
node.children = node.children ?? []
|
|
235
270
|
node.children.push({
|
|
@@ -237,7 +272,7 @@ export function makeSkeletonNode (
|
|
|
237
272
|
pointer: `${pointer}/oneOf`,
|
|
238
273
|
parentPointer: pointer,
|
|
239
274
|
childrenTrees,
|
|
240
|
-
pure: childrenTrees[0]
|
|
275
|
+
pure: skeletonTrees[childrenTrees[0]]?.root.pure,
|
|
241
276
|
propertyKeys: [],
|
|
242
277
|
roPropertyKeys: []
|
|
243
278
|
})
|
|
@@ -250,7 +285,10 @@ export function makeSkeletonNode (
|
|
|
250
285
|
node.children = node.children ?? []
|
|
251
286
|
node.children.push(makeSkeletonNode(
|
|
252
287
|
schema.then,
|
|
288
|
+
schemaId,
|
|
253
289
|
options,
|
|
290
|
+
getJSONRef,
|
|
291
|
+
skeletonTrees,
|
|
254
292
|
validates,
|
|
255
293
|
validationErrors,
|
|
256
294
|
normalizedLayouts,
|
|
@@ -268,7 +306,10 @@ export function makeSkeletonNode (
|
|
|
268
306
|
node.children = node.children ?? []
|
|
269
307
|
node.children.push(makeSkeletonNode(
|
|
270
308
|
schema.else,
|
|
309
|
+
schemaId,
|
|
271
310
|
options,
|
|
311
|
+
getJSONRef,
|
|
312
|
+
skeletonTrees,
|
|
272
313
|
validates,
|
|
273
314
|
validationErrors,
|
|
274
315
|
normalizedLayouts,
|
|
@@ -300,7 +341,10 @@ export function makeSkeletonNode (
|
|
|
300
341
|
node.children = schema.items.map((/** @type {any} */ itemSchema, /** @type {number} */ i) => {
|
|
301
342
|
return makeSkeletonNode(
|
|
302
343
|
itemSchema,
|
|
344
|
+
schemaId,
|
|
303
345
|
options,
|
|
346
|
+
getJSONRef,
|
|
347
|
+
skeletonTrees,
|
|
304
348
|
validates,
|
|
305
349
|
validationErrors,
|
|
306
350
|
normalizedLayouts,
|
|
@@ -312,23 +356,34 @@ export function makeSkeletonNode (
|
|
|
312
356
|
)
|
|
313
357
|
})
|
|
314
358
|
} else {
|
|
315
|
-
|
|
316
|
-
|
|
359
|
+
const childTreePointer = `${pointer}/items`
|
|
360
|
+
if (!skeletonTrees[childTreePointer]) {
|
|
361
|
+
// @ts-ignore
|
|
362
|
+
skeletonTrees[childTreePointer] = 'recursing'
|
|
363
|
+
skeletonTrees[childTreePointer] = makeSkeletonTree(
|
|
317
364
|
schema.items,
|
|
365
|
+
schemaId,
|
|
318
366
|
options,
|
|
367
|
+
getJSONRef,
|
|
368
|
+
skeletonTrees,
|
|
319
369
|
validates,
|
|
320
370
|
validationErrors,
|
|
321
371
|
normalizedLayouts,
|
|
322
372
|
expressions,
|
|
323
|
-
|
|
373
|
+
childTreePointer,
|
|
324
374
|
schema.items.title
|
|
325
375
|
)
|
|
326
|
-
|
|
376
|
+
}
|
|
377
|
+
node.childrenTrees = [childTreePointer]
|
|
327
378
|
}
|
|
328
379
|
}
|
|
329
380
|
|
|
330
|
-
for (const child of node.children || [])
|
|
331
|
-
|
|
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
|
+
}
|
|
332
387
|
|
|
333
388
|
return node
|
|
334
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
|
|