diff --git a/README.md b/README.md index faf10bdc..ff1f1460 100644 --- a/README.md +++ b/README.md @@ -689,8 +689,7 @@ 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 `ajv`, `fast-uri` and `ajv-formats` for -the standalone code to work. +itself. You need to have `fast-json-stringify` installed for the standalone code to work. ```js const fs = require('fs') diff --git a/index.js b/index.js index efee5081..423f15fd 100644 --- a/index.js +++ b/index.js @@ -170,18 +170,15 @@ function build (schema, options) { } } + /* eslint no-new-func: "off" */ + const contextFunc = new Function('validator', 'serializer', contextFunctionCode) + if (options.mode === 'standalone') { - // lazy load - const isValidatorUsed = context.validatorSchemasIds.size > 0 const buildStandaloneCode = require('./lib/standalone') - return buildStandaloneCode(options, validator, isValidatorUsed, contextFunctionCode) + return buildStandaloneCode(contextFunc, context, serializer, validator) } - /* eslint no-new-func: "off" */ - const contextFunc = new Function('validator', 'serializer', contextFunctionCode) - const stringifyFunc = contextFunc(validator, serializer) - - return stringifyFunc + return contextFunc(validator, serializer) } const objectKeywords = [ diff --git a/lib/serializer.js b/lib/serializer.js index fdc7ec0a..b3606e1a 100644 --- a/lib/serializer.js +++ b/lib/serializer.js @@ -20,6 +20,7 @@ module.exports = class Serializer { this.parseInteger = Math.trunc break } + this._options = options } asInteger (i) { @@ -156,4 +157,12 @@ module.exports = class Serializer { } return ((point < 32) || (surrogateFound === true)) ? JSON.stringify(str) : '"' + result + '"' } + + getState () { + return this._options + } + + static restoreFromState (state) { + return new Serializer(state) + } } diff --git a/lib/standalone.js b/lib/standalone.js index 567896a6..0abaa2cd 100644 --- a/lib/standalone.js +++ b/lib/standalone.js @@ -1,42 +1,31 @@ -const fs = require('fs') -const path = require('path') - -function buildStandaloneCode (options, validator, isValidatorUsed, contextFunctionCode) { - const serializerCode = fs.readFileSync(path.join(__dirname, 'serializer.js')).toString() - let buildAjvCode = '' - let ajvSchemasCode = '' - - if (isValidatorUsed) { - let defaultAjvSchema = '' - // we need to export the custom json schema - const defaultMeta = validator.ajv.defaultMeta() - if (typeof defaultMeta === 'string') { - defaultAjvSchema = defaultMeta - } else { - defaultAjvSchema = defaultMeta.$id || defaultMeta.id - } - - ajvSchemasCode += `const validator = new Validator(${JSON.stringify(options.ajv || {})})\n` - for (const [id, schema] of Object.entries(validator.ajv.schemas)) { - // should skip ajv default schema - if (id === defaultAjvSchema) continue - ajvSchemasCode += `validator.ajv.addSchema(${JSON.stringify(schema.schema)}, "${id}")\n` - } - buildAjvCode = fs.readFileSync(path.join(__dirname, 'validator.js')).toString() - buildAjvCode = buildAjvCode.replace("'use strict'", '').replace('module.exports = SchemaValidator', '') +'use strict' + +function buildStandaloneCode (contextFunc, context, serializer, validator) { + let ajvDependencyCode = '' + if (context.validatorSchemasIds.size > 0) { + ajvDependencyCode += `const validatorState = ${JSON.stringify(validator.getState())}\n` + ajvDependencyCode += 'const validator = Validator.restoreFromState(validatorState)\n' + } else { + ajvDependencyCode += 'const validator = null\n' } - const serializerOptions = options && options.rounding ? JSON.stringify({ options: options.rounding }) : '' return ` 'use strict' - ${serializerCode.replace("'use strict'", '').replace('module.exports = ', '')} - ${buildAjvCode} - const serializer = new Serializer(${serializerOptions}) - ${ajvSchemasCode} + const { dependencies } = require('fast-json-stringify/lib/standalone') + + const { Serializer, Validator } = dependencies + + const serializerState = ${JSON.stringify(serializer.getState())} + const serializer = Serializer.restoreFromState(serializerState) - ${contextFunctionCode.replace('return main', '')} + ${ajvDependencyCode} - module.exports = main` + module.exports = ${contextFunc.toString()}(validator, serializer)` } module.exports = buildStandaloneCode + +module.exports.dependencies = { + Serializer: require('./serializer'), + Validator: require('./validator') +} diff --git a/lib/validator.js b/lib/validator.js index 53dfa0fe..26c93f28 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -25,6 +25,9 @@ class Validator { return date instanceof Date } }) + + this._ajvSchemas = {} + this._ajvOptions = ajvOptions || {} } addSchema (schema, schemaName) { @@ -40,6 +43,7 @@ class Validator { const ajvSchema = clone(schema) this.convertSchemaToAjvFormat(ajvSchema) this.ajv.addSchema(ajvSchema, schemaKey) + this._ajvSchemas[schemaKey] = schema } } @@ -70,6 +74,21 @@ class Validator { } } } + + getState () { + return { + ajvOptions: this._ajvOptions, + ajvSchemas: this._ajvSchemas + } + } + + static restoreFromState (state) { + const validator = new Validator(state.ajvOptions) + for (const [id, ajvSchema] of Object.entries(state.ajvSchemas)) { + validator.ajv.addSchema(ajvSchema, id) + } + return validator + } } module.exports = Validator diff --git a/package.json b/package.json index 6f93d56b..c5ea1fa3 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,8 @@ "standard": "^17.0.0", "tap": "^16.0.1", "tsd": "^0.28.0", - "webpack": "^5.40.0" + "webpack": "^5.40.0", + "fast-json-stringify": "." }, "dependencies": { "@fastify/deepmerge": "^1.0.0",