diff --git a/index.js b/index.js index 14e65982..1806526c 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ 'use strict' var Ajv = require('ajv') +var merge = require('deepmerge') var uglify = null var isLong @@ -86,7 +87,7 @@ function build (schema, options) { var dependencies = [] var dependenciesName = [] - if (hasAnyOf(schema) || hasArrayOfTypes(schema)) { + if (hasAnyOf(schema) || hasArrayOfTypes(schema) || hasIf(schema)) { dependencies.push(new Ajv()) dependenciesName.push('ajv') } @@ -143,6 +144,11 @@ function hasArrayOfTypes (schema) { return false } +function hasIf (schema) { + const str = JSON.stringify(schema) + return /"if":{/.test(str) && /"then":{/.test(str) +} + function $asNull () { return 'null' } @@ -475,21 +481,15 @@ function buildCode (schema, code, laterCode, name, externalSchema, fullSchema) { return { code: code, laterCode: laterCode } } -function buildObject (schema, code, name, externalSchema, fullSchema) { - code += ` - function ${name} (obj) { - var json = '{' - var addComma = false - ` - +function buildInnerObject (schema, name, externalSchema, fullSchema) { + var laterCode = '' + var code = '' if (schema.patternProperties) { code += addPatternProperties(schema, externalSchema, fullSchema) } else if (schema.additionalProperties && !schema.patternProperties) { code += addAdditionalProperties(schema, externalSchema, fullSchema) } - var laterCode = '' - if (schema.allOf) { schema.allOf.forEach((ss) => { var builtCode = buildCode(ss, code, laterCode, name, externalSchema, fullSchema) @@ -504,6 +504,60 @@ function buildObject (schema, code, name, externalSchema, fullSchema) { laterCode = builtCode.laterCode } + return { code: code, laterCode: laterCode } +} + +function buildObject (schema, code, name, externalSchema, fullSchema) { + code += ` + function ${name} (obj) { + var json = '{' + var addComma = false + ` + + var laterCode = '' + var r, merged + if (schema.if && schema.then) { + merged = merge(schema, schema.then) + delete merged.if + delete merged.then + delete merged.else + + code += ` + var valid = ajv.validate(${require('util').inspect(schema.if, {depth: null})}, obj) + if (valid) { + ` + + r = buildInnerObject(merged, name, externalSchema, fullSchema) + code += r.code + laterCode = r.laterCode + + code += ` + } + ` + if (schema.else) { + merged = merge(schema, schema.else) + delete merged.if + delete merged.then + delete merged.else + + code += ` + else { + ` + + r = buildInnerObject(merged, name, externalSchema, fullSchema) + code += r.code + laterCode = r.laterCode + + code += ` + } + ` + } + } else { + r = buildInnerObject(schema, name, externalSchema, fullSchema) + code += r.code + laterCode = r.laterCode + } + // Removes the comma if is the last element of the string (in case there are not properties) code += ` json += '}' diff --git a/package.json b/package.json index d25695e6..be8f5f71 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "uglify-es": "^3.3.10" }, "dependencies": { - "ajv": "^6.4.0" + "ajv": "^6.4.0", + "deepmerge": "^2.1.0" } } diff --git a/test/if-then-else.test.js b/test/if-then-else.test.js new file mode 100644 index 00000000..9ebbabd2 --- /dev/null +++ b/test/if-then-else.test.js @@ -0,0 +1,85 @@ +'use strict' + +const t = require('tap') +const build = require('..') + +const schema = { + 'type': 'object', + 'properties': { + 'kind': { 'type': 'string', 'enum': ['foobar', 'greeting'] } + }, + 'if': { + 'properties': { + 'kind': { 'type': 'string', 'enum': ['foobar'] } + } + }, + 'then': { + 'properties': { + 'foo': { 'type': 'string' }, + 'bar': { 'type': 'number' } + } + }, + 'else': { + 'properties': { + 'hi': { 'type': 'string' }, + 'hello': { 'type': 'number' } + } + } +} + +t.test('if-then-else', t => { + const tests = [ + { + name: 'foobar', + schema: schema, + input: { + kind: 'foobar', + foo: 'FOO', + bar: 42, + hi: 'HI', + hello: 'HELLO' + }, + expected: JSON.stringify({ + kind: 'foobar', + foo: 'FOO', + bar: 42 + }) + }, + { + name: 'greeting', + schema: schema, + input: { + kind: 'greeting', + foo: 'FOO', + bar: 42, + hi: 'HI', + hello: 45 + }, + expected: JSON.stringify({ + kind: 'greeting', + hi: 'HI', + hello: 45 + }) + } + ] + + tests.forEach(test => { + t.test(test.name + ' - normal', t => { + t.plan(1) + + const stringify = build(test.schema) + const serialized = stringify(test.input) + t.equal(serialized, test.expected) + }) + + t.test(test.name + ' - uglify', t => { + t.plan(1) + + const stringify = build(test.schema, { uglify: true }) + const serialized = stringify(test.input) + t.equal(serialized, test.expected) + }) + }) + + t.end() +})