Skip to content

Commit 074a120

Browse files
fix: generated function names collision (#438)
1 parent e37db95 commit 074a120

File tree

2 files changed

+74
-50
lines changed

2 files changed

+74
-50
lines changed

index.js

Lines changed: 43 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,11 @@ function build (schema, options) {
297297
switch (schema.type) {
298298
case 'object':
299299
main = '$main'
300-
code = buildObject(location, code, main)
300+
code = buildObject(location, code, main, main)
301301
break
302302
case 'array':
303303
main = '$main'
304-
code = buildArray(location, code, main)
304+
code = buildArray(location, code, main, main)
305305
schema = location.schema
306306
break
307307
case 'string':
@@ -449,13 +449,13 @@ function addPatternProperties (location) {
449449
const ifPpKeyExists = `if (/${regex.replace(/\\*\//g, '\\/')}/.test(keys[i])) {`
450450

451451
if (type === 'object') {
452-
code += `${buildObject(ppLocation, '', 'buildObjectPP' + index)}
452+
code += `${buildObject(ppLocation, '', 'buildObjectPP' + index, 'buildObjectPP' + index)}
453453
${ifPpKeyExists}
454454
${addComma}
455455
json += serializer.asString(keys[i]) + ':' + buildObjectPP${index}(obj[keys[i]])
456456
`
457457
} else if (type === 'array') {
458-
code += `${buildArray(ppLocation, '', 'buildArrayPP' + index)}
458+
code += `${buildArray(ppLocation, '', 'buildArrayPP' + index, 'buildArrayPP' + index)}
459459
${ifPpKeyExists}
460460
${addComma}
461461
json += serializer.asString(keys[i]) + ':' + buildArrayPP${index}(obj[keys[i]])
@@ -539,12 +539,12 @@ function additionalProperty (location) {
539539
const format = ap.format
540540
const stringSerializer = getStringSerializer(format)
541541
if (type === 'object') {
542-
code += `${buildObject(apLocation, '', 'buildObjectAP')}
542+
code += `${buildObject(apLocation, '', 'buildObjectAP', 'buildObjectAP')}
543543
${addComma}
544544
json += serializer.asString(keys[i]) + ':' + buildObjectAP(obj[keys[i]])
545545
`
546546
} else if (type === 'array') {
547-
code += `${buildArray(apLocation, '', 'buildArrayAP')}
547+
code += `${buildArray(apLocation, '', 'buildArrayAP', 'buildArrayAP')}
548548
${addComma}
549549
json += serializer.asString(keys[i]) + ':' + buildArrayAP(obj[keys[i]])
550550
`
@@ -734,7 +734,7 @@ function refFinder (ref, location) {
734734
}
735735
}
736736

737-
function buildCode (location, code, laterCode, name) {
737+
function buildCode (location, code, laterCode, locationPath) {
738738
if (location.schema.$ref) {
739739
location = refFinder(location.schema.$ref, location)
740740
}
@@ -761,7 +761,7 @@ function buildCode (location, code, laterCode, name) {
761761
json += ${asString} + ':'
762762
`
763763

764-
const result = nested(laterCode, name, key, mergeLocation(propertyLocation, { schema: schema.properties[key] }), undefined, false)
764+
const result = nested(laterCode, locationPath, key, mergeLocation(propertyLocation, { schema: schema.properties[key] }), undefined, false)
765765
code += result.code
766766
laterCode = result.laterCode
767767

@@ -790,23 +790,23 @@ function buildCode (location, code, laterCode, name) {
790790
}
791791

792792
if (schema.allOf) {
793-
const builtCode = buildCodeWithAllOfs(location, code, laterCode, name)
793+
const builtCode = buildCodeWithAllOfs(location, code, laterCode, locationPath)
794794
code = builtCode.code
795795
laterCode = builtCode.laterCode
796796
}
797797

798798
return { code, laterCode }
799799
}
800800

801-
function buildCodeWithAllOfs (location, code, laterCode, name) {
801+
function buildCodeWithAllOfs (location, code, laterCode, locationPath) {
802802
if (location.schema.allOf) {
803803
location.schema.allOf.forEach((ss) => {
804-
const builtCode = buildCodeWithAllOfs(mergeLocation(location, { schema: ss }), code, laterCode, name)
804+
const builtCode = buildCodeWithAllOfs(mergeLocation(location, { schema: ss }), code, laterCode, locationPath)
805805
code = builtCode.code
806806
laterCode = builtCode.laterCode
807807
})
808808
} else {
809-
const builtCode = buildCode(location, code, laterCode, name)
809+
const builtCode = buildCode(location, code, laterCode, locationPath)
810810

811811
code = builtCode.code
812812
laterCode = builtCode.laterCode
@@ -815,9 +815,9 @@ function buildCodeWithAllOfs (location, code, laterCode, name) {
815815
return { code, laterCode }
816816
}
817817

818-
function buildInnerObject (location, name) {
818+
function buildInnerObject (location, locationPath) {
819819
const schema = location.schema
820-
const result = buildCodeWithAllOfs(location, '', '', name)
820+
const result = buildCodeWithAllOfs(location, '', '', locationPath)
821821
if (schema.patternProperties) {
822822
result.code += addPatternProperties(location)
823823
} else if (schema.additionalProperties && !schema.patternProperties) {
@@ -826,7 +826,7 @@ function buildInnerObject (location, name) {
826826
return result
827827
}
828828

829-
function addIfThenElse (location, name) {
829+
function addIfThenElse (location, locationPath) {
830830
let code = ''
831831
let r
832832
let laterCode = ''
@@ -851,12 +851,12 @@ function addIfThenElse (location, name) {
851851
if (valid) {
852852
`
853853
if (merged.if && merged.then) {
854-
innerR = addIfThenElse(mergedLocation, name + 'Then')
854+
innerR = addIfThenElse(mergedLocation, locationPath + 'Then')
855855
code += innerR.code
856856
laterCode = innerR.laterCode
857857
}
858858

859-
r = buildInnerObject(mergedLocation, name + 'Then')
859+
r = buildInnerObject(mergedLocation, locationPath + 'Then')
860860
code += r.code
861861
laterCode += r.laterCode
862862

@@ -871,12 +871,12 @@ function addIfThenElse (location, name) {
871871
`
872872

873873
if (merged.if && merged.then) {
874-
innerR = addIfThenElse(mergedLocation, name + 'Else')
874+
innerR = addIfThenElse(mergedLocation, locationPath + 'Else')
875875
code += innerR.code
876876
laterCode += innerR.laterCode
877877
}
878878

879-
r = buildInnerObject(mergedLocation, name + 'Else')
879+
r = buildInnerObject(mergedLocation, locationPath + 'Else')
880880
code += r.code
881881
laterCode += r.laterCode
882882

@@ -893,13 +893,14 @@ function toJSON (variableName) {
893893
`
894894
}
895895

896-
function buildObject (location, code, name) {
896+
function buildObject (location, code, functionName, locationPath) {
897897
const schema = location.schema
898898
if (schema.$id !== undefined) {
899899
schemaReferenceMap.set(schema.$id, schema)
900900
}
901901
code += `
902-
function ${name} (input) {
902+
function ${functionName} (input) {
903+
// ${locationPath}
903904
`
904905
if (schema.nullable) {
905906
code += `
@@ -909,14 +910,14 @@ function buildObject (location, code, name) {
909910
`
910911
}
911912

912-
if (objectReferenceSerializersMap.has(schema) && objectReferenceSerializersMap.get(schema) !== name) {
913+
if (objectReferenceSerializersMap.has(schema) && objectReferenceSerializersMap.get(schema) !== functionName) {
913914
code += `
914915
return ${objectReferenceSerializersMap.get(schema)}(input)
915916
}
916917
`
917918
return code
918919
}
919-
objectReferenceSerializersMap.set(schema, name)
920+
objectReferenceSerializersMap.set(schema, functionName)
920921

921922
code += `
922923
var obj = ${toJSON('input')}
@@ -929,9 +930,9 @@ function buildObject (location, code, name) {
929930
code += `
930931
var valid
931932
`
932-
r = addIfThenElse(location, name)
933+
r = addIfThenElse(location, locationPath)
933934
} else {
934-
r = buildInnerObject(location, name)
935+
r = buildInnerObject(location, locationPath)
935936
}
936937

937938
// Removes the comma if is the last element of the string (in case there are not properties)
@@ -945,13 +946,14 @@ function buildObject (location, code, name) {
945946
return code
946947
}
947948

948-
function buildArray (location, code, name, key = null) {
949+
function buildArray (location, code, functionName, locationPath, key = null) {
949950
let schema = location.schema
950951
if (schema.$id !== undefined) {
951952
schemaReferenceMap.set(schema.$id, schema)
952953
}
953954
code += `
954-
function ${name} (obj) {
955+
function ${functionName} (obj) {
956+
// ${locationPath}
955957
`
956958
if (schema.nullable) {
957959
code += `
@@ -984,14 +986,14 @@ function buildArray (location, code, name, key = null) {
984986
`
985987
return code
986988
}
987-
arrayItemsReferenceSerializersMap.set(schema.items, name)
989+
arrayItemsReferenceSerializersMap.set(schema.items, functionName)
988990
}
989991

990992
let result = { code: '', laterCode: '' }
991993
const accessor = '[i]'
992994
if (Array.isArray(schema.items)) {
993995
result = schema.items.reduce((res, item, i) => {
994-
const tmpRes = nested(laterCode, name, accessor, mergeLocation(location, { schema: item }), i, true)
996+
const tmpRes = nested(laterCode, locationPath, accessor, mergeLocation(location, { schema: item }), i, true)
995997
const condition = `i === ${i} && ${buildArrayTypeCondition(item.type, accessor)}`
996998
return {
997999
code: `${res.code}
@@ -1004,7 +1006,7 @@ function buildArray (location, code, name, key = null) {
10041006
}, result)
10051007

10061008
if (schema.additionalItems) {
1007-
const tmpRes = nested(laterCode, name, accessor, mergeLocation(location, { schema: schema.items }), undefined, true)
1009+
const tmpRes = nested(laterCode, locationPath, accessor, mergeLocation(location, { schema: schema.items }), undefined, true)
10081010
result.code += `
10091011
else if (i >= ${schema.items.length}) {
10101012
${tmpRes.code}
@@ -1018,7 +1020,7 @@ function buildArray (location, code, name, key = null) {
10181020
}
10191021
`
10201022
} else {
1021-
result = nested(laterCode, name, accessor, mergeLocation(location, { schema: schema.items }), undefined, true)
1023+
result = nested(laterCode, locationPath, accessor, mergeLocation(location, { schema: schema.items }), undefined, true)
10221024
}
10231025

10241026
if (key) {
@@ -1120,21 +1122,12 @@ function dereferenceOfRefs (location, type) {
11201122
return locations
11211123
}
11221124

1123-
let strNameCounter = 0
1124-
function asFuncName (str) {
1125-
// only allow chars that can work
1126-
let rep = str.replace(/[^a-zA-Z0-9$_]/g, '')
1127-
1128-
if (rep.length === 0) {
1129-
return 'anan' + strNameCounter++
1130-
} else if (rep !== str) {
1131-
rep += strNameCounter++
1132-
}
1133-
1134-
return rep
1125+
let genFuncNameCounter = 0
1126+
function generateFuncName () {
1127+
return 'anonymous' + genFuncNameCounter++
11351128
}
11361129

1137-
function nested (laterCode, name, key, location, subKey, isArray) {
1130+
function nested (laterCode, locationPath, key, location, subKey, isArray) {
11381131
subKey = subKey || ''
11391132

11401133
let schema = location.schema
@@ -1182,21 +1175,21 @@ function nested (laterCode, name, key, location, subKey, isArray) {
11821175
code += `json += ${funcName}(obj${accessor})`
11831176
break
11841177
case 'object':
1185-
funcName = asFuncName(name + key + subKey)
1186-
laterCode = buildObject(location, laterCode, funcName)
1178+
funcName = generateFuncName()
1179+
laterCode = buildObject(location, laterCode, funcName, locationPath + key + subKey)
11871180
code += `json += ${funcName}(obj${accessor})`
11881181
break
11891182
case 'array':
1190-
funcName = asFuncName('$arr' + name + key + subKey) // eslint-disable-line
1191-
laterCode = buildArray(location, laterCode, funcName, key)
1183+
funcName = generateFuncName()
1184+
laterCode = buildArray(location, laterCode, funcName, locationPath + key + subKey, key)
11921185
code += `json += ${funcName}(obj${accessor})`
11931186
break
11941187
case undefined:
11951188
if (schema.anyOf || schema.oneOf) {
11961189
// beware: dereferenceOfRefs has side effects and changes schema.anyOf
11971190
const locations = dereferenceOfRefs(location, schema.anyOf ? 'anyOf' : 'oneOf')
11981191
locations.forEach((location, index) => {
1199-
const nestedResult = nested(laterCode, name, key, location, subKey !== '' ? subKey : 'i' + index, isArray)
1192+
const nestedResult = nested(laterCode, locationPath, key, location, subKey !== '' ? subKey : 'i' + index, isArray)
12001193
// We need a test serializer as the String serializer will not work with
12011194
// date/time ajv validations
12021195
// see: https://github.com/fastify/fast-json-stringify/issues/325
@@ -1252,7 +1245,7 @@ function nested (laterCode, name, key, location, subKey, isArray) {
12521245
sortedTypes.forEach((type, index) => {
12531246
const statement = index === 0 ? 'if' : 'else if'
12541247
const tempSchema = Object.assign({}, schema, { type })
1255-
const nestedResult = nested(laterCode, name, key, mergeLocation(location, { schema: tempSchema }), subKey, isArray)
1248+
const nestedResult = nested(laterCode, locationPath, key, mergeLocation(location, { schema: tempSchema }), subKey, isArray)
12561249
switch (type) {
12571250
case 'string': {
12581251
code += `

test/nestedObjects.test.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,34 @@ test('nested objects with same properties', (t) => {
3030
})
3131
t.equal(value, '{"stringProperty":"string1","objectProperty":{"stringProperty":"string2","numberProperty":42}}')
3232
})
33+
34+
test('names collision', (t) => {
35+
t.plan(1)
36+
37+
const schema = {
38+
title: 'nested objects with same properties',
39+
type: 'object',
40+
properties: {
41+
test: {
42+
type: 'object',
43+
properties: {
44+
a: { type: 'string' }
45+
}
46+
},
47+
tes: {
48+
type: 'object',
49+
properties: {
50+
b: { type: 'string' },
51+
t: { type: 'object' }
52+
}
53+
}
54+
}
55+
}
56+
const stringify = build(schema)
57+
const data = {
58+
test: { a: 'a' },
59+
tes: { b: 'b', t: {} }
60+
}
61+
62+
t.equal(stringify(data), JSON.stringify(data))
63+
})

0 commit comments

Comments
 (0)