From 59a2f84541f8fc3f9d85fed2f7d3192a9056f69f Mon Sep 17 00:00:00 2001 From: L2jLiga Date: Mon, 18 Oct 2021 18:15:31 +0200 Subject: [PATCH 1/4] support for recursion in arrays --- index.js | 12 ++++++++++++ test/recursion.test.js | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 test/recursion.test.js diff --git a/index.js b/index.js index 34d44cda..28f5a9e5 100644 --- a/index.js +++ b/index.js @@ -944,6 +944,8 @@ function buildObject (location, code, name) { return code } +const functionsWithReferenceCode = new Map() + function buildArray (location, code, name, key = null) { let schema = location.schema code += ` @@ -969,6 +971,16 @@ function buildArray (location, code, name, key = null) { schema = location.schema schema[fjsCloned] = true } + + if (functionsWithReferenceCode.has(schema.items.$ref)) { + code += ` + return ${functionsWithReferenceCode.get(schema.items.$ref)}(obj) + } + ` + return code + } + functionsWithReferenceCode.set(schema.items.$ref, name) + location = refFinder(schema.items.$ref, location) schema.items = location.schema } diff --git a/test/recursion.test.js b/test/recursion.test.js new file mode 100644 index 00000000..3a9fb17a --- /dev/null +++ b/test/recursion.test.js @@ -0,0 +1,38 @@ +'use strict' + +const test = require('tap').test +const build = require('..') + +test('can stringify recursive directory tree (issue #181)', (t) => { + t.plan(1) + + const schema = { + definitions: { + directory: { + type: 'object', + properties: { + name: { type: 'string' }, + subDirectories: { + type: 'array', + items: { $ref: '#/definitions/directory' }, + default: [] + } + } + } + }, + type: 'array', + items: { $ref: '#/definitions/directory' } + } + const stringify = build(schema) + + t.equal(stringify([ + { name: 'directory 1', subDirectories: [] }, + { + name: 'directory 2', + subDirectories: [ + { name: 'directory 2.1', subDirectories: [] }, + { name: 'directory 2.2', subDirectories: [] } + ] + } + ]), '[{"name":"directory 1","subDirectories":[]},{"name":"directory 2","subDirectories":[{"name":"directory 2.1","subDirectories":[]},{"name":"directory 2.2","subDirectories":[]}]}]') +}) From 7af7065577882deebe2fcbd64a3c49df89493206 Mon Sep 17 00:00:00 2001 From: L2jLiga Date: Mon, 18 Oct 2021 19:09:17 +0200 Subject: [PATCH 2/4] clear reference serializer map upon build --- index.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 28f5a9e5..3662ae58 100644 --- a/index.js +++ b/index.js @@ -47,7 +47,10 @@ function mergeLocation (source, dest) { } } +const referenceSerializersMap = new Map() + function build (schema, options) { + referenceSerializersMap.clear() options = options || {} isValidSchema(schema) if (options.schema) { @@ -944,8 +947,6 @@ function buildObject (location, code, name) { return code } -const functionsWithReferenceCode = new Map() - function buildArray (location, code, name, key = null) { let schema = location.schema code += ` @@ -972,14 +973,14 @@ function buildArray (location, code, name, key = null) { schema[fjsCloned] = true } - if (functionsWithReferenceCode.has(schema.items.$ref)) { + if (referenceSerializersMap.has(schema.items.$ref)) { code += ` - return ${functionsWithReferenceCode.get(schema.items.$ref)}(obj) + return ${referenceSerializersMap.get(schema.items.$ref)}(obj) } ` return code } - functionsWithReferenceCode.set(schema.items.$ref, name) + referenceSerializersMap.set(schema.items.$ref, name) location = refFinder(schema.items.$ref, location) schema.items = location.schema From e3cf0406ea0b9bb6d2c1c09df0a008a008e602ac Mon Sep 17 00:00:00 2001 From: L2jLiga Date: Mon, 18 Oct 2021 20:17:40 +0200 Subject: [PATCH 3/4] handle case when external definitions use ref to themselves --- index.js | 14 ++++---- test/recursion.test.js | 81 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 6 deletions(-) diff --git a/index.js b/index.js index 3662ae58..9a0ba030 100644 --- a/index.js +++ b/index.js @@ -147,6 +147,8 @@ function build (schema, options) { return dependenciesName } + referenceSerializersMap.clear() + return (Function.apply(null, dependenciesName).apply(null, dependencies)) } @@ -973,17 +975,17 @@ function buildArray (location, code, name, key = null) { schema[fjsCloned] = true } - if (referenceSerializersMap.has(schema.items.$ref)) { + location = refFinder(schema.items.$ref, location) + schema.items = location.schema + + if (referenceSerializersMap.has(schema.items)) { code += ` - return ${referenceSerializersMap.get(schema.items.$ref)}(obj) + return ${referenceSerializersMap.get(schema.items)}(obj) } ` return code } - referenceSerializersMap.set(schema.items.$ref, name) - - location = refFinder(schema.items.$ref, location) - schema.items = location.schema + referenceSerializersMap.set(schema.items, name) } let result = { code: '', laterCode: '' } diff --git a/test/recursion.test.js b/test/recursion.test.js index 3a9fb17a..dfc95387 100644 --- a/test/recursion.test.js +++ b/test/recursion.test.js @@ -36,3 +36,84 @@ test('can stringify recursive directory tree (issue #181)', (t) => { } ]), '[{"name":"directory 1","subDirectories":[]},{"name":"directory 2","subDirectories":[{"name":"directory 2.1","subDirectories":[]},{"name":"directory 2.2","subDirectories":[]}]}]') }) + +test('can stringify when recursion in external schema', t => { + t.plan(1) + + const referenceSchema = { + $id: 'person', + type: 'object', + properties: { + name: { type: 'string' }, + children: { + type: 'array', + items: { $ref: '#' } + } + } + } + + const schema = { + $id: 'mainSchema', + type: 'object', + properties: { + people: { + $ref: 'person' + } + } + } + const stringify = build(schema, { + schema: { + [referenceSchema.$id]: referenceSchema + } + }) + + const value = stringify({ people: { name: 'Elizabeth', children: [{ name: 'Charles' }] } }) + t.equal(value, '{"people":{"name":"Elizabeth","children":[{"name":"Charles"}]}}') +}) + +test('use proper serialize function', t => { + t.plan(1) + + const personSchema = { + $id: 'person', + type: 'object', + properties: { + name: { type: 'string' }, + children: { + type: 'array', + items: { $ref: '#' } + } + } + } + + const directorySchema = { + $id: 'directory', + type: 'object', + properties: { + name: { type: 'string' }, + subDirectories: { + type: 'array', + items: { $ref: '#' }, + default: [] + } + } + } + + const schema = { + $id: 'mainSchema', + type: 'object', + properties: { + people: { $ref: 'person' }, + directory: { $ref: 'directory' } + } + } + const stringify = build(schema, { + schema: { + [personSchema.$id]: personSchema, + [directorySchema.$id]: directorySchema + } + }) + + const value = stringify({ people: { name: 'Elizabeth', children: [{ name: 'Charles' }] }, directory: { name: 'directory 1', subDirectories: [] } }) + t.equal(value, '{"people":{"name":"Elizabeth","children":[{"name":"Charles"}]},"directory":{"name":"directory 1","subDirectories":[]}}') +}) From b2251b1f382bcc09b88b712b837cc17989f645d8 Mon Sep 17 00:00:00 2001 From: L2jLiga Date: Tue, 19 Oct 2021 17:46:24 +0200 Subject: [PATCH 4/4] expanded test case --- test/recursion.test.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/recursion.test.js b/test/recursion.test.js index dfc95387..f89dd365 100644 --- a/test/recursion.test.js +++ b/test/recursion.test.js @@ -114,6 +114,24 @@ test('use proper serialize function', t => { } }) - const value = stringify({ people: { name: 'Elizabeth', children: [{ name: 'Charles' }] }, directory: { name: 'directory 1', subDirectories: [] } }) - t.equal(value, '{"people":{"name":"Elizabeth","children":[{"name":"Charles"}]},"directory":{"name":"directory 1","subDirectories":[]}}') + const value = stringify({ + people: { + name: 'Elizabeth', + children: [{ + name: 'Charles', + children: [{ name: 'William', children: [{ name: 'George' }, { name: 'Charlotte' }] }, { name: 'Harry' }] + }] + }, + directory: { + name: 'directory 1', + subDirectories: [ + { name: 'directory 1.1', subDirectories: [] }, + { + name: 'directory 1.2', + subDirectories: [{ name: 'directory 1.2.1' }, { name: 'directory 1.2.2' }] + } + ] + } + }) + t.equal(value, '{"people":{"name":"Elizabeth","children":[{"name":"Charles","children":[{"name":"William","children":[{"name":"George"},{"name":"Charlotte"}]},{"name":"Harry"}]}]},"directory":{"name":"directory 1","subDirectories":[{"name":"directory 1.1","subDirectories":[]},{"name":"directory 1.2","subDirectories":[{"name":"directory 1.2.1","subDirectories":[]},{"name":"directory 1.2.2","subDirectories":[]}]}]}}') })