Skip to content

Commit

Permalink
fix: standalone code generation creating duplicate functions (closes #…
Browse files Browse the repository at this point in the history
  • Loading branch information
epoberezkin committed Dec 19, 2020
1 parent eae2d5d commit 0b89a00
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 18 deletions.
2 changes: 1 addition & 1 deletion lib/compile/codegen/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {_, nil, _Code, Code, Name, UsedNames, CodeItem, addCodeArg, _CodeOrName}
import {Scope, varKinds} from "./scope"

export {_, str, strConcat, nil, getProperty, stringify, Name, Code} from "./code"
export {Scope, ScopeStore, ValueScope, ScopeValueSets, varKinds} from "./scope"
export {Scope, ScopeStore, ValueScope, ValueScopeName, ScopeValueSets, varKinds} from "./scope"

// type for expressions that can be safely inserted in code without quotes
export type SafeExpr = Code | number | boolean | null
Expand Down
4 changes: 2 additions & 2 deletions lib/compile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type {
} from "../types"
import type Ajv from "../core"
import type {InstanceOptions} from "../core"
import {CodeGen, _, nil, stringify, Name, Code} from "./codegen"
import {CodeGen, _, nil, stringify, Name, Code, ValueScopeName} from "./codegen"
import {ValidationError} from "./error_classes"
import N from "./names"
import {LocalRefs, getFullPath, _getFullPath, inlineRef, normalizeId, resolveUrl} from "./resolve"
Expand Down Expand Up @@ -77,7 +77,7 @@ export class SchemaEnv implements SchemaEnvArgs {
readonly refs: SchemaRefs = {}
readonly dynamicAnchors: {[Ref in string]?: true} = {}
validate?: AnyValidateFunction
validateName?: Name
validateName?: ValueScopeName

constructor(env: SchemaEnvArgs) {
let schema: AnySchemaObject | undefined
Expand Down
4 changes: 4 additions & 0 deletions lib/standalone/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export default function standaloneCode(

function validateCode(usedValues: ScopeValueSets, s?: SourceCode): Code {
if (!s) throw new Error('moduleCode: function does not have "source" property')
const {prefix} = s.validateName
const nameSet = (usedValues[prefix] = usedValues[prefix] || new Set())
nameSet.add(s.validateName)

const scopeCode = ajv.scope.scopeCode(s.scopeValues, usedValues, refValidateCode)
const code = new _Code(`${scopeCode}${_n}${s.validateCode}`)
return s.evaluated ? _`${code}${s.validateName}.evaluated = ${s.evaluated};${_n}` : code
Expand Down
4 changes: 2 additions & 2 deletions lib/types/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {CodeGen, Code, Name, ScopeValueSets} from "../compile/codegen"
import type {CodeGen, Code, Name, ScopeValueSets, ValueScopeName} from "../compile/codegen"
import type {SchemaEnv, SchemaCxt, SchemaObjCxt} from "../compile"
import type {JSONType} from "../compile/rules"
import type KeywordCxt from "../compile/context"
Expand Down Expand Up @@ -30,7 +30,7 @@ export type AnySchema = Schema | AsyncSchema
export type SchemaMap = {[Key in string]?: AnySchema}

export interface SourceCode {
validateName: Name
validateName: ValueScopeName
validateCode: string
scopeValues: ScopeValueSets
evaluated?: Code
Expand Down
44 changes: 31 additions & 13 deletions spec/standalone.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe("standalone code generation", () => {
}
})

describe.skip("two refs to the same schema (issue #1361)", () => {
describe("two refs to the same schema (issue #1361)", () => {
const userSchema = {
$id: "user.json",
type: "object",
Expand Down Expand Up @@ -119,19 +119,23 @@ describe("standalone code generation", () => {

const moduleCode = standaloneCode(ajv)
assertNoDuplicateFunctions(moduleCode)
const {"user.json": user, "info.json": info} = requireFromString(moduleCode)
testExports({user, info})
})
})

const {"user.json": validateUser, "info.json": validateInfo} = requireFromString(moduleCode)
assert.strictEqual(validateUser({}), false)
assert.strictEqual(validateUser({name: "usr1"}), true)

assert.strictEqual(validateInfo({}), false)
assert.strictEqual(
validateInfo({
author: {name: "usr1"},
contributors: [{name: "usr2"}],
}),
true
)
describe("named exports", () => {
it("should not have duplicate functions", () => {
const ajv = new _Ajv({
allErrors: true,
code: {optimize: false, source: true},
inlineRefs: false, // it is needed to show the issue, schemas with refs won't be inlined anyway
schemas: [userSchema, infoSchema],
})

const moduleCode = standaloneCode(ajv, {user: "user.json", info: "info.json"})
assertNoDuplicateFunctions(moduleCode)
testExports(requireFromString(moduleCode))
})
})

Expand All @@ -141,6 +145,20 @@ describe("standalone code generation", () => {
assert(funcs.length > 0)
assert.strictEqual(funcs.length, new Set(funcs).size, "should have no duplicates")
}

function testExports(validate: {[n: string]: AnyValidateFunction<unknown>}): void {
assert.strictEqual(validate.user({}), false)
assert.strictEqual(validate.user({name: "usr1"}), true)

assert.strictEqual(validate.info({}), false)
assert.strictEqual(
validate.info({
author: {name: "usr1"},
contributors: [{name: "usr2"}],
}),
true
)
}
})

it("should generate module code with a single export (ESM compatible)", () => {
Expand Down

0 comments on commit 0b89a00

Please sign in to comment.