8000 fix: standalone ajv schema (#461) · fastify/fast-json-stringify@12fa1e9 · GitHub
[go: up one dir, main page]

Skip to content

Commit 12fa1e9

Browse files
climba03003Fdawgsmcollinazedeus
authored
fix: standalone ajv schema (#461)
* fix: standalone ajv schemas * docs: update readme * types: update types * docs: update readme Co-authored-by: Frazer Smith <frazer.dev@outlook.com> * chore: typo Co-authored-by: Matteo Collina <hello@matteocollina.com> * chore: remove console log * feat: on-demand ajv * chore: lint * chore: typo Co-authored-by: Zed <zedeus@pm.me> * chore: update types Co-authored-by: Zed <zedeus@pm.me> Co-authored-by: Frazer Smith <frazer.dev@outlook.com> Co-authored-by: Matteo Collina <hello@matteocollina.com> Co-authored-by: Zed <zedeus@pm.me>
1 parent 310325b commit 12fa1e9

File tree

6 files changed

+169
-22
lines changed

6 files changed

+169
-22
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,8 @@ console.log(stringify({ firstName: 'Foo', surname: 'bar' })) // '{"firstName":"F
679679
### Standalone Mode
680680

681681
The standalone mode is used to compile the code that can be directly run by `node`
682-
itself. You need to install `fast-json-stringify`, `ajv`, `fast-uri` and `ajv-formats`
683-
in order to let the standalone code works.
682+
itself. You need to install `ajv`, `fast-uri` and `ajv-formats` for
683+
the standalone code to work.
684684

685685
```js
686686
const fs = require('fs')

index.d.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Options as AjvOptions } from "ajv"
1+
import Ajv, { Options as AjvOptions } from "ajv"
22
declare namespace build {
33
interface BaseSchema {
44
/**
@@ -157,14 +157,38 @@ declare namespace build {
157157
* Optionally configure how the integer will be rounded
158158
*/
159159
rounding?: 'ceil' | 'floor' | 'round'
160+
/**
161+
* @deprecated
162+
* Enable debug mode. Please use `mode: "debug"` instead
163+
*/
164+
debugMode?: boolean
165+
/**
166+
* Running mode of fast-json-stringify
167+
*/
168+
mode?: 'debug' | 'standalone'
160169
}
161170
}
162171

172+
interface DebugOption extends build.Options {
173+
mode: 'debug'
174+
}
175+
176+
interface DeprecateDebugOption extends build.Options {
177+
debugMode: true
178+
}
179+
180+
interface StandaloneOption extends build.Options {
181+
mode: 'standalone'
182+
}
183+
163184
/**
164185
* Build a stringify function using a schema of the documents that should be stringified
165186
* @param schema The schema used to stringify values
166187
* @param options The options to use (optional)
< 628C /code>
167188
*/
189+
declare function build(schema: build.AnySchema, options: DebugOption): { code: string, ajv: Ajv };
190+
declare function build(schema: build.AnySchema, options: DeprecateDebugOption): { code: string, ajv: Ajv };
191+
declare function build(schema: build.AnySchema, options: StandaloneOption): string;
168192
declare function build(schema: build.AnySchema, options?: build.Options): (doc: any) => any;
169193
declare function build(schema: build.StringSchema, options?: build.Options): (doc: string) => string;
170194
declare function build(schema: build.IntegerSchema | build.NumberSchema, options?: build.Options): (doc: number) => string;

index.js

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -136,19 +136,9 @@ function build (schema, options) {
136136
}
137137

138138
if (options.mode === 'standalone') {
139-
return `
140-
'use strict'
141-
142-
const Serializer = require('fast-json-stringify/serializer')
143-
const buildAjv = require('fast-json-stringify/ajv')
144-
145-
const serializer = new Serializer(${JSON.stringify(options || {})})
146-
const ajv = buildAjv(${JSON.stringify(options.ajv || {})})
147-
148-
${contextFunctionCode.replace('return main', '')}
149-
150-
module.exports = main
151-
`
139+
// lazy load
140+
const buildStandaloneCode = require('./standalone')
141+
return buildStandaloneCode(options, ajvInstance, contextFunctionCode)
152142
}
153143

154144
/* eslint no-new-func: "off" */

standalone.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
const fs = require('fs')
2+
const path = require('path')
3+
4+
function buildStandaloneCode (options, ajvInstance, contextFunctionCode) {
5+
const serializerCode = fs.readFileSync(path.join(__dirname, 'serializer.js')).toString()
6+
let buildAjvCode = ''
7+
let defaultAjvSchema = ''
8+
const defaultMeta = ajvInstance.defaultMeta()
9+
if (typeof defaultMeta === 'string') {
10+
defaultAjvSchema = defaultMeta
11+
} else {
12+
defaultAjvSchema = defaultMeta.$id || defaultMeta.id
13+
}
14+
const shouldUseAjv = contextFunctionCode.indexOf('ajv') !== -1
15+
// we need to export the custom json schema
16+
let ajvSchemasCode = ''
17+
if (shouldUseAjv) {
18+
ajvSchemasCode += `const ajv = buildAjv(${JSON.stringify(options.ajv || {})})\n`
19+
for (const [id, schema] of Object.entries(ajvInstance.schemas)) {
20+
// should skip ajv default schema
21+
if (id === defaultAjvSchema) continue
22+
ajvSchemasCode += `ajv.addSchema(${JSON.stringify(schema.schema)}, "${id}")\n`
23+
}
24+
buildAjvCode = fs.readFileSync(path.join(__dirname, 'ajv.js')).toString()
25+
buildAjvCode = buildAjvCode.replace("'use strict'", '').replace('module.exports = buildAjv', '')
26+
}
27+
return `
28+
'use strict'
29+
30+
${serializerCode.replace("'use strict'", '').replace('module.exports = ', '')}
31+
${buildAjvCode}
32+
33+
const serializer = new Serializer(${JSON.stringify(options || {})})
34+
${ajvSchemasCode}
35+
36+
${contextFunctionCode.replace('return main', '')}
37+
38+
module.exports = main
39+
`
40+
}
41+
42+
module.exports = buildStandaloneCode

test/standalone-mode.test.js

Lines changed: 88 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ const fjs = require('..')
55
const fs = require('fs')
66
const path = require('path')
77

8-
function build (opts) {
9-
return fjs({
8+
function build (opts, schema) {
9+
return fjs(schema || {
1010
title: 'default string',
1111
type: 'object',
1212
properties: {
@@ -21,10 +21,10 @@ function build (opts) {
2121
const tmpDir = 'test/fixtures'
2222

2323
test('activate standalone mode', async (t) => {
24-
t.plan(2)
25-
let code = build({ mode: 'standalone' })
24+
t.plan(3)
25+
const code = build({ mode: 'standalone' })
2626
t.type(code, 'string')
27-
code = code.replace(/fast-json-stringify/g, '../..')
27+
t.equal(code.indexOf('ajv'), -1)
2828

2929
const destionation = path.resolve(tmpDir, 'standalone.js')
3030

@@ -36,3 +36,86 @@ test('activate standalone mode', async (t) => {
3636
const standalone = require(destionation)
3737
t.same(standalone({ firstName: 'Foo', surname: 'bar' }), JSON.stringify({ firstName: 'Foo' }), 'surname evicted')
3838
})
39+
40+
test('test ajv schema', async (t) => {
41+
t.plan(3)
42+
const code = build({ mode: 'standalone' }, {
43+
type: 'object',
44+
properties: {
45+
},
46+
if: {
47+
type: 'object',
48+
properties: {
49+
kind: { type: 'string', enum: ['foobar'] }
50+
}
51+
},
52+
then: {
53+
type: 'object',
54+
properties: {
55+
kind: { type: 'string', enum: ['foobar'] },
56+
foo: { type: 'string' },
57+
bar: { type: 'number' },
58+
list: {
59+
type: 'array',
60+
items: {
61+
type: 'object',
62+
properties: {
63+
name: { type: 'string' },
64+
value: { type: 'string' }
65+
}
66+
}
67+
}
68+
}
69+
},
70+
else: {
71+
type: 'object',
72+
properties: {
73+
kind: { type: 'string', enum: ['greeting'] },
74+
hi: { type: 'string' },
75+
hello: { type: 'number' },
76+
list: {
77+
type: 'array',
78+
items: {
79+
type: 'object',
80+
properties: {
81+
name: { type: 'string' },
82+
value: { type: 'string' }
83+
}
84+
}
85+
}
86+
}
87+
}
88+
})
89+
t.type(code, 'string')
90+
t.equal(code.indexOf('ajv') > 0, true)
91+
92+
const destionation = path.resolve(tmpDir, 'standalone2.js')
93+
94+
t.teardown(async () => {
95+
await fs.promises.rm(destionation, { force: true })
96+
})
97+
98+
await fs.promises.writeFile(destionation, code)
99+
const standalone = require(destionation)
100+
t.same(standalone({
101+
kind: 'foobar',
102+
foo: 'FOO',
103+
list: [{
104+
name: 'name',
105+
value: 'foo'
106+
}],
107+
bar: 42,
108+
hi: 'HI',
109+
hello: 45,
110+
a: 'A',
111+
b: 35
112+
}), JSON.stringify({
113+
kind: 'foobar',
114+
foo: 'FOO',
115+
bar: 42,
116+
list: [{
117+
name: 'name',
118+
value: 'foo'
119+
}]
120+
}))
121+
})

test/types/test.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import Ajv from 'ajv'
12
import build, { Schema } from '../..'
23

34
// Number schemas
@@ -142,4 +143,11 @@ const schema12: Schema = {
142143
format: 'date-time'
143144
}
144145

145-
build(schema12)(new Date())
146+
build(schema12)(new Date())
147+
148+
let str: string, ajv: Ajv
149+
str = build(schema1, { debugMode: true }).code
150+
ajv = build(schema1, { debugMode: true }).ajv
151+
str = build(schema1, { mode: 'debug' }).code
152+
ajv = build(schema1, { mode: 'debug' }).ajv
153+
str = build(schema1, { mode: 'standalone' })

0 commit comments

Comments
 (0)
0