Skip to content

Commit 4c661c1

Browse files
committed
🎉 feat: release
1 parent e90ada5 commit 4c661c1

File tree

7 files changed

+356
-31
lines changed

7 files changed

+356
-31
lines changed

CHANGELOG.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1-
# 0.0.2
1+
# 0.0.2 - 4 Feb 2025
22
Feature:
33
- support integer, bigint, date, datetime
4+
- support `default` value for optional, and nullable on primitive type
5+
6+
Improvement:
7+
- refactor properties instruction generation
8+
- flatten optional properties to speed up runtime performance in Bun
9+
- remove negate where possible in runtime
10+
- use stringified null to prevent `toString()` call
11+
12+
Bug fix:
13+
- `integer` is using `JSON.stringify`
414

515
# 0.0.1 - 3 Feb 2025
616
Bug fix:

benchmarks/utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ export const benchmark = <T extends TAnySchema>(
1616
throw new Error('Invalid result')
1717
}
1818

19-
if (process.env.DEBUG) console.log(encode.toString())
19+
if (process.env.DEBUG) {
20+
console.log(encode.toString())
21+
console.log(encode(value))
22+
}
2023

2124
compact(() => {
2225
barplot(() => {

example/index.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
import { t } from 'elysia'
22
import { createAccelerator } from '../src/index'
33

4-
const shape = t.Object({ a: t.Date() })
4+
const shape = t.Object({
5+
name: t.String(),
6+
playing: t.Nullable(t.Integer({ default: 2 }))
7+
})
58

6-
const value = { a: new Date() } satisfies typeof shape.static
9+
console.log(t.Integer({ default: 2 }))
10+
11+
const value = {
12+
name: 'saltyaom',
13+
playing: null
14+
} satisfies typeof shape.static
715

816
const stringify = createAccelerator(shape)
917

1018
console.log(stringify(value))
19+
console.log(stringify.toString())

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "json-accelerator",
3-
"version": "0.0.1",
3+
"version": "0.0.2",
44
"description": "Speed up JSON stringification by providing OpenAPI/TypeBox model",
55
"license": "MIT",
66
"scripts": {

src/index.ts

Lines changed: 83 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,47 @@ import type { TAnySchema } from '@sinclair/typebox'
33
const Kind = Symbol.for('TypeBox.Kind')
44
const OptionalKind = Symbol.for('TypeBox.Optional')
55

6-
const isSpecialProperty = (name: string): boolean => /(\ |-|\t|\n)/.test(name)
6+
const isSpecialProperty = (name: string) => /(\ |-|\t|\n)/.test(name)
77

8-
const joinProperty = (v1: string, v2: string): string => {
8+
const joinProperty = (v1: string, v2: string) => {
99
if (isSpecialProperty(v2)) return `${v1}["${v2}"]`
1010

1111
return `${v1}.${v2}`
1212
}
1313

14-
const encodeProperty = (v: string): string => `"${v}"`
14+
const encodeProperty = (v: string) => `"${v}"`
15+
16+
const isInteger = (schema: TAnySchema) => {
17+
if (!schema.anyOf || (Kind in schema && schema[Kind] !== 'Union'))
18+
return false
19+
20+
let hasIntegerFormat = false
21+
let hasNumberType = false
22+
23+
for (const type of schema.anyOf) {
24+
if (type.type === 'null' || type.type === 'undefined') {
25+
continue
26+
}
27+
28+
if (
29+
!hasIntegerFormat &&
30+
type.type === 'string' &&
31+
type.format === 'integer'
32+
) {
33+
hasIntegerFormat = true
34+
continue
35+
}
36+
37+
if (!hasNumberType && type.type === 'number') {
38+
hasNumberType = true
39+
continue
40+
}
41+
42+
return false
43+
}
44+
45+
return hasIntegerFormat && hasNumberType
46+
}
1547

1648
const getMetadata = (schema: TAnySchema) => {
1749
let isNullable = false
@@ -148,16 +180,16 @@ const accelerate = (
148180

149181
switch (schema.type) {
150182
case 'string':
151-
if (isNullable || isUndefinable)
152-
v = `\${${nullableCondition}?null:\`\\"\${${property}}\\"\`}`
183+
if (nullableCondition)
184+
v = `\${${nullableCondition}?${schema.default !== undefined ? `'"${schema.default}"'` : `'null'`}:\`\\"\${${property}}\\"\`}`
153185
else v = `\"\${${property}}\"`
154186
break
155187

156188
case 'number':
157189
case 'boolean':
158-
case 'integer':
159190
case 'bigint':
160-
if (nullableCondition) v = `\${${property}??null}`
191+
if (nullableCondition)
192+
v = `\${${property}??${schema.default !== undefined ? schema.default : `'null'`}}`
161193
else v = `\${${property}}`
162194
break
163195

@@ -169,7 +201,7 @@ const accelerate = (
169201
break
170202

171203
case 'object':
172-
if (nullableCondition) v += `\${${nullableCondition}?null:\``
204+
if (nullableCondition) v += `\${${nullableCondition}?"null":\``
173205

174206
v += '{'
175207

@@ -182,7 +214,7 @@ const accelerate = (
182214

183215
let init = true
184216
let hasOptional = false
185-
let op = `op[${instruction.optional}]`
217+
let op = `op${instruction.optional}`
186218

187219
for (const key in schema.properties)
188220
if (OptionalKind in schema.properties[key]) {
@@ -210,8 +242,16 @@ const accelerate = (
210242

211243
const comma = `\${${op}?',':(${op}=true)&&''}`
212244

245+
let defaultValue = schema.properties[key].default
246+
if (defaultValue !== undefined) {
247+
if (typeof defaultValue === 'string')
248+
defaultValue = `"${defaultValue}"`
249+
250+
defaultValue = `\`${comma}${k}:${defaultValue}\``
251+
} else defaultValue = '""'
252+
213253
v += isOptional
214-
? `\${(${name}!==undefined?\`${comma}${k}:${p}\`:'')}`
254+
? `\${(${name}===undefined?${defaultValue}:\`${comma}${k}:${p}\`)}`
215255
: hasOptional
216256
? `${!init ? ',' : `\${(${op}=true)&&""}`}${k}:${p}`
217257
: `${!init ? ',' : ''}${k}:${p}`
@@ -232,7 +272,7 @@ const accelerate = (
232272

233273
if (schema.items.type === 'string') {
234274
if (nullableCondition)
235-
v += `\${${nullableCondition}?null:${property}.length?\`["$\{${property}.join('",\"')}"]\`:"[]"}`
275+
v += `\${${nullableCondition}?"null":${property}.length?\`["$\{${property}.join('",\"')}"]\`:"[]"}`
236276
else
237277
v += `\${${property}.length?\`["$\{${property}.join('",\"')}"]\`:"[]"}`
238278

@@ -242,18 +282,18 @@ const accelerate = (
242282
if (
243283
schema.items.type === 'number' ||
244284
schema.items.type === 'boolean' ||
245-
schema.items.type === 'integer' ||
246-
schema.items.type === 'bigint'
285+
schema.items.type === 'bigint' ||
286+
isInteger(schema.items)
247287
) {
248288
if (nullableCondition)
249-
v += `\${${nullableCondition}?null:${property}.length?\`[$\{${property}.join(',')}]\`:"[]"`
289+
v += `\${${nullableCondition}?'"null"':${property}.length?\`[$\{${property}.join(',')}]\`:"[]"`
250290
else
251291
v += `\${${property}.length?\`[$\{${property}.join(',')}]\`:"[]"}`
252292

253293
break
254294
}
255295

256-
if (isNullable || isUndefinable) v += `\${!${property}?null:\``
296+
if (isNullable || isUndefinable) v += `\${!${property}?'"null"':\``
257297

258298
if (!isRoot) v += `\${(()=>{`
259299

@@ -276,14 +316,22 @@ const accelerate = (
276316
default:
277317
if (isDateType(schema)) {
278318
if (isNullable || isUndefinable)
279-
v = `\${${nullableCondition}?null:typeof ${property}==="object"?\`\"\${${property}.toISOString()}\"\`:${property}}`
319+
v = `\${${nullableCondition}?${schema.default !== undefined ? `'"${schema.default}"'` : "'null'"}:typeof ${property}==="object"?\`\"\${${property}.toISOString()}\"\`:${property}}`
280320
else {
281321
v = `\${typeof ${property}==="object"?\`\"\${${property}.toISOString()}\"\`:${property}}`
282322
}
283323

284324
break
285325
}
286326

327+
if (isInteger(schema)) {
328+
if (nullableCondition)
329+
v = `\${${property}??${schema.default !== undefined ? schema.default : `'null'`}}`
330+
else v = `\${${property}}`
331+
332+
break
333+
}
334+
287335
v = `$\{JSON.stringify(${property})}`
288336

289337
break
@@ -296,16 +344,27 @@ const accelerate = (
296344

297345
let setup = ''
298346

299-
if (instruction.optional)
300-
setup += `let op=new Array(${instruction.optional})\n`
347+
if (instruction.optional) {
348+
setup += 'let '
349+
350+
for (let i = 0; i < instruction.optional; i++) {
351+
if (i !== 0) setup += ','
352+
setup += `op${i}=false`
353+
}
354+
355+
setup += '\n'
356+
}
357+
358+
if (instruction.properties.length) {
359+
setup += 'const '
360+
361+
for (let i = 0; i < instruction.properties.length; i++) {
362+
if (i !== 0) setup += ','
363+
setup += `s${i}=${instruction.properties[i]}`
364+
}
301365

302-
for (let i = 0; i < instruction.properties.length; i++) {
303-
const key = `s${i}`
304-
if (i === 0) setup += 'const '
305-
else setup += ','
306-
setup += `${key}=${instruction.properties[i]}`
366+
setup += '\n'
307367
}
308-
if (instruction.properties.length) setup += '\n'
309368

310369
if (isArray) return setup + '\n' + v
311370

0 commit comments

Comments
 (0)