diff --git a/lib/index.js b/lib/index.js index 7447a2f..d1f5704 100644 --- a/lib/index.js +++ b/lib/index.js @@ -251,7 +251,17 @@ function derefSchema (schema, options, state) { delete node.$ref newValue = _.merge({}, newValue, node) } - this.update(newValue) + // Only walk into a resolved value the first time it is substituted. + // The same object is reused at every site pointing at that ref, so + // re-walking it repeats work already done - and when refs nest, each + // level is re-walked once per path that reaches it, which is + // exponential in the number of definitions. + const alreadyResolved = + newValue !== null && typeof newValue === 'object' && state.resolved.has(newValue) + if (newValue !== null && typeof newValue === 'object') { + state.resolved.add(newValue) + } + this.update(newValue, alreadyResolved) if (state.missing.indexOf(refVal) !== -1) { state.missing.splice(state.missing.indexOf(refVal), 1) } @@ -293,7 +303,11 @@ function deref (schema, options) { circularRefs: [], cwd: cwd, missing: [], - history: [] + history: [], + // Values already dereferenced during this call. A resolved ref is shared - + // the same object is substituted at every site that points at it - so once + // one has been walked there is nothing left to do on later substitutions. + resolved: new WeakSet() } try {