Skip to content

Commit

Permalink
fix: standalone ajv schema (#461)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
4 people committed Jun 13, 2022
1 parent 310325b commit 12fa1e9
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 22 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -679,8 +679,8 @@ console.log(stringify({ firstName: 'Foo', surname: 'bar' })) // '{"firstName":"F
### Standalone Mode

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

```js
const fs = require('fs')
Expand Down
26 changes: 25 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Options as AjvOptions } from "ajv"
import Ajv, { Options as AjvOptions } from "ajv"
declare namespace build {
interface BaseSchema {
/**
Expand Down Expand Up @@ -157,14 +157,38 @@ declare namespace build {
* Optionally configure how the integer will be rounded
*/
rounding?: 'ceil' | 'floor' | 'round'
/**
* @deprecated
* Enable debug mode. Please use `mode: "debug"` instead
*/
debugMode?: boolean
/**
* Running mode of fast-json-stringify
*/
mode?: 'debug' | 'standalone'
}
}

interface DebugOption extends build.Options {
mode: 'debug'
}

interface DeprecateDebugOption extends build.Options {
debugMode: true
}

interface StandaloneOption extends build.Options {
mode: 'standalone'
}

/**
* Build a stringify function using a schema of the documents that should be stringified
* @param schema The schema used to stringify values
* @param options The options to use (optional)
*/
declare function build(schema: build.AnySchema, options: DebugOption): { code: string, ajv: Ajv };
declare function build(schema: build.AnySchema, options: DeprecateDebugOption): { code: string, ajv: Ajv };
declare function build(schema: build.AnySchema, options: StandaloneOption): string;
declare function build(schema: build.AnySchema, options?: build.Options): (doc: any) => any;
declare function build(schema: build.StringSchema, options?: build.Options): (doc: string) => string;
declare function build(schema: build.IntegerSchema | build.NumberSchema, options?: build.Options): (doc: number) => string;
Expand Down
16 changes: 3 additions & 13 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,19 +136,9 @@ function build (schema, options) {
}

if (options.mode === 'standalone') {
return `
'use strict'
const Serializer = require('fast-json-stringify/serializer')
const buildAjv = require('fast-json-stringify/ajv')
const serializer = new Serializer(${JSON.stringify(options || {})})
const ajv = buildAjv(${JSON.stringify(options.ajv || {})})
${contextFunctionCode.replace('return main', '')}
module.exports = main
`
// lazy load
const buildStandaloneCode = require('./standalone')
return buildStandaloneCode(options, ajvInstance, contextFunctionCode)
}

/* eslint no-new-func: "off" */
Expand Down
42 changes: 42 additions & 0 deletions standalone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const fs = require('fs')
const path = require('path')

function buildStandaloneCode (options, ajvInstance, contextFunctionCode) {
const serializerCode = fs.readFileSync(path.join(__dirname, 'serializer.js')).toString()
let buildAjvCode = ''
let defaultAjvSchema = ''
const defaultMeta = ajvInstance.defaultMeta()
if (typeof defaultMeta === 'string') {
defaultAjvSchema = defaultMeta
} else {
defaultAjvSchema = defaultMeta.$id || defaultMeta.id
}
const shouldUseAjv = contextFunctionCode.indexOf('ajv') !== -1
// we need to export the custom json schema
let ajvSchemasCode = ''
if (shouldUseAjv) {
ajvSchemasCode += `const ajv = buildAjv(${JSON.stringify(options.ajv || {})})\n`
for (const [id, schema] of Object.entries(ajvInstance.schemas)) {
// should skip ajv default schema
if (id === defaultAjvSchema) continue
ajvSchemasCode += `ajv.addSchema(${JSON.stringify(schema.schema)}, "${id}")\n`
}
buildAjvCode = fs.readFileSync(path.join(__dirname, 'ajv.js')).toString()
buildAjvCode = buildAjvCode.replace("'use strict'", '').replace('module.exports = buildAjv', '')
}
return `
'use strict'
${serializerCode.replace("'use strict'", '').replace('module.exports = ', '')}
${buildAjvCode}
const serializer = new Serializer(${JSON.stringify(options || {})})
${ajvSchemasCode}
${contextFunctionCode.replace('return main', '')}
module.exports = main
`
}

module.exports = buildStandaloneCode
93 changes: 88 additions & 5 deletions test/standalone-mode.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ const fjs = require('..')
const fs = require('fs')
const path = require('path')

function build (opts) {
return fjs({
function build (opts, schema) {
return fjs(schema || {
title: 'default string',
type: 'object',
properties: {
Expand All @@ -21,10 +21,10 @@ function build (opts) {
const tmpDir = 'test/fixtures'

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

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

Expand All @@ -36,3 +36,86 @@ test('activate standalone mode', async (t) => {
const standalone = require(destionation)
t.same(standalone({ firstName: 'Foo', surname: 'bar' }), JSON.stringify({ firstName: 'Foo' }), 'surname evicted')
})

test('test ajv schema', async (t) => {
t.plan(3)
const code = build({ mode: 'standalone' }, {
type: 'object',
properties: {
},
if: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] }
}
},
then: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['foobar'] },
foo: { type: 'string' },
bar: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
},
else: {
type: 'object',
properties: {
kind: { type: 'string', enum: ['greeting'] },
hi: { type: 'string' },
hello: { type: 'number' },
list: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
value: { type: 'string' }
}
}
}
}
}
})
t.type(code, 'string')
t.equal(code.indexOf('ajv') > 0, true)

const destionation = path.resolve(tmpDir, 'standalone2.js')

t.teardown(async () => {
await fs.promises.rm(destionation, { force: true })
})

await fs.promises.writeFile(destionation, code)
const standalone = require(destionation)
t.same(standalone({
kind: 'foobar',
foo: 'FOO',
list: [{
name: 'name',
value: 'foo'
}],
bar: 42,
hi: 'HI',
hello: 45,
a: 'A',
b: 35
}), JSON.stringify({
kind: 'foobar',
foo: 'FOO',
bar: 42,
list: [{
name: 'name',
value: 'foo'
}]
}))
})
10 changes: 9 additions & 1 deletion test/types/test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Ajv from 'ajv'
import build, { Schema } from '../..'

// Number schemas
Expand Down Expand Up @@ -142,4 +143,11 @@ const schema12: Schema = {
format: 'date-time'
}

build(schema12)(new Date())
build(schema12)(new Date())

let str: string, ajv: Ajv
str = build(schema1, { debugMode: true }).code
ajv = build(schema1, { debugMode: true }).ajv
str = build(schema1, { mode: 'debug' }).code
ajv = build(schema1, { mode: 'debug' }).ajv
str = build(schema1, { mode: 'standalone' })

0 comments on commit 12fa1e9

Please sign in to comment.