Skip to content

Commit c7f5761

Browse files
committed
refactor: performance improvements
1 parent b424f89 commit c7f5761

28 files changed

+300
-231
lines changed

benchmarks/object_with_arrays.ts

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import { z } from 'zod'
2+
// @ts-ignore
3+
import Benchmark from 'benchmark'
4+
import Ajv, { AsyncValidateFunction } from 'ajv'
5+
import { Compiler } from '../src/compiler/main.js'
6+
import { ErrorReporterFactory } from '../factories/error_reporter.js'
7+
8+
const suite = new Benchmark.Suite()
9+
10+
const data = { contacts: [{ email: 'foo@bar.com' }] }
11+
const meta = {}
12+
const refs = {
13+
'ref://1': {
14+
validator(value: unknown, _: any, ctx: any) {
15+
if (typeof value !== 'string') {
16+
ctx.report('Value is not a string', ctx)
17+
}
18+
},
19+
options: {},
20+
},
21+
}
22+
const errorReporter = new ErrorReporterFactory().create()
23+
24+
const zodSchema = z.object({
25+
contacts: z.array(
26+
z.object({
27+
email: z.string(),
28+
})
29+
),
30+
})
31+
32+
const compiler = new Compiler({
33+
type: 'root',
34+
schema: {
35+
type: 'object',
36+
allowNull: false,
37+
isOptional: false,
38+
fieldName: '*',
39+
allowUnknownProperties: false,
40+
bail: true,
41+
properties: [
42+
{
43+
type: 'array',
44+
fieldName: 'contacts',
45+
propertyName: 'contacts',
46+
allowNull: false,
47+
bail: true,
48+
isOptional: false,
49+
each: {
50+
type: 'object',
51+
propertyName: '*',
52+
allowNull: false,
53+
allowUnknownProperties: false,
54+
isOptional: false,
55+
fieldName: '*',
56+
bail: true,
57+
groups: [],
58+
properties: [
59+
{
60+
type: 'literal',
61+
bail: true,
62+
allowNull: false,
63+
fieldName: 'email',
64+
propertyName: 'email',
65+
isOptional: false,
66+
validations: [
67+
{
68+
isAsync: false,
69+
ruleFnId: 'ref://1',
70+
implicit: false,
71+
},
72+
],
73+
},
74+
],
75+
validations: [],
76+
},
77+
validations: [],
78+
},
79+
],
80+
groups: [],
81+
propertyName: '*',
82+
validations: [],
83+
},
84+
})
85+
86+
compiler.compile()
87+
const fn = compiler.compile()
88+
89+
const ajv = new Ajv.default({ removeAdditional: true })
90+
const ajvSchema = {
91+
$async: true,
92+
type: 'object',
93+
properties: {
94+
contacts: {
95+
type: 'array',
96+
items: {
97+
type: 'object',
98+
properties: {
99+
email: { type: 'string' },
100+
},
101+
required: ['email'],
102+
},
103+
},
104+
},
105+
required: ['contacts'],
106+
}
107+
108+
const ajvValidate = ajv.compile(ajvSchema) as AsyncValidateFunction<any>
109+
110+
suite
111+
.add('VineJS compiler', {
112+
defer: true,
113+
114+
// benchmark test function
115+
fn: function (deferred: any) {
116+
fn(data, meta, refs, errorReporter).then(() => deferred.resolve())
117+
},
118+
})
119+
.add('Ajv', {
120+
defer: true,
121+
122+
// benchmark test function
123+
fn: function (deferred: any) {
124+
ajvValidate(data).then(() => deferred.resolve())
125+
},
126+
})
127+
.add('Zod', {
128+
defer: true,
129+
fn(deferred: any) {
130+
zodSchema.parseAsync(data).then(() => deferred.resolve())
131+
},
132+
})
133+
.on('cycle', function (event: any) {
134+
console.log(String(event.target))
135+
})
136+
.on('complete', function (this: any) {
137+
console.log('Fastest is ' + this.filter('fastest').map('name'))
138+
})
139+
.run({ async: true })

src/compiler/fields/array_field.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,24 @@
1010
import type { CompilerField, CompilerParent } from '../../types.js'
1111

1212
export function createArrayField(parent: CompilerParent): CompilerField {
13-
const fieldPathExpression =
14-
parent.fieldPathExpression !== `''`
15-
? `${parent.fieldPathExpression} + '.' + ${parent.variableName}_i`
16-
: `${parent.variableName}_i`
13+
/**
14+
* Commented to see if a use case arrives for using this.
15+
*/
16+
// const fieldPathExpression =
17+
// parent.fieldPathExpression !== `''`
18+
// ? `${parent.fieldPathExpression} + '.' + ${parent.variableName}_i`
19+
// : `${parent.variableName}_i`
1720

1821
const wildCardPath = parent.wildCardPath !== '' ? `${parent.wildCardPath}.*` : `*`
1922

2023
return {
2124
parentValueExpression: `${parent.variableName}.value`,
2225
fieldNameExpression: `${parent.variableName}_i`,
23-
fieldPathExpression: fieldPathExpression,
26+
fieldPathExpression: wildCardPath,
2427
wildCardPath: wildCardPath,
2528
variableName: `${parent.variableName}_item`,
2629
valueExpression: `${parent.variableName}.value[${parent.variableName}_i]`,
27-
outputExpression: `${parent.outputExpression}[${parent.variableName}_i]`,
30+
outputExpression: `${parent.variableName}_out[${parent.variableName}_i]`,
2831
isArrayMember: true,
2932
}
3033
}

src/compiler/fields/object_field.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,25 @@ export function createObjectField(
1414
variablesCounter: number,
1515
parent: CompilerParent
1616
): CompilerField {
17-
const fieldPathExpression =
18-
parent.fieldPathExpression !== `''`
19-
? `${parent.fieldPathExpression} + '.' + '${node.fieldName}'`
20-
: `'${node.fieldName}'`
17+
/**
18+
* Commented to see if a use case arrives for using this.
19+
*/
20+
// const fieldPathExpression =
21+
// parent.fieldPathExpression !== `''`
22+
// ? `${parent.fieldPathExpression} + '.' + '${node.fieldName}'`
23+
// : `'${node.fieldName}'`
2124

2225
const wildCardPath =
2326
parent.wildCardPath !== '' ? `${parent.wildCardPath}.${node.fieldName}` : node.fieldName
2427

2528
return {
2629
parentValueExpression: `${parent.variableName}.value`,
2730
fieldNameExpression: `'${node.fieldName}'`,
28-
fieldPathExpression: fieldPathExpression,
31+
fieldPathExpression: wildCardPath,
2932
wildCardPath: wildCardPath,
3033
variableName: `${node.propertyName}_${variablesCounter}`,
3134
valueExpression: `${parent.variableName}.value['${node.fieldName}']`,
32-
outputExpression: `${parent.outputExpression}['${node.propertyName}']`,
35+
outputExpression: `${parent.variableName}_out['${node.propertyName}']`,
3336
isArrayMember: false,
3437
}
3538
}

src/compiler/fields/record_field.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,24 @@
1010
import type { CompilerField, CompilerParent } from '../../types.js'
1111

1212
export function createRecordField(parent: CompilerParent): CompilerField {
13-
const fieldPathExpression =
14-
parent.fieldPathExpression !== `''`
15-
? `${parent.fieldPathExpression} + '.' + ${parent.variableName}_i`
16-
: `${parent.variableName}_i`
13+
/**
14+
* Commented to see if a use case arrives for using this.
15+
*/
16+
// const fieldPathExpression =
17+
// parent.fieldPathExpression !== `''`
18+
// ? `${parent.fieldPathExpression} + '.' + ${parent.variableName}_i`
19+
// : `${parent.variableName}_i`
1720

1821
const wildCardPath = parent.wildCardPath !== '' ? `${parent.wildCardPath}.*` : `*`
1922

2023
return {
2124
parentValueExpression: `${parent.variableName}.value`,
2225
fieldNameExpression: `${parent.variableName}_i`,
23-
fieldPathExpression: fieldPathExpression,
26+
fieldPathExpression: wildCardPath,
2427
wildCardPath: wildCardPath,
2528
variableName: `${parent.variableName}_item`,
2629
valueExpression: `${parent.variableName}.value[${parent.variableName}_i]`,
27-
outputExpression: `${parent.outputExpression}[${parent.variableName}_i]`,
30+
outputExpression: `${parent.variableName}_out[${parent.variableName}_i]`,
2831
isArrayMember: false,
2932
}
3033
}

src/compiler/fields/tuple_field.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,25 @@ export function createTupleField(
1313
node: Pick<FieldNode, 'fieldName' | 'propertyName'>,
1414
parent: CompilerParent
1515
): CompilerField {
16-
const fieldPathExpression =
17-
parent.fieldPathExpression !== `''`
18-
? `${parent.fieldPathExpression} + '.' + '${node.fieldName}'`
19-
: `'${node.fieldName}'`
16+
/**
17+
* Commented to see if a use case arrives for using this.
18+
*/
19+
// const fieldPathExpression =
20+
// parent.fieldPathExpression !== `''`
21+
// ? `${parent.fieldPathExpression} + '.' + '${node.fieldName}'`
22+
// : `'${node.fieldName}'`
2023

21-
const wildCardPath = parent.wildCardPath !== '' ? `${parent.wildCardPath}.*` : `*`
24+
const wildCardPath =
25+
parent.wildCardPath !== '' ? `${parent.wildCardPath}.${node.fieldName}` : node.fieldName
2226

2327
return {
2428
parentValueExpression: `${parent.variableName}.value`,
2529
fieldNameExpression: `${node.fieldName}`,
26-
fieldPathExpression: fieldPathExpression,
30+
fieldPathExpression: wildCardPath,
2731
wildCardPath: wildCardPath,
2832
variableName: `${parent.variableName}_item_${node.fieldName}`,
2933
valueExpression: `${parent.variableName}.value[${node.fieldName}]`,
30-
outputExpression: `${parent.outputExpression}[${node.propertyName}]`,
34+
outputExpression: `${parent.variableName}_out[${node.propertyName}]`,
3135
isArrayMember: true,
3236
}
3337
}

src/compiler/nodes/array.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export class ArrayNodeCompiler extends BaseNode {
9393
variableName: this.field.variableName,
9494
bail: this.#node.bail,
9595
guardedCodeSnippet: `${defineArrayInitialOutput({
96+
variableName: this.field.variableName,
9697
outputExpression: this.field.outputExpression,
9798
outputValueExpression: `[]`,
9899
})}${this.#buffer.newLine}${this.#compileArrayElements()}`,

src/compiler/nodes/object.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,13 +158,13 @@ export class ObjectNodeCompiler extends BaseNode {
158158
variableName: this.field.variableName,
159159
bail: this.#node.bail,
160160
guardedCodeSnippet: `${defineObjectInitialOutput({
161+
variableName: this.field.variableName,
161162
outputExpression: this.field.outputExpression,
162163
outputValueExpression: '{}',
163164
})}${this.#buffer.newLine}${this.#compileObjectChildren()}${
164165
this.#buffer.newLine
165166
}${this.#compileObjectGroups()}${this.#buffer.newLine}${defineMoveProperties({
166167
variableName: this.field.variableName,
167-
outputExpression: this.field.outputExpression,
168168
allowUnknownProperties: this.#node.allowUnknownProperties,
169169
fieldsToIgnore: this.#node.allowUnknownProperties ? this.#getFieldNames(this.#node) : [],
170170
})}`,

src/compiler/nodes/record.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export class RecordNodeCompiler extends BaseNode {
9393
variableName: this.field.variableName,
9494
bail: this.#node.bail,
9595
guardedCodeSnippet: `${defineObjectInitialOutput({
96+
variableName: this.field.variableName,
9697
outputExpression: this.field.outputExpression,
9798
outputValueExpression: `{}`,
9899
})}${this.#compileRecordElements()}`,

src/compiler/nodes/tuple.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,12 @@ export class TupleNodeCompiler extends BaseNode {
8686
variableName: this.field.variableName,
8787
bail: this.#node.bail,
8888
guardedCodeSnippet: `${defineArrayInitialOutput({
89+
variableName: this.field.variableName,
8990
outputExpression: this.field.outputExpression,
9091
outputValueExpression: this.#node.allowUnknownProperties
9192
? `copyProperties(${this.field.variableName}.value)`
9293
: `[]`,
93-
})}${this.#compileTupleChildren()}${this.#buffer.newLine}`,
94+
})}${this.#compileTupleChildren()}`,
9495
})
9596

9697
/**

src/scripts/array/initial_output.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
* Options accepts by the output script
1212
*/
1313
type OutputOptions = {
14+
variableName: string
1415
outputExpression: string
1516
outputValueExpression: string
1617
}
@@ -19,8 +20,10 @@ type OutputOptions = {
1920
* Returns JS fragment for writing the initial output for an array
2021
*/
2122
export function defineArrayInitialOutput({
23+
variableName,
2224
outputExpression,
2325
outputValueExpression,
2426
}: OutputOptions) {
25-
return `${outputExpression} = ${outputValueExpression};`
27+
return `const ${variableName}_out = ${outputValueExpression};
28+
${outputExpression} = ${variableName}_out;`
2629
}

0 commit comments

Comments
 (0)