You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
283 lines
12 KiB
283 lines
12 KiB
/* eslint-disable no-unused-vars */
|
|
"use strict";
|
|
|
|
const $Refs = require("./refs");
|
|
const _parse = require("./parse");
|
|
const normalizeArgs = require("./normalize-args");
|
|
const resolveExternal = require("./resolve-external");
|
|
const _bundle = require("./bundle");
|
|
const _dereference = require("./dereference");
|
|
const url = require("./util/url");
|
|
const { JSONParserError, InvalidPointerError, MissingPointerError, ResolverError, ParserError, UnmatchedParserError, UnmatchedResolverError, isHandledError, JSONParserErrorGroup } = require("./util/errors");
|
|
const maybe = require("call-me-maybe");
|
|
const { ono } = require("@jsdevtools/ono");
|
|
|
|
module.exports = $RefParser;
|
|
module.exports.default = $RefParser;
|
|
module.exports.JSONParserError = JSONParserError;
|
|
module.exports.InvalidPointerError = InvalidPointerError;
|
|
module.exports.MissingPointerError = MissingPointerError;
|
|
module.exports.ResolverError = ResolverError;
|
|
module.exports.ParserError = ParserError;
|
|
module.exports.UnmatchedParserError = UnmatchedParserError;
|
|
module.exports.UnmatchedResolverError = UnmatchedResolverError;
|
|
|
|
/**
|
|
* This class parses a JSON schema, builds a map of its JSON references and their resolved values,
|
|
* and provides methods for traversing, manipulating, and dereferencing those references.
|
|
*
|
|
* @constructor
|
|
*/
|
|
function $RefParser () {
|
|
/**
|
|
* The parsed (and possibly dereferenced) JSON schema object
|
|
*
|
|
* @type {object}
|
|
* @readonly
|
|
*/
|
|
this.schema = null;
|
|
|
|
/**
|
|
* The resolved JSON references
|
|
*
|
|
* @type {$Refs}
|
|
* @readonly
|
|
*/
|
|
this.$refs = new $Refs();
|
|
}
|
|
|
|
/**
|
|
* Parses the given JSON schema.
|
|
* This method does not resolve any JSON references.
|
|
* It just reads a single file in JSON or YAML format, and parse it as a JavaScript object.
|
|
*
|
|
* @param {string} [path] - The file path or URL of the JSON schema
|
|
* @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
|
|
* @param {$RefParserOptions} [options] - Options that determine how the schema is parsed
|
|
* @param {function} [callback] - An error-first callback. The second parameter is the parsed JSON schema object.
|
|
* @returns {Promise} - The returned promise resolves with the parsed JSON schema object.
|
|
*/
|
|
$RefParser.parse = function parse (path, schema, options, callback) {
|
|
let Class = this; // eslint-disable-line consistent-this
|
|
let instance = new Class();
|
|
return instance.parse.apply(instance, arguments);
|
|
};
|
|
|
|
/**
|
|
* Parses the given JSON schema.
|
|
* This method does not resolve any JSON references.
|
|
* It just reads a single file in JSON or YAML format, and parse it as a JavaScript object.
|
|
*
|
|
* @param {string} [path] - The file path or URL of the JSON schema
|
|
* @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
|
|
* @param {$RefParserOptions} [options] - Options that determine how the schema is parsed
|
|
* @param {function} [callback] - An error-first callback. The second parameter is the parsed JSON schema object.
|
|
* @returns {Promise} - The returned promise resolves with the parsed JSON schema object.
|
|
*/
|
|
$RefParser.prototype.parse = async function parse (path, schema, options, callback) {
|
|
let args = normalizeArgs(arguments);
|
|
let promise;
|
|
|
|
if (!args.path && !args.schema) {
|
|
let err = ono(`Expected a file path, URL, or object. Got ${args.path || args.schema}`);
|
|
return maybe(args.callback, Promise.reject(err));
|
|
}
|
|
|
|
// Reset everything
|
|
this.schema = null;
|
|
this.$refs = new $Refs();
|
|
|
|
// If the path is a filesystem path, then convert it to a URL.
|
|
// NOTE: According to the JSON Reference spec, these should already be URLs,
|
|
// but, in practice, many people use local filesystem paths instead.
|
|
// So we're being generous here and doing the conversion automatically.
|
|
// This is not intended to be a 100% bulletproof solution.
|
|
// If it doesn't work for your use-case, then use a URL instead.
|
|
let pathType = "http";
|
|
if (url.isFileSystemPath(args.path)) {
|
|
args.path = url.fromFileSystemPath(args.path);
|
|
pathType = "file";
|
|
}
|
|
|
|
// Resolve the absolute path of the schema
|
|
args.path = url.resolve(url.cwd(), args.path);
|
|
|
|
if (args.schema && typeof args.schema === "object") {
|
|
// A schema object was passed-in.
|
|
// So immediately add a new $Ref with the schema object as its value
|
|
let $ref = this.$refs._add(args.path);
|
|
$ref.value = args.schema;
|
|
$ref.pathType = pathType;
|
|
promise = Promise.resolve(args.schema);
|
|
}
|
|
else {
|
|
// Parse the schema file/url
|
|
promise = _parse(args.path, this.$refs, args.options);
|
|
}
|
|
|
|
let me = this;
|
|
try {
|
|
let result = await promise;
|
|
|
|
if (result !== null && typeof result === "object" && !Buffer.isBuffer(result)) {
|
|
me.schema = result;
|
|
return maybe(args.callback, Promise.resolve(me.schema));
|
|
}
|
|
else if (args.options.continueOnError) {
|
|
me.schema = null; // it's already set to null at line 79, but let's set it again for the sake of readability
|
|
return maybe(args.callback, Promise.resolve(me.schema));
|
|
}
|
|
else {
|
|
throw ono.syntax(`"${me.$refs._root$Ref.path || result}" is not a valid JSON Schema`);
|
|
}
|
|
}
|
|
catch (err) {
|
|
if (!args.options.continueOnError || !isHandledError(err)) {
|
|
return maybe(args.callback, Promise.reject(err));
|
|
}
|
|
|
|
if (this.$refs._$refs[url.stripHash(args.path)]) {
|
|
this.$refs._$refs[url.stripHash(args.path)].addError(err);
|
|
}
|
|
|
|
return maybe(args.callback, Promise.resolve(null));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Parses the given JSON schema and resolves any JSON references, including references in
|
|
* externally-referenced files.
|
|
*
|
|
* @param {string} [path] - The file path or URL of the JSON schema
|
|
* @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
|
|
* @param {$RefParserOptions} [options] - Options that determine how the schema is parsed and resolved
|
|
* @param {function} [callback]
|
|
* - An error-first callback. The second parameter is a {@link $Refs} object containing the resolved JSON references
|
|
*
|
|
* @returns {Promise}
|
|
* The returned promise resolves with a {@link $Refs} object containing the resolved JSON references
|
|
*/
|
|
$RefParser.resolve = function resolve (path, schema, options, callback) {
|
|
let Class = this; // eslint-disable-line consistent-this
|
|
let instance = new Class();
|
|
return instance.resolve.apply(instance, arguments);
|
|
};
|
|
|
|
/**
|
|
* Parses the given JSON schema and resolves any JSON references, including references in
|
|
* externally-referenced files.
|
|
*
|
|
* @param {string} [path] - The file path or URL of the JSON schema
|
|
* @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
|
|
* @param {$RefParserOptions} [options] - Options that determine how the schema is parsed and resolved
|
|
* @param {function} [callback]
|
|
* - An error-first callback. The second parameter is a {@link $Refs} object containing the resolved JSON references
|
|
*
|
|
* @returns {Promise}
|
|
* The returned promise resolves with a {@link $Refs} object containing the resolved JSON references
|
|
*/
|
|
$RefParser.prototype.resolve = async function resolve (path, schema, options, callback) {
|
|
let me = this;
|
|
let args = normalizeArgs(arguments);
|
|
|
|
try {
|
|
await this.parse(args.path, args.schema, args.options);
|
|
await resolveExternal(me, args.options);
|
|
finalize(me);
|
|
return maybe(args.callback, Promise.resolve(me.$refs));
|
|
}
|
|
catch (err) {
|
|
return maybe(args.callback, Promise.reject(err));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Parses the given JSON schema, resolves any JSON references, and bundles all external references
|
|
* into the main JSON schema. This produces a JSON schema that only has *internal* references,
|
|
* not any *external* references.
|
|
*
|
|
* @param {string} [path] - The file path or URL of the JSON schema
|
|
* @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
|
|
* @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced
|
|
* @param {function} [callback] - An error-first callback. The second parameter is the bundled JSON schema object
|
|
* @returns {Promise} - The returned promise resolves with the bundled JSON schema object.
|
|
*/
|
|
$RefParser.bundle = function bundle (path, schema, options, callback) {
|
|
let Class = this; // eslint-disable-line consistent-this
|
|
let instance = new Class();
|
|
return instance.bundle.apply(instance, arguments);
|
|
};
|
|
|
|
/**
|
|
* Parses the given JSON schema, resolves any JSON references, and bundles all external references
|
|
* into the main JSON schema. This produces a JSON schema that only has *internal* references,
|
|
* not any *external* references.
|
|
*
|
|
* @param {string} [path] - The file path or URL of the JSON schema
|
|
* @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
|
|
* @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced
|
|
* @param {function} [callback] - An error-first callback. The second parameter is the bundled JSON schema object
|
|
* @returns {Promise} - The returned promise resolves with the bundled JSON schema object.
|
|
*/
|
|
$RefParser.prototype.bundle = async function bundle (path, schema, options, callback) {
|
|
let me = this;
|
|
let args = normalizeArgs(arguments);
|
|
|
|
try {
|
|
await this.resolve(args.path, args.schema, args.options);
|
|
_bundle(me, args.options);
|
|
finalize(me);
|
|
return maybe(args.callback, Promise.resolve(me.schema));
|
|
}
|
|
catch (err) {
|
|
return maybe(args.callback, Promise.reject(err));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Parses the given JSON schema, resolves any JSON references, and dereferences the JSON schema.
|
|
* That is, all JSON references are replaced with their resolved values.
|
|
*
|
|
* @param {string} [path] - The file path or URL of the JSON schema
|
|
* @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
|
|
* @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced
|
|
* @param {function} [callback] - An error-first callback. The second parameter is the dereferenced JSON schema object
|
|
* @returns {Promise} - The returned promise resolves with the dereferenced JSON schema object.
|
|
*/
|
|
$RefParser.dereference = function dereference (path, schema, options, callback) {
|
|
let Class = this; // eslint-disable-line consistent-this
|
|
let instance = new Class();
|
|
return instance.dereference.apply(instance, arguments);
|
|
};
|
|
|
|
/**
|
|
* Parses the given JSON schema, resolves any JSON references, and dereferences the JSON schema.
|
|
* That is, all JSON references are replaced with their resolved values.
|
|
*
|
|
* @param {string} [path] - The file path or URL of the JSON schema
|
|
* @param {object} [schema] - A JSON schema object. This object will be used instead of reading from `path`.
|
|
* @param {$RefParserOptions} [options] - Options that determine how the schema is parsed, resolved, and dereferenced
|
|
* @param {function} [callback] - An error-first callback. The second parameter is the dereferenced JSON schema object
|
|
* @returns {Promise} - The returned promise resolves with the dereferenced JSON schema object.
|
|
*/
|
|
$RefParser.prototype.dereference = async function dereference (path, schema, options, callback) {
|
|
let me = this;
|
|
let args = normalizeArgs(arguments);
|
|
|
|
try {
|
|
await this.resolve(args.path, args.schema, args.options);
|
|
_dereference(me, args.options);
|
|
finalize(me);
|
|
return maybe(args.callback, Promise.resolve(me.schema));
|
|
}
|
|
catch (err) {
|
|
return maybe(args.callback, Promise.reject(err));
|
|
}
|
|
};
|
|
|
|
function finalize (parser) {
|
|
const errors = JSONParserErrorGroup.getParserErrors(parser);
|
|
if (errors.length > 0) {
|
|
throw new JSONParserErrorGroup(parser);
|
|
}
|
|
}
|
|
|