Befor generating
This commit is contained in:
Generated
Vendored
+136
@@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
|
||||
const { Ono } = require("@jsdevtools/ono");
|
||||
|
||||
const { stripHash, toFileSystemPath } = require("./url");
|
||||
|
||||
const JSONParserError = exports.JSONParserError = class JSONParserError extends Error {
|
||||
constructor (message, source) {
|
||||
super();
|
||||
|
||||
this.code = "EUNKNOWN";
|
||||
this.message = message;
|
||||
this.source = source;
|
||||
this.path = null;
|
||||
|
||||
Ono.extend(this);
|
||||
}
|
||||
|
||||
get footprint () {
|
||||
return `${this.path}+${this.source}+${this.code}+${this.message}`;
|
||||
}
|
||||
};
|
||||
|
||||
setErrorName(JSONParserError);
|
||||
|
||||
const JSONParserErrorGroup = exports.JSONParserErrorGroup = class JSONParserErrorGroup extends Error {
|
||||
constructor (parser) {
|
||||
super();
|
||||
|
||||
this.files = parser;
|
||||
this.message = `${this.errors.length} error${this.errors.length > 1 ? "s" : ""} occurred while reading '${toFileSystemPath(parser.$refs._root$Ref.path)}'`;
|
||||
|
||||
Ono.extend(this);
|
||||
}
|
||||
|
||||
static getParserErrors (parser) {
|
||||
const errors = [];
|
||||
|
||||
for (const $ref of Object.values(parser.$refs._$refs)) {
|
||||
if ($ref.errors) {
|
||||
errors.push(...$ref.errors);
|
||||
}
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
get errors () {
|
||||
return JSONParserErrorGroup.getParserErrors(this.files);
|
||||
}
|
||||
};
|
||||
|
||||
setErrorName(JSONParserErrorGroup);
|
||||
|
||||
const ParserError = exports.ParserError = class ParserError extends JSONParserError {
|
||||
constructor (message, source) {
|
||||
super(`Error parsing ${source}: ${message}`, source);
|
||||
|
||||
this.code = "EPARSER";
|
||||
}
|
||||
};
|
||||
|
||||
setErrorName(ParserError);
|
||||
|
||||
const UnmatchedParserError = exports.UnmatchedParserError = class UnmatchedParserError extends JSONParserError {
|
||||
constructor (source) {
|
||||
super(`Could not find parser for "${source}"`, source);
|
||||
|
||||
this.code = "EUNMATCHEDPARSER";
|
||||
}
|
||||
};
|
||||
|
||||
setErrorName(UnmatchedParserError);
|
||||
|
||||
const ResolverError = exports.ResolverError = class ResolverError extends JSONParserError {
|
||||
constructor (ex, source) {
|
||||
super(ex.message || `Error reading file "${source}"`, source);
|
||||
|
||||
this.code = "ERESOLVER";
|
||||
|
||||
if ("code" in ex) {
|
||||
this.ioErrorCode = String(ex.code);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setErrorName(ResolverError);
|
||||
|
||||
const UnmatchedResolverError = exports.UnmatchedResolverError = class UnmatchedResolverError extends JSONParserError {
|
||||
constructor (source) {
|
||||
super(`Could not find resolver for "${source}"`, source);
|
||||
|
||||
this.code = "EUNMATCHEDRESOLVER";
|
||||
}
|
||||
};
|
||||
|
||||
setErrorName(UnmatchedResolverError);
|
||||
|
||||
const MissingPointerError = exports.MissingPointerError = class MissingPointerError extends JSONParserError {
|
||||
constructor (token, path) {
|
||||
super(`Token "${token}" does not exist.`, stripHash(path));
|
||||
|
||||
this.code = "EMISSINGPOINTER";
|
||||
}
|
||||
};
|
||||
|
||||
setErrorName(MissingPointerError);
|
||||
|
||||
const InvalidPointerError = exports.InvalidPointerError = class InvalidPointerError extends JSONParserError {
|
||||
constructor (pointer, path) {
|
||||
super(`Invalid $ref pointer "${pointer}". Pointers must begin with "#/"`, stripHash(path));
|
||||
|
||||
this.code = "EINVALIDPOINTER";
|
||||
}
|
||||
};
|
||||
|
||||
setErrorName(InvalidPointerError);
|
||||
|
||||
function setErrorName (err) {
|
||||
Object.defineProperty(err.prototype, "name", {
|
||||
value: err.name,
|
||||
enumerable: true,
|
||||
});
|
||||
}
|
||||
|
||||
exports.isHandledError = function (err) {
|
||||
return err instanceof JSONParserError || err instanceof JSONParserErrorGroup;
|
||||
};
|
||||
|
||||
exports.normalizeError = function (err) {
|
||||
if (err.path === null) {
|
||||
err.path = [];
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
Generated
Vendored
+159
@@ -0,0 +1,159 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Returns the given plugins as an array, rather than an object map.
|
||||
* All other methods in this module expect an array of plugins rather than an object map.
|
||||
*
|
||||
* @param {object} plugins - A map of plugin objects
|
||||
* @return {object[]}
|
||||
*/
|
||||
exports.all = function (plugins) {
|
||||
return Object.keys(plugins)
|
||||
.filter((key) => {
|
||||
return typeof plugins[key] === "object";
|
||||
})
|
||||
.map((key) => {
|
||||
plugins[key].name = key;
|
||||
return plugins[key];
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters the given plugins, returning only the ones return `true` for the given method.
|
||||
*
|
||||
* @param {object[]} plugins - An array of plugin objects
|
||||
* @param {string} method - The name of the filter method to invoke for each plugin
|
||||
* @param {object} file - A file info object, which will be passed to each method
|
||||
* @return {object[]}
|
||||
*/
|
||||
exports.filter = function (plugins, method, file) {
|
||||
return plugins
|
||||
.filter((plugin) => {
|
||||
return !!getResult(plugin, method, file);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sorts the given plugins, in place, by their `order` property.
|
||||
*
|
||||
* @param {object[]} plugins - An array of plugin objects
|
||||
* @returns {object[]}
|
||||
*/
|
||||
exports.sort = function (plugins) {
|
||||
for (let plugin of plugins) {
|
||||
plugin.order = plugin.order || Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
return plugins.sort((a, b) => { return a.order - b.order; });
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the specified method of the given plugins, in order, until one of them returns a successful result.
|
||||
* Each method can return a synchronous value, a Promise, or call an error-first callback.
|
||||
* If the promise resolves successfully, or the callback is called without an error, then the result
|
||||
* is immediately returned and no further plugins are called.
|
||||
* If the promise rejects, or the callback is called with an error, then the next plugin is called.
|
||||
* If ALL plugins fail, then the last error is thrown.
|
||||
*
|
||||
* @param {object[]} plugins - An array of plugin objects
|
||||
* @param {string} method - The name of the method to invoke for each plugin
|
||||
* @param {object} file - A file info object, which will be passed to each method
|
||||
* @returns {Promise}
|
||||
*/
|
||||
exports.run = function (plugins, method, file, $refs) {
|
||||
let plugin, lastError, index = 0;
|
||||
|
||||
return new Promise(((resolve, reject) => {
|
||||
runNextPlugin();
|
||||
|
||||
function runNextPlugin () {
|
||||
plugin = plugins[index++];
|
||||
if (!plugin) {
|
||||
// There are no more functions, so re-throw the last error
|
||||
return reject(lastError);
|
||||
}
|
||||
|
||||
try {
|
||||
// console.log(' %s', plugin.name);
|
||||
let result = getResult(plugin, method, file, callback, $refs);
|
||||
if (result && typeof result.then === "function") {
|
||||
// A promise was returned
|
||||
result.then(onSuccess, onError);
|
||||
}
|
||||
else if (result !== undefined) {
|
||||
// A synchronous result was returned
|
||||
onSuccess(result);
|
||||
}
|
||||
else if (index === plugins.length) {
|
||||
throw new Error("No promise has been returned or callback has been called.");
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
function callback (err, result) {
|
||||
if (err) {
|
||||
onError(err);
|
||||
}
|
||||
else {
|
||||
onSuccess(result);
|
||||
}
|
||||
}
|
||||
|
||||
function onSuccess (result) {
|
||||
// console.log(' success');
|
||||
resolve({
|
||||
plugin,
|
||||
result
|
||||
});
|
||||
}
|
||||
|
||||
function onError (error) {
|
||||
// console.log(' %s', err.message || err);
|
||||
lastError = {
|
||||
plugin,
|
||||
error,
|
||||
};
|
||||
runNextPlugin();
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the value of the given property.
|
||||
* If the property is a function, then the result of the function is returned.
|
||||
* If the value is a RegExp, then it will be tested against the file URL.
|
||||
* If the value is an aray, then it will be compared against the file extension.
|
||||
*
|
||||
* @param {object} obj - The object whose property/method is called
|
||||
* @param {string} prop - The name of the property/method to invoke
|
||||
* @param {object} file - A file info object, which will be passed to the method
|
||||
* @param {function} [callback] - A callback function, which will be passed to the method
|
||||
* @returns {*}
|
||||
*/
|
||||
function getResult (obj, prop, file, callback, $refs) {
|
||||
let value = obj[prop];
|
||||
|
||||
if (typeof value === "function") {
|
||||
return value.apply(obj, [file, callback, $refs]);
|
||||
}
|
||||
|
||||
if (!callback) {
|
||||
// The synchronous plugin functions (canParse and canRead)
|
||||
// allow a "shorthand" syntax, where the user can match
|
||||
// files by RegExp or by file extension.
|
||||
if (value instanceof RegExp) {
|
||||
return value.test(file.url);
|
||||
}
|
||||
else if (typeof value === "string") {
|
||||
return value === file.extension;
|
||||
}
|
||||
else if (Array.isArray(value)) {
|
||||
return value.indexOf(file.extension) !== -1;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
Generated
Vendored
+271
@@ -0,0 +1,271 @@
|
||||
"use strict";
|
||||
|
||||
let isWindows = /^win/.test(process.platform),
|
||||
forwardSlashPattern = /\//g,
|
||||
protocolPattern = /^(\w{2,}):\/\//i,
|
||||
url = module.exports,
|
||||
jsonPointerSlash = /~1/g,
|
||||
jsonPointerTilde = /~0/g;
|
||||
|
||||
// RegExp patterns to URL-encode special characters in local filesystem paths
|
||||
let urlEncodePatterns = [
|
||||
/\?/g, "%3F",
|
||||
/\#/g, "%23",
|
||||
];
|
||||
|
||||
// RegExp patterns to URL-decode special characters for local filesystem paths
|
||||
let urlDecodePatterns = [
|
||||
/\%23/g, "#",
|
||||
/\%24/g, "$",
|
||||
/\%26/g, "&",
|
||||
/\%2C/g, ",",
|
||||
/\%40/g, "@"
|
||||
];
|
||||
|
||||
exports.parse = require("url").parse;
|
||||
exports.resolve = require("url").resolve;
|
||||
|
||||
/**
|
||||
* Returns the current working directory (in Node) or the current page URL (in browsers).
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.cwd = function cwd () {
|
||||
if (process.browser) {
|
||||
return location.href;
|
||||
}
|
||||
|
||||
let path = process.cwd();
|
||||
|
||||
let lastChar = path.slice(-1);
|
||||
if (lastChar === "/" || lastChar === "\\") {
|
||||
return path;
|
||||
}
|
||||
else {
|
||||
return path + "/";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the protocol of the given URL, or `undefined` if it has no protocol.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {?string}
|
||||
*/
|
||||
exports.getProtocol = function getProtocol (path) {
|
||||
let match = protocolPattern.exec(path);
|
||||
if (match) {
|
||||
return match[1].toLowerCase();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the lowercased file extension of the given URL,
|
||||
* or an empty string if it has no extension.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.getExtension = function getExtension (path) {
|
||||
let lastDot = path.lastIndexOf(".");
|
||||
if (lastDot >= 0) {
|
||||
return url.stripQuery(path.substr(lastDot).toLowerCase());
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the query, if any, from the given path.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.stripQuery = function stripQuery (path) {
|
||||
let queryIndex = path.indexOf("?");
|
||||
if (queryIndex >= 0) {
|
||||
path = path.substr(0, queryIndex);
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the hash (URL fragment), of the given path.
|
||||
* If there is no hash, then the root hash ("#") is returned.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.getHash = function getHash (path) {
|
||||
let hashIndex = path.indexOf("#");
|
||||
if (hashIndex >= 0) {
|
||||
return path.substr(hashIndex);
|
||||
}
|
||||
return "#";
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the hash (URL fragment), if any, from the given path.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.stripHash = function stripHash (path) {
|
||||
let hashIndex = path.indexOf("#");
|
||||
if (hashIndex >= 0) {
|
||||
path = path.substr(0, hashIndex);
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the given path is an HTTP(S) URL.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
exports.isHttp = function isHttp (path) {
|
||||
let protocol = url.getProtocol(path);
|
||||
if (protocol === "http" || protocol === "https") {
|
||||
return true;
|
||||
}
|
||||
else if (protocol === undefined) {
|
||||
// There is no protocol. If we're running in a browser, then assume it's HTTP.
|
||||
return process.browser;
|
||||
}
|
||||
else {
|
||||
// It's some other protocol, such as "ftp://", "mongodb://", etc.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether the given path is a filesystem path.
|
||||
* This includes "file://" URLs.
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
exports.isFileSystemPath = function isFileSystemPath (path) {
|
||||
if (process.browser) {
|
||||
// We're running in a browser, so assume that all paths are URLs.
|
||||
// This way, even relative paths will be treated as URLs rather than as filesystem paths
|
||||
return false;
|
||||
}
|
||||
|
||||
let protocol = url.getProtocol(path);
|
||||
return protocol === undefined || protocol === "file";
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a properly-encoded URL.
|
||||
*
|
||||
* This is intended to handle situations where JSON Schema $Ref Parser is called
|
||||
* with a filesystem path that contains characters which are not allowed in URLs.
|
||||
*
|
||||
* @example
|
||||
* The following filesystem paths would be converted to the following URLs:
|
||||
*
|
||||
* <"!@#$%^&*+=?'>.json ==> %3C%22!@%23$%25%5E&*+=%3F\'%3E.json
|
||||
* C:\\My Documents\\File (1).json ==> C:/My%20Documents/File%20(1).json
|
||||
* file://Project #42/file.json ==> file://Project%20%2342/file.json
|
||||
*
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.fromFileSystemPath = function fromFileSystemPath (path) {
|
||||
// Step 1: On Windows, replace backslashes with forward slashes,
|
||||
// rather than encoding them as "%5C"
|
||||
if (isWindows) {
|
||||
path = path.replace(/\\/g, "/");
|
||||
}
|
||||
|
||||
// Step 2: `encodeURI` will take care of MOST characters
|
||||
path = encodeURI(path);
|
||||
|
||||
// Step 3: Manually encode characters that are not encoded by `encodeURI`.
|
||||
// This includes characters such as "#" and "?", which have special meaning in URLs,
|
||||
// but are just normal characters in a filesystem path.
|
||||
for (let i = 0; i < urlEncodePatterns.length; i += 2) {
|
||||
path = path.replace(urlEncodePatterns[i], urlEncodePatterns[i + 1]);
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a URL to a local filesystem path.
|
||||
*
|
||||
* @param {string} path
|
||||
* @param {boolean} [keepFileProtocol] - If true, then "file://" will NOT be stripped
|
||||
* @returns {string}
|
||||
*/
|
||||
exports.toFileSystemPath = function toFileSystemPath (path, keepFileProtocol) {
|
||||
// Step 1: `decodeURI` will decode characters such as Cyrillic characters, spaces, etc.
|
||||
path = decodeURI(path);
|
||||
|
||||
// Step 2: Manually decode characters that are not decoded by `decodeURI`.
|
||||
// This includes characters such as "#" and "?", which have special meaning in URLs,
|
||||
// but are just normal characters in a filesystem path.
|
||||
for (let i = 0; i < urlDecodePatterns.length; i += 2) {
|
||||
path = path.replace(urlDecodePatterns[i], urlDecodePatterns[i + 1]);
|
||||
}
|
||||
|
||||
// Step 3: If it's a "file://" URL, then format it consistently
|
||||
// or convert it to a local filesystem path
|
||||
let isFileUrl = path.substr(0, 7).toLowerCase() === "file://";
|
||||
if (isFileUrl) {
|
||||
// Strip-off the protocol, and the initial "/", if there is one
|
||||
path = path[7] === "/" ? path.substr(8) : path.substr(7);
|
||||
|
||||
// insert a colon (":") after the drive letter on Windows
|
||||
if (isWindows && path[1] === "/") {
|
||||
path = path[0] + ":" + path.substr(1);
|
||||
}
|
||||
|
||||
if (keepFileProtocol) {
|
||||
// Return the consistently-formatted "file://" URL
|
||||
path = "file:///" + path;
|
||||
}
|
||||
else {
|
||||
// Convert the "file://" URL to a local filesystem path.
|
||||
// On Windows, it will start with something like "C:/".
|
||||
// On Posix, it will start with "/"
|
||||
isFileUrl = false;
|
||||
path = isWindows ? path : "/" + path;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Normalize Windows paths (unless it's a "file://" URL)
|
||||
if (isWindows && !isFileUrl) {
|
||||
// Replace forward slashes with backslashes
|
||||
path = path.replace(forwardSlashPattern, "\\");
|
||||
|
||||
// Capitalize the drive letter
|
||||
if (path.substr(1, 2) === ":\\") {
|
||||
path = path[0].toUpperCase() + path.substr(1);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a $ref pointer to a valid JSON Path.
|
||||
*
|
||||
* @param {string} pointer
|
||||
* @returns {Array<number | string>}
|
||||
*/
|
||||
exports.safePointerToPath = function safePointerToPath (pointer) {
|
||||
if (pointer.length <= 1 || pointer[0] !== "#" || pointer[1] !== "/") {
|
||||
return [];
|
||||
}
|
||||
|
||||
return pointer
|
||||
.slice(2)
|
||||
.split("/")
|
||||
.map((value) => {
|
||||
return decodeURIComponent(value)
|
||||
.replace(jsonPointerSlash, "/")
|
||||
.replace(jsonPointerTilde, "~");
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user