diff --git a/index.js b/index.js index 862c7c83..dbe8837e 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ const Validator = require('./lib/validator') const Location = require('./lib/location') const validate = require('./lib/schema-validator') const mergeSchemas = require('./lib/merge-schemas') +const hash = require('object-hash') const SINGLE_TICK = /'/g @@ -410,6 +411,10 @@ function buildInnerObject (context, location) { return code } +function getMergedSchemasIdsKey (schema, mergeContext) { + return `${hash.sha1(schema)}:${mergeContext}` +} + function mergeLocations (context, mergedSchemaId, mergedLocations) { for (let i = 0; i < mergedLocations.length; i++) { const location = mergedLocations[i] @@ -444,9 +449,9 @@ function cloneOriginSchema (context, schema, schemaId) { schemaId = schema.$id } - const mergedSchemaRef = context.mergedSchemasIds.get(schema) + const mergedSchemaRef = context.mergedSchemasIds.get(getMergedSchemasIdsKey(schema, 'cloneOrigin')) if (mergedSchemaRef) { - context.mergedSchemasIds.set(clonedSchema, mergedSchemaRef) + context.mergedSchemasIds.set(getMergedSchemasIdsKey(clonedSchema, 'cloneOrigin'), mergedSchemaRef) } for (const key in schema) { @@ -809,14 +814,14 @@ function buildConstSerializer (location, input) { function buildAllOf (context, location, input) { const schema = location.schema - let mergedSchemaId = context.mergedSchemasIds.get(schema) + let mergedSchemaId = context.mergedSchemasIds.get(getMergedSchemasIdsKey(schema, 'AllOf')) if (mergedSchemaId) { const mergedLocation = getMergedLocation(context, mergedSchemaId) return buildValue(context, mergedLocation, input) } mergedSchemaId = `__fjs_merged_${schemaIdCounter++}` - context.mergedSchemasIds.set(schema, mergedSchemaId) + context.mergedSchemasIds.set(getMergedSchemasIdsKey(schema, 'AllOf'), mergedSchemaId) const { allOf, ...schemaWithoutAllOf } = location.schema const locations = [ @@ -857,13 +862,13 @@ function buildOneOf (context, location, input) { const optionLocation = oneOfsLocation.getPropertyLocation(index) const optionSchema = optionLocation.schema - let mergedSchemaId = context.mergedSchemasIds.get(optionSchema) + let mergedSchemaId = context.mergedSchemasIds.get(getMergedSchemasIdsKey(optionSchema, 'OneOf')) let mergedLocation = null if (mergedSchemaId) { mergedLocation = getMergedLocation(context, mergedSchemaId) } else { mergedSchemaId = `__fjs_merged_${schemaIdCounter++}` - context.mergedSchemasIds.set(optionSchema, mergedSchemaId) + context.mergedSchemasIds.set(getMergedSchemasIdsKey(optionSchema, 'OneOf'), mergedSchemaId) mergedLocation = mergeLocations(context, mergedSchemaId, [ locationWithoutOneOf, @@ -911,13 +916,13 @@ function buildIfThenElse (context, location, input) { const ifSchemaRef = ifLocation.getSchemaRef() const thenLocation = location.getPropertyLocation('then') - let thenMergedSchemaId = context.mergedSchemasIds.get(thenSchema) + let thenMergedSchemaId = context.mergedSchemasIds.get(getMergedSchemasIdsKey(thenSchema, 'Then')) let thenMergedLocation = null if (thenMergedSchemaId) { thenMergedLocation = getMergedLocation(context, thenMergedSchemaId) } else { thenMergedSchemaId = `__fjs_merged_${schemaIdCounter++}` - context.mergedSchemasIds.set(thenSchema, thenMergedSchemaId) + context.mergedSchemasIds.set(getMergedSchemasIdsKey(thenSchema, 'Then'), thenMergedSchemaId) thenMergedLocation = mergeLocations(context, thenMergedSchemaId, [ rootLocation, @@ -936,13 +941,13 @@ function buildIfThenElse (context, location, input) { } const elseLocation = location.getPropertyLocation('else') - let elseMergedSchemaId = context.mergedSchemasIds.get(elseSchema) + let elseMergedSchemaId = context.mergedSchemasIds.get(getMergedSchemasIdsKey(elseSchema, 'Else')) let elseMergedLocation = null if (elseMergedSchemaId) { elseMergedLocation = getMergedLocation(context, elseMergedSchemaId) } else { elseMergedSchemaId = `__fjs_merged_${schemaIdCounter++}` - context.mergedSchemasIds.set(elseSchema, elseMergedSchemaId) + context.mergedSchemasIds.set(getMergedSchemasIdsKey(elseSchema, 'Else'), elseMergedSchemaId) elseMergedLocation = mergeLocations(context, elseMergedSchemaId, [ rootLocation, diff --git a/package.json b/package.json index 5c54b545..5c562440 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "ajv-formats": "^3.0.1", "fast-uri": "^3.0.0", "json-schema-ref-resolver": "^1.0.1", + "object-hash": "^3.0.0", "rfdc": "^1.2.0" }, "standard": { diff --git a/test/ref.test.js b/test/ref.test.js index 4163ad24..5f1e96f3 100644 --- a/test/ref.test.js +++ b/test/ref.test.js @@ -2128,3 +2128,40 @@ test('ref internal - throw if schema has definition twice with different shape', t.equal(err.message, 'There is already another anchor "#uri" in a schema "test".') } }) + +test('2 anyOf self ref', (t) => { + t.plan(1) + + const schema = { + type: 'object', + properties: { + field: { type: 'string' }, + field1: { + anyOf: [ + { $ref: '#' }, + { type: 'null' } + ] + }, + field2: { + anyOf: [ + { $ref: '#' }, + { type: 'null' } + ] + } + } + } + + const object = { + field: 'a', + field1: { + field: 'b', + field1: null, + field2: null + }, + field2: null + } + const stringify = build(schema) + const output = stringify(object) + + t.equal(output, JSON.stringify(object)) +})