@@ -191,6 +191,7 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {
191
191
const recursiveLog = [ ]
192
192
const getMeta = ( ) => rootMeta . get ( root )
193
193
const basePathStack = basePathRoot ? [ basePathRoot ] : [ ]
194
+ const recursiveDelta = isPlainObject ( schema ) && ( schema . $dynamicAnchor || schema . $recursiveAnchor )
194
195
const visit = ( errors , history , current , node , schemaPath , trace = { } , { constProp } = { } ) => {
195
196
// e.g. top-level data and property names, OR already checked by present() in history, OR in keys and not undefined
196
197
const isSub = history . length > 0 && history [ history . length - 1 ] . prop === current
@@ -254,7 +255,10 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {
254
255
255
256
// evaluated tracing
256
257
const stat = initTracing ( )
257
- const evaluateDelta = ( delta ) => applyDelta ( stat , delta )
258
+ const evaluateDelta = ( delta ) => {
259
+ applyDelta ( stat , delta )
260
+ if ( recursiveDelta && node === schema ) evaluateDeltaDynamic ( delta , true )
261
+ }
258
262
259
263
if ( typeof node === 'boolean' ) {
260
264
if ( node === true ) {
@@ -369,30 +373,36 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {
369
373
370
374
// evaluated: declare dynamic
371
375
const needUnevaluated = ( rule ) =>
372
- opts [ optDynamic ] && ( node [ rule ] || node [ rule ] === false || node === schema )
376
+ ( opts [ optDynamic ] && ( node [ rule ] || node [ rule ] === false || node === schema ) ) ||
377
+ ( recursiveDelta && node === schema ) // FIXME
373
378
const local = Object . freeze ( {
374
379
item : needUnevaluated ( 'unevaluatedItems' ) ? gensym ( 'evaluatedItem' ) : null ,
375
380
items : needUnevaluated ( 'unevaluatedItems' ) ? gensym ( 'evaluatedItems' ) : null ,
376
381
props : needUnevaluated ( 'unevaluatedProperties' ) ? gensym ( 'evaluatedProps' ) : null ,
377
382
} )
383
+ // RECHECK!
378
384
const dyn = Object . freeze ( {
379
385
item : local . item || trace . item ,
380
386
items : local . items || trace . items ,
381
387
props : local . props || trace . props ,
382
388
} )
389
+ // RECHECK!
383
390
const canSkipDynamic = ( ) =>
384
391
( ! dyn . items || stat . items === Infinity ) && ( ! dyn . props || stat . properties . includes ( true ) )
385
- const evaluateDeltaDynamic = ( delta ) => {
386
- // Skips applying those that have already been proved statically
387
- if ( dyn . item && delta . item && stat . items !== Infinity )
392
+ const evaluateDeltaDynamic = ( delta , noskip = false ) => {
393
+ // Skips applying those that have already been proven statically (if not enforced for dynamic recursion)
394
+ if ( dyn . item && delta . item && ( noskip || stat . items !== Infinity ) ) {
388
395
fun . write ( '%s.push(%s)' , dyn . item , delta . item )
389
- if ( dyn . items && delta . items > stat . items ) fun . write ( '%s.push(%d)' , dyn . items , delta . items )
390
- if ( dyn . props && ( delta . properties || [ ] ) . includes ( true ) && ! stat . properties . includes ( true ) ) {
391
- fun . write ( '%s[0].push(true)' , dyn . props )
396
+ }
397
+ if ( dyn . items && delta . items && ( noskip || delta . items > stat . items ) ) {
398
+ fun . write ( '%s.push(%d)' , dyn . items , delta . items )
399
+ }
400
+ if ( dyn . props && ( delta . properties || [ ] ) . includes ( true ) ) {
401
+ if ( noskip || ! stat . properties . includes ( true ) ) fun . write ( '%s[0].push(true)' , dyn . props )
392
402
} else if ( dyn . props ) {
393
403
const inStat = ( properties , patterns ) => inProperties ( stat , { properties, patterns } )
394
- const properties = ( delta . properties || [ ] ) . filter ( ( x ) => ! inStat ( [ x ] , [ ] ) )
395
- const patterns = ( delta . patterns || [ ] ) . filter ( ( x ) => ! inStat ( [ ] , [ x ] ) )
404
+ const properties = ( delta . properties || [ ] ) . filter ( ( x ) => noskip || ! inStat ( [ x ] , [ ] ) )
405
+ const patterns = ( delta . patterns || [ ] ) . filter ( ( x ) => noskip || ! inStat ( [ ] , [ x ] ) )
396
406
if ( properties . length > 0 ) fun . write ( '%s[0].push(...%j)' , dyn . props , properties )
397
407
if ( patterns . length > 0 ) fun . write ( '%s[1].push(...%j)' , dyn . props , patterns )
398
408
for ( const sym of delta . propertiesVars || [ ] ) fun . write ( '%s[0].push(%s)' , dyn . props , sym )
@@ -1351,8 +1361,11 @@ const compileSchema = (schema, root, opts, scope, basePathRoot = '') => {
1351
1361
if ( refsNeedFullValidation . has ( funname ) ) throw new Error ( 'Unexpected: unvalidated cyclic ref' )
1352
1362
1353
1363
// evaluated: return dynamic for refs
1354
- if ( opts [ optDynamic ] && ( isDynamic ( stat ) . items || isDynamic ( stat ) . properties ) ) {
1355
- if ( ! local ) throw new Error ( 'Failed to trace dynamic properties' ) // Unreachable
1364
+ if (
1365
+ ( opts [ optDynamic ] && ( isDynamic ( stat ) . items || isDynamic ( stat ) . properties ) ) ||
1366
+ recursiveDelta
1367
+ ) {
1368
+ if ( ! local || ! local . props ) throw new Error ( 'Failed to trace dynamic properties' ) // Unreachable
1356
1369
fun . write ( 'validate.evaluatedDynamic = [%s, %s, %s]' , local . item , local . items , local . props )
1357
1370
}
1358
1371
0 commit comments