@@ -201,6 +201,7 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {
201
201
const recursiveLog = [ ]
202
202
const getMeta = ( ) => rootMeta . get ( root )
203
203
const basePathStack = basePathRoot ? [ basePathRoot ] : [ ]
204
+ const recursiveDelta = isPlainObject ( schema ) && ( schema . $dynamicAnchor || schema . $recursiveAnchor )
204
205
const visit = ( errors , history , current , node , schemaPath , trace = { } , { constProp } = { } ) => {
205
206
// e.g. top-level data and property names, OR already checked by present() in history, OR in keys and not undefined
206
207
const isSub = history . length > 0 && history [ history . length - 1 ] . prop === current
@@ -264,7 +265,10 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {
264
265
265
266
// evaluated tracing
266
267
const stat = initTracing ( )
267
- const evaluateDelta = ( delta ) => applyDelta ( stat , delta )
268
+ const evaluateDelta = ( delta ) => {
269
+ applyDelta ( stat , delta )
270
+ if ( recursiveDelta && node === schema ) evaluateDeltaDynamic ( delta , true )
271
+ }
268
272
269
273
if ( typeof node === 'boolean' ) {
270
274
if ( node === true ) {
@@ -379,30 +383,36 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {
379
383
380
384
// evaluated: declare dynamic
381
385
const needUnevaluated = ( rule ) =>
382
- opts [ optDynamic ] && ( node [ rule ] || node [ rule ] === false || node === schema )
386
+ ( opts [ optDynamic ] && ( node [ rule ] || node [ rule ] === false || node === schema ) ) ||
387
+ ( recursiveDelta && node === schema ) // FIXME
383
388
const local = Object . freeze ( {
384
389
item : needUnevaluated ( 'unevaluatedItems' ) ? gensym ( 'evaluatedItem' ) : null ,
385
390
items : needUnevaluated ( 'unevaluatedItems' ) ? gensym ( 'evaluatedItems' ) : null ,
386
391
props : needUnevaluated ( 'unevaluatedProperties' ) ? gensym ( 'evaluatedProps' ) : null ,
387
392
} )
393
+ // RECHECK!
388
394
const dyn = Object . freeze ( {
389
395
item : local . item || trace . item ,
390
396
items : local . items || trace . items ,
391
397
props : local . props || trace . props ,
392
398
} )
399
+ // RECHECK!
393
400
const canSkipDynamic = ( ) =>
394
401
( ! dyn . items || stat . items === Infinity ) && ( ! dyn . props || stat . properties . includes ( true ) )
395
- const evaluateDeltaDynamic = ( delta ) => {
396
- // Skips applying those that have already been proved statically
397
- if ( dyn . item && delta . item && stat . items !== Infinity )
402
+ const evaluateDeltaDynamic = ( delta , noskip = false ) => {
403
+ // Skips applying those that have already been proven statically (if not enforced for dynamic recursion)
404
+ if ( dyn . item && delta . item && ( noskip || stat . items !== Infinity ) ) {
398
405
fun . write ( '%s.push(%s)' , dyn . item , delta . item )
399
- if ( dyn . items && delta . items > stat . items ) fun . write ( '%s.push(%d)' , dyn . items , delta . items )
400
- if ( dyn . props && ( delta . properties || [ ] ) . includes ( true ) && ! stat . properties . includes ( true ) ) {
401
- fun . write ( '%s[0].push(true)' , dyn . props )
406
+ }
407
+ if ( dyn . items && delta . items && ( noskip || delta . items > stat . items ) ) {
408
+ fun . write ( '%s.push(%d)' , dyn . items , delta . items )
409
+ }
410
+ if ( dyn . props && ( delta . properties || [ ] ) . includes ( true ) ) {
411
+ if ( noskip || ! stat . properties . includes ( true ) ) fun . write ( '%s[0].push(true)' , dyn . props )
402
412
} else if ( dyn . props ) {
403
413
const inStat = ( properties , patterns ) => inProperties ( stat , { properties, patterns } )
404
- const properties = ( delta . properties || [ ] ) . filter ( ( x ) => ! inStat ( [ x ] , [ ] ) )
405
- const patterns = ( delta . patterns || [ ] ) . filter ( ( x ) => ! inStat ( [ ] , [ x ] ) )
414
+ const properties = ( delta . properties || [ ] ) . filter ( ( x ) => noskip || ! inStat ( [ x ] , [ ] ) )
415
+ const patterns = ( delta . patterns || [ ] ) . filter ( ( x ) => noskip || ! inStat ( [ ] , [ x ] ) )
406
416
if ( properties . length > 0 ) fun . write ( '%s[0].push(...%j)' , dyn . props , properties )
407
417
if ( patterns . length > 0 ) fun . write ( '%s[1].push(...%j)' , dyn . props , patterns )
408
418
for ( const sym of delta . propertiesVars || [ ] ) fun . write ( '%s[0].push(%s)' , dyn . props , sym )
@@ -1360,8 +1370,11 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {
1360
1370
if ( refsNeedFullValidation . has ( funname ) ) throw new Error ( 'Unexpected: unvalidated cyclic ref' )
1361
1371
1362
1372
// evaluated: return dynamic for refs
1363
- if ( opts [ optDynamic ] && ( isDynamic ( stat ) . items || isDynamic ( stat ) . properties ) ) {
1364
- if ( ! local ) throw new Error ( 'Failed to trace dynamic properties' ) // Unreachable
1373
+ if (
1374
+ ( opts [ optDynamic ] && ( isDynamic ( stat ) . items || isDynamic ( stat ) . properties ) ) ||
1375
+ recursiveDelta
1376
+ ) {
1377
+ if ( ! local || ! local . props ) throw new Error ( 'Failed to trace dynamic properties' ) // Unreachable
1365
1378
fun . write ( 'validate.evaluatedDynamic = [%s, %s, %s]' , local . item , local . items , local . props )
1366
1379
}
1367
1380
0 commit comments