Socket
Socket
Sign inDemoInstall

json-schema-to-typescript

Package Overview
Dependencies
Maintainers
1
Versions
114
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

json-schema-to-typescript - npm Package Compare versions

Comparing version 10.1.5 to 11.0.0

dist/src/optionValidator.d.ts

14

CHANGELOG.md

@@ -5,2 +5,16 @@ # Changelog

## 11.0.0
This is a major release with lots of bugfixes, some of which may change emitted types.
- 2ca6e50 Bugfix: Fix crash that may happen when emitting types for cyclical schemas (#323, #376)
- 8fa728e Bugfix: Fix tests on Windows, make snapshot ordering consistent
- b78a616 Bugfix: Make `compile()` non-mutating (#370, #443)
- a89ffe1 Bugfix: Add maximum size heuristic for tuple types (#438)
- 6fbcbc8 Bugfix: Improve performance & stability issue caused by JSON serialization (#422)
- 7aa353d Feat: Add support for `$id` (#436)
- 59747b1 Feat: Add support for specifying a default for `additionalProperties` (#335)
- 966cca5 Cleanup: Drop support for Node 10
## 10.1.0

@@ -7,0 +21,0 @@

40

dist/src/cli.js

@@ -5,3 +5,7 @@ #!/usr/bin/env node

if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {

@@ -95,3 +99,3 @@ if (k2 === undefined) k2 = k;

if ((ISGLOB || ISDIR) && argOut && argOut.includes('.d.ts')) {
throw new ReferenceError("You have specified a single file " + argOut + " output for a multi file input " + argIn + ". This feature is not yet supported, refer to issue #272 (https://github.com/bcherny/json-schema-to-typescript/issues/272)");
throw new ReferenceError("You have specified a single file ".concat(argOut, " output for a multi file input ").concat(argIn, ". This feature is not yet supported, refer to issue #272 (https://github.com/bcherny/json-schema-to-typescript/issues/272)"));
}

@@ -120,3 +124,3 @@ _a.label = 1;

e_1 = _a.sent();
utils_1.error(e_1);
(0, utils_1.error)(e_1);
process.exit(1);

@@ -131,3 +135,3 @@ return [3 /*break*/, 9];

function isDir(path) {
return fs_1.existsSync(path) && fs_1.lstatSync(path).isDirectory();
return (0, fs_1.existsSync)(path) && (0, fs_1.lstatSync)(path).isDirectory();
}

@@ -140,3 +144,3 @@ function processGlob(argIn, argOut, argv) {

switch (_a.label) {
case 0: return [4 /*yield*/, glob_promise_1.default(argIn)]; // execute glob pattern match
case 0: return [4 /*yield*/, (0, glob_promise_1.default)(argIn)]; // execute glob pattern match
case 1:

@@ -146,3 +150,3 @@ files = _a.sent() // execute glob pattern match

if (files.length === 0) {
throw ReferenceError("You passed a glob pattern \"" + argIn + "\", but there are no files that match that pattern in " + process.cwd());
throw ReferenceError("You passed a glob pattern \"".concat(argIn, "\", but there are no files that match that pattern in ").concat(process.cwd()));
}

@@ -167,3 +171,3 @@ return [4 /*yield*/, Promise.all(files.map(function (file) { return __awaiter(_this, void 0, void 0, function () {

var file = _a[0], result = _a[1];
var outputPath = argOut && argOut + "/" + path_1.basename(file, '.json') + ".d.ts";
var outputPath = argOut && "".concat(argOut, "/").concat((0, path_1.basename)(file, '.json'), ".d.ts");
outputResult(result, outputPath);

@@ -194,3 +198,3 @@ });

case 2:
outputPath = utils_1.pathTransform(argOut, argIn, file);
outputPath = (0, utils_1.pathTransform)(argOut, argIn, file);
_b = [file];

@@ -209,3 +213,3 @@ return [4 /*yield*/, processFile(file, argv)];

var file = _a[0], result = _a[1], outputPath = _a[2];
return outputResult(result, outputPath ? outputPath + "/" + path_1.basename(file, '.json') + ".d.ts" : undefined);
return outputResult(result, outputPath ? "".concat(outputPath, "/").concat((0, path_1.basename)(file, '.json'), ".d.ts") : undefined);
});

@@ -226,6 +230,6 @@ return [2 /*return*/];

case 1:
if (!isDir(path_1.dirname(outputPath))) {
mkdirp.sync(path_1.dirname(outputPath));
if (!isDir((0, path_1.dirname)(outputPath))) {
mkdirp.sync((0, path_1.dirname)(outputPath));
}
return [4 /*yield*/, fs_1.writeFile(outputPath, result)];
return [4 /*yield*/, (0, fs_1.writeFile)(outputPath, result)];
case 2: return [2 /*return*/, _a.sent()];

@@ -247,3 +251,3 @@ case 3: return [2 /*return*/];

schema = _b.apply(_a, [_c.sent()]);
return [2 /*return*/, index_1.compile(schema, argIn, argv)];
return [2 /*return*/, (0, index_1.compile)(schema, argIn, argv)];
}

@@ -255,4 +259,4 @@ });

if (paths === void 0) { paths = []; }
if (fs_1.existsSync(path) && fs_1.lstatSync(path).isDirectory()) {
fs_1.readdirSync(path_1.resolve(path)).forEach(function (item) { return getPaths(path_1.join(path, item), paths); });
if ((0, fs_1.existsSync)(path) && (0, fs_1.lstatSync)(path).isDirectory()) {
(0, fs_1.readdirSync)((0, path_1.resolve)(path)).forEach(function (item) { return getPaths((0, path_1.join)(path, item), paths); });
}

@@ -266,10 +270,10 @@ else {

if (!argIn) {
return get_stdin_1.default();
return (0, get_stdin_1.default)();
}
return fs_1.readFile(path_1.resolve(process.cwd(), argIn), 'utf-8');
return (0, fs_1.readFile)((0, path_1.resolve)(process.cwd(), argIn), 'utf-8');
}
function printHelp() {
var pkg = require('../../package.json');
process.stdout.write("\n" + pkg.name + " " + pkg.version + "\nUsage: json2ts [--input, -i] [IN_FILE] [--output, -o] [OUT_FILE] [OPTIONS]\n\nWith no IN_FILE, or when IN_FILE is -, read standard input.\nWith no OUT_FILE and when IN_FILE is specified, create .d.ts file in the same directory.\nWith no OUT_FILE nor IN_FILE, write to standard output.\n\nYou can use any of the following options by adding them at the end.\nBoolean values can be set to false using the 'no-' prefix.\n\n --cwd=XXX\n Root directory for resolving $ref\n --declareExternallyReferenced\n Declare external schemas referenced via '$ref'?\n --enableConstEnums\n Prepend enums with 'const'?\n --format\n Format code? Set this to false to improve performance.\n --style.XXX=YYY\n Prettier configuration\n --unknownAny\n Output unknown type instead of any type\n --unreachableDefinitions\n Generates code for definitions that aren't referenced by the schema\n");
process.stdout.write("\n".concat(pkg.name, " ").concat(pkg.version, "\nUsage: json2ts [--input, -i] [IN_FILE] [--output, -o] [OUT_FILE] [OPTIONS]\n\nWith no IN_FILE, or when IN_FILE is -, read standard input.\nWith no OUT_FILE and when IN_FILE is specified, create .d.ts file in the same directory.\nWith no OUT_FILE nor IN_FILE, write to standard output.\n\nYou can use any of the following options by adding them at the end.\nBoolean values can be set to false using the 'no-' prefix.\n\n --additionalProperties\n Default value for additionalProperties, when it is not explicitly set\n --cwd=XXX\n Root directory for resolving $ref\n --declareExternallyReferenced\n Declare external schemas referenced via '$ref'?\n --enableConstEnums\n Prepend enums with 'const'?\n --format\n Format code? Set this to false to improve performance.\n --maxItems\n Maximum number of unioned tuples to emit when representing bounded-size\n array types, before falling back to emitting unbounded arrays. Increase\n this to improve precision of emitted types, decrease it to improve\n performance, or set it to -1 to ignore minItems and maxItems.\n --style.XXX=YYY\n Prettier configuration\n --unknownAny\n Output unknown type instead of any type\n --unreachableDefinitions\n Generates code for definitions that aren't referenced by the schema\n"));
}
//# sourceMappingURL=cli.js.map

@@ -20,5 +20,5 @@ "use strict";

}
return prettier_1.format(code, __assign({ parser: 'typescript' }, options.style));
return (0, prettier_1.format)(code, __assign({ parser: 'typescript' }, options.style));
}
exports.format = format;
//# sourceMappingURL=formatter.js.map

@@ -0,3 +1,7 @@

/// <reference types="lodash" />
import { Options } from './index';
import { AST } from './types/AST';
export declare function generate(ast: AST, options?: Options): string;
declare function generateTypeUnmemoized(ast: AST, options: Options): string;
export declare const generateType: typeof generateTypeUnmemoized & import("lodash").MemoizedFunction;
export {};
"use strict";
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generate = void 0;
exports.generateType = exports.generate = void 0;
var lodash_1 = require("lodash");

@@ -67,3 +69,3 @@ var index_1 = require("./index");

type = [
AST_1.hasStandaloneName(ast) &&
(0, AST_1.hasStandaloneName)(ast) &&
(ast.standaloneName === rootASTName || options.declareExternallyReferenced) &&

@@ -101,17 +103,14 @@ generateStandaloneInterface(ast, options),

processed.add(ast);
var type = '';
switch (ast.type) {
case 'ARRAY':
type = [
return [
declareNamedTypes(ast.params, options, rootASTName, processed),
AST_1.hasStandaloneName(ast) ? generateStandaloneType(ast, options) : undefined
(0, AST_1.hasStandaloneName)(ast) ? generateStandaloneType(ast, options) : undefined
]
.filter(Boolean)
.join('\n');
break;
case 'ENUM':
type = '';
break;
return '';
case 'INTERFACE':
type = getSuperTypesAndParams(ast)
return getSuperTypesAndParams(ast)
.map(function (ast) {

@@ -123,8 +122,7 @@ return (ast.standaloneName === rootASTName || options.declareExternallyReferenced) &&

.join('\n');
break;
case 'INTERSECTION':
case 'TUPLE':
case 'UNION':
type = [
AST_1.hasStandaloneName(ast) ? generateStandaloneType(ast, options) : undefined,
return [
(0, AST_1.hasStandaloneName)(ast) ? generateStandaloneType(ast, options) : undefined,
ast.params

@@ -140,21 +138,21 @@ .map(function (ast) { return declareNamedTypes(ast, options, rootASTName, processed); })

.join('\n');
break;
default:
if (AST_1.hasStandaloneName(ast)) {
type = generateStandaloneType(ast, options);
if ((0, AST_1.hasStandaloneName)(ast)) {
return generateStandaloneType(ast, options);
}
return '';
}
return type;
}
function generateType(ast, options) {
function generateTypeUnmemoized(ast, options) {
var type = generateRawType(ast, options);
if (options.strictIndexSignatures && ast.keyName === '[k: string]') {
return type + " | undefined";
return "".concat(type, " | undefined");
}
return type;
}
exports.generateType = (0, lodash_1.memoize)(generateTypeUnmemoized);
function generateRawType(ast, options) {
utils_1.log('magenta', 'generator', ast);
if (AST_1.hasStandaloneName(ast)) {
return utils_1.toSafeString(ast.standaloneName);
(0, utils_1.log)('magenta', 'generator', ast);
if ((0, AST_1.hasStandaloneName)(ast)) {
return (0, utils_1.toSafeString)(ast.standaloneName);
}

@@ -166,3 +164,3 @@ switch (ast.type) {

return (function () {
var type = generateType(ast.params, options);
var type = (0, exports.generateType)(ast.params, options);
return type.endsWith('"') ? '(' + type + ')[]' : type + '[]';

@@ -193,3 +191,3 @@ })();

var spreadParam = ast.spreadParam;
var astParams = __spreadArrays(ast.params);
var astParams = __spreadArray([], ast.params, true);
if (minItems > 0 && minItems > astParams.length && ast.spreadParam === undefined) {

@@ -211,3 +209,3 @@ // this is a valid state, and JSONSchema doesn't care about the item type

if (spreadParam) {
var spread = '...(' + generateType(spreadParam, options) + ')[]';
var spread = '...(' + (0, exports.generateType)(spreadParam, options) + ')[]';
params.push(spread);

@@ -220,3 +218,3 @@ }

}
var paramsList = astParams.map(function (param) { return generateType(param, options); });
var paramsList = astParams.map(function (param) { return (0, exports.generateType)(param, options); });
if (paramsList.length > minItems) {

@@ -270,3 +268,3 @@ /*

function generateSetOperation(ast, options) {
var members = ast.params.map(function (_) { return generateType(_, options); });
var members = ast.params.map(function (_) { return (0, exports.generateType)(_, options); });
var separator = ast.type === 'UNION' ? '|' : '&';

@@ -282,11 +280,11 @@ return members.length === 1 ? members[0] : '(' + members.join(' ' + separator + ' ') + ')';

var isRequired = _a.isRequired, keyName = _a.keyName, ast = _a.ast;
return [isRequired, keyName, ast, generateType(ast, options)];
return [isRequired, keyName, ast, (0, exports.generateType)(ast, options)];
})
.map(function (_a) {
var isRequired = _a[0], keyName = _a[1], ast = _a[2], type = _a[3];
return (AST_1.hasComment(ast) && !ast.standaloneName ? generateComment(ast.comment) + '\n' : '') +
return ((0, AST_1.hasComment)(ast) && !ast.standaloneName ? generateComment(ast.comment) + '\n' : '') +
escapeKeyName(keyName) +
(isRequired ? '' : '?') +
': ' +
(AST_1.hasStandaloneName(ast) ? utils_1.toSafeString(type) : type);
((0, AST_1.hasStandaloneName)(ast) ? (0, utils_1.toSafeString)(type) : type);
})

@@ -298,13 +296,13 @@ .join('\n') +

function generateComment(comment) {
return __spreadArrays(['/**'], comment.split('\n').map(function (_) { return ' * ' + _; }), [' */']).join('\n');
return __spreadArray(__spreadArray(['/**'], comment.split('\n').map(function (_) { return ' * ' + _; }), true), [' */'], false).join('\n');
}
function generateStandaloneEnum(ast, options) {
return ((AST_1.hasComment(ast) ? generateComment(ast.comment) + '\n' : '') +
return (((0, AST_1.hasComment)(ast) ? generateComment(ast.comment) + '\n' : '') +
'export ' +
(options.enableConstEnums ? 'const ' : '') +
("enum " + utils_1.toSafeString(ast.standaloneName) + " {") +
"enum ".concat((0, utils_1.toSafeString)(ast.standaloneName), " {") +
'\n' +
ast.params.map(function (_a) {
var ast = _a.ast, keyName = _a.keyName;
return keyName + ' = ' + generateType(ast, options);
return keyName + ' = ' + (0, exports.generateType)(ast, options);
}).join(',\n') +

@@ -315,6 +313,6 @@ '\n' +

function generateStandaloneInterface(ast, options) {
return ((AST_1.hasComment(ast) ? generateComment(ast.comment) + '\n' : '') +
("export interface " + utils_1.toSafeString(ast.standaloneName) + " ") +
return (((0, AST_1.hasComment)(ast) ? generateComment(ast.comment) + '\n' : '') +
"export interface ".concat((0, utils_1.toSafeString)(ast.standaloneName), " ") +
(ast.superTypes.length > 0
? "extends " + ast.superTypes.map(function (superType) { return utils_1.toSafeString(superType.standaloneName); }).join(', ') + " "
? "extends ".concat(ast.superTypes.map(function (superType) { return (0, utils_1.toSafeString)(superType.standaloneName); }).join(', '), " ")
: '') +

@@ -324,4 +322,4 @@ generateInterface(ast, options));

function generateStandaloneType(ast, options) {
return ((AST_1.hasComment(ast) ? generateComment(ast.comment) + '\n' : '') +
("export type " + utils_1.toSafeString(ast.standaloneName) + " = " + generateType(lodash_1.omit(ast, 'standaloneName') /* TODO */, options)));
return (((0, AST_1.hasComment)(ast) ? generateComment(ast.comment) + '\n' : '') +
"export type ".concat((0, utils_1.toSafeString)(ast.standaloneName), " = ").concat((0, exports.generateType)((0, lodash_1.omit)(ast, 'standaloneName') /* TODO */, options)));
}

@@ -328,0 +326,0 @@ function escapeKeyName(keyName) {

import { JSONSchema4 } from 'json-schema';
import { Options as $RefOptions } from 'json-schema-ref-parser';
import { Options as $RefOptions } from '@apidevtools/json-schema-ref-parser';
import { Options as PrettierOptions } from 'prettier';

@@ -7,2 +7,10 @@ export { EnumJSONSchema, JSONSchema, NamedEnumJSONSchema, CustomTypeJSONSchema } from './types/JSONSchema';

/**
* [$RefParser](https://github.com/BigstickCarpet/json-schema-ref-parser) Options, used when resolving `$ref`s
*/
$refOptions: $RefOptions;
/**
* Default value for additionalProperties, when it is not explicitly set.
*/
additionalProperties: boolean;
/**
* Disclaimer comment prepended to the top of each generated file.

@@ -32,2 +40,9 @@ */

/**
* Maximum number of unioned tuples to emit when representing bounded-size array types,
* before falling back to emitting unbounded arrays. Increase this to improve precision
* of emitted types, decrease it to improve performance, or set it to `-1` to ignore
* `minItems` and `maxItems`.
*/
maxItems: number;
/**
* Append all index signatures with `| undefined` so that they are strictly typed.

@@ -50,6 +65,2 @@ *

unknownAny: boolean;
/**
* [$RefParser](https://github.com/BigstickCarpet/json-schema-ref-parser) Options, used when resolving `$ref`s
*/
$refOptions: $RefOptions;
}

@@ -56,0 +67,0 @@ export declare const DEFAULT_OPTIONS: Options;

@@ -10,2 +10,4 @@ "use strict";

return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);

@@ -78,4 +80,6 @@ function __() { this.constructor = d; }

var linker_1 = require("./linker");
var optionValidator_1 = require("./optionValidator");
exports.DEFAULT_OPTIONS = {
$refOptions: {},
additionalProperties: true,
bannerComment: "/* tslint:disable */\n/**\n* This file was automatically generated by json-schema-to-typescript.\n* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,\n* and run json-schema-to-typescript to regenerate this file.\n*/",

@@ -87,2 +91,3 @@ cwd: process.cwd(),

ignoreMinAndMaxItems: false,
maxItems: 20,
strictIndexSignatures: false,

@@ -103,9 +108,9 @@ style: {

if (options === void 0) { options = exports.DEFAULT_OPTIONS; }
var contents = utils_1.Try(function () { return fs_1.readFileSync(filename); }, function () {
throw new ReferenceError("Unable to read file \"" + filename + "\"");
var contents = (0, utils_1.Try)(function () { return (0, fs_1.readFileSync)(filename); }, function () {
throw new ReferenceError("Unable to read file \"".concat(filename, "\""));
});
var schema = utils_1.Try(function () { return JSON.parse(contents.toString()); }, function () {
throw new TypeError("Error parsing JSON in file \"" + filename + "\"");
var schema = (0, utils_1.Try)(function () { return JSON.parse(contents.toString()); }, function () {
throw new TypeError("Error parsing JSON in file \"".concat(filename, "\""));
});
return compile(schema, utils_1.stripExtension(filename), __assign({ cwd: path_1.dirname(filename) }, options));
return compile(schema, (0, utils_1.stripExtension)(filename), __assign({ cwd: (0, path_1.dirname)(filename) }, options));
}

@@ -117,61 +122,49 @@ exports.compileFromFile = compileFromFile;

function time() {
return "(" + (Date.now() - start) + "ms)";
return "(".concat(Date.now() - start, "ms)");
}
var _options, start, errors, dereferenced, linked, normalized, parsed, optimized, generated, formatted;
return __generator(this, function (_a) {
switch (_a.label) {
var _options, start, _schema, _a, dereferencedPaths, dereferencedSchema, linked, errors, normalized, parsed, optimized, generated, formatted;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_options = lodash_1.merge({}, exports.DEFAULT_OPTIONS, options);
(0, optionValidator_1.validateOptions)(options);
_options = (0, lodash_1.merge)({}, exports.DEFAULT_OPTIONS, options);
start = Date.now();
errors = validator_1.validate(schema, name);
if (errors.length) {
errors.forEach(function (_) { return utils_1.error(_); });
throw new ValidationError();
}
if (process.env.VERBOSE) {
utils_1.log('green', 'validator', time(), '✅ No change');
}
// normalize options
if (!lodash_1.endsWith(_options.cwd, '/')) {
if (!(0, lodash_1.endsWith)(_options.cwd, '/')) {
_options.cwd += '/';
}
return [4 /*yield*/, resolver_1.dereference(schema, _options)];
_schema = (0, lodash_1.cloneDeep)(schema);
return [4 /*yield*/, (0, resolver_1.dereference)(_schema, _options)];
case 1:
dereferenced = _a.sent();
_a = _b.sent(), dereferencedPaths = _a.dereferencedPaths, dereferencedSchema = _a.dereferencedSchema;
if (process.env.VERBOSE) {
if (util_1.isDeepStrictEqual(schema, dereferenced)) {
utils_1.log('green', 'dereferencer', time(), '✅ No change');
if ((0, util_1.isDeepStrictEqual)(_schema, dereferencedSchema)) {
(0, utils_1.log)('green', 'dereferencer', time(), '✅ No change');
}
else {
utils_1.log('green', 'dereferencer', time(), '✅ Result:', dereferenced);
(0, utils_1.log)('green', 'dereferencer', time(), '✅ Result:', dereferencedSchema);
}
}
linked = linker_1.link(dereferenced);
linked = (0, linker_1.link)(dereferencedSchema);
if (process.env.VERBOSE) {
utils_1.log('green', 'linker', time(), '✅ No change');
(0, utils_1.log)('green', 'linker', time(), '✅ No change');
}
normalized = normalizer_1.normalize(linked, name, _options);
if (process.env.VERBOSE) {
if (util_1.isDeepStrictEqual(linked, normalized)) {
utils_1.log('yellow', 'normalizer', time(), '✅ No change');
}
else {
utils_1.log('yellow', 'normalizer', time(), '✅ Result:', normalized);
}
errors = (0, validator_1.validate)(linked, name);
if (errors.length) {
errors.forEach(function (_) { return (0, utils_1.error)(_); });
throw new ValidationError();
}
parsed = parser_1.parse(normalized, _options);
utils_1.log('blue', 'parser', time(), '✅ Result:', parsed);
optimized = optimizer_1.optimize(parsed);
if (process.env.VERBOSE) {
if (util_1.isDeepStrictEqual(parsed, optimized)) {
utils_1.log('cyan', 'optimizer', time(), '✅ No change');
}
else {
utils_1.log('cyan', 'optimizer', time(), '✅ Result:', optimized);
}
(0, utils_1.log)('green', 'validator', time(), '✅ No change');
}
generated = generator_1.generate(optimized, _options);
utils_1.log('magenta', 'generator', time(), '✅ Result:', generated);
formatted = formatter_1.format(generated, _options);
utils_1.log('white', 'formatter', time(), '✅ Result:', formatted);
normalized = (0, normalizer_1.normalize)(linked, dereferencedPaths, name, _options);
(0, utils_1.log)('yellow', 'normalizer', time(), '✅ Result:', normalized);
parsed = (0, parser_1.parse)(normalized, _options);
(0, utils_1.log)('blue', 'parser', time(), '✅ Result:', parsed);
optimized = (0, optimizer_1.optimize)(parsed, _options);
(0, utils_1.log)('cyan', 'optimizer', time(), '✅ Result:', optimized);
generated = (0, generator_1.generate)(optimized, _options);
(0, utils_1.log)('magenta', 'generator', time(), '✅ Result:', generated);
formatted = (0, formatter_1.format)(generated, _options);
(0, utils_1.log)('white', 'formatter', time(), '✅ Result:', formatted);
return [2 /*return*/, formatted];

@@ -178,0 +171,0 @@ }

@@ -12,3 +12,3 @@ "use strict";

if (parent === void 0) { parent = null; }
if (!Array.isArray(schema) && !lodash_1.isPlainObject(schema)) {
if (!Array.isArray(schema) && !(0, lodash_1.isPlainObject)(schema)) {
return schema;

@@ -15,0 +15,0 @@ }

import { LinkedJSONSchema, NormalizedJSONSchema } from './types/JSONSchema';
import { Options } from './';
export declare function normalize(rootSchema: LinkedJSONSchema, filename: string, options: Options): NormalizedJSONSchema;
import { DereferencedPaths } from './resolver';
export declare function normalize(rootSchema: LinkedJSONSchema, dereferencedPaths: DereferencedPaths, filename: string, options: Options): NormalizedJSONSchema;
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -39,26 +48,67 @@ exports.normalize = void 0;

});
// TODO: default to empty schema (as per spec) instead
rules.set('Default additionalProperties to true', function (schema) {
rules.set('Default additionalProperties', function (schema, _, options) {
if (isObjectType(schema) && !('additionalProperties' in schema) && schema.patternProperties === undefined) {
schema.additionalProperties = true;
schema.additionalProperties = options.additionalProperties;
}
});
rules.set('Default top level `id`', function (schema, fileName) {
var isRoot = schema[JSONSchema_1.Parent] === null;
if (isRoot && !schema.id) {
schema.id = utils_1.toSafeString(utils_1.justName(fileName));
rules.set('Transform id to $id', function (schema, fileName) {
if (!(0, utils_1.isSchemaLike)(schema)) {
return;
}
if (schema.id && schema.$id && schema.id !== schema.$id) {
throw ReferenceError("Schema must define either id or $id, not both. Given id=".concat(schema.id, ", $id=").concat(schema.$id, " in ").concat(fileName));
}
if (schema.id) {
schema.$id = schema.id;
delete schema.id;
}
});
rules.set('Escape closing JSDoc Comment', function (schema) {
utils_1.escapeBlockComment(schema);
rules.set('Add an $id to anything that needs it', function (schema, fileName, _options, _key, dereferencedPaths) {
if (!(0, utils_1.isSchemaLike)(schema)) {
return;
}
// Top-level schema
if (!schema.$id && !schema[JSONSchema_1.Parent]) {
schema.$id = (0, utils_1.toSafeString)((0, utils_1.justName)(fileName));
return;
}
// Sub-schemas with references
if (!isArrayType(schema) && !isObjectType(schema)) {
return;
}
// We'll infer from $id and title downstream
// TODO: Normalize upstream
var dereferencedName = dereferencedPaths.get(schema);
if (!schema.$id && !schema.title && dereferencedName) {
schema.$id = (0, utils_1.toSafeString)((0, utils_1.justName)(dereferencedName));
}
if (dereferencedName) {
dereferencedPaths.delete(schema);
}
});
rules.set('Escape closing JSDoc comment', function (schema) {
(0, utils_1.escapeBlockComment)(schema);
});
rules.set('Add JSDoc comments for minItems and maxItems', function (schema) {
if (!isArrayType(schema)) {
return;
}
var commentsToAppend = [
'minItems' in schema ? "@minItems ".concat(schema.minItems) : '',
'maxItems' in schema ? "@maxItems ".concat(schema.maxItems) : ''
].filter(Boolean);
if (commentsToAppend.length) {
schema.description = utils_1.appendToDescription.apply(void 0, __spreadArray([schema.description], commentsToAppend, false));
}
});
rules.set('Optionally remove maxItems and minItems', function (schema, _fileName, options) {
if (options.ignoreMinAndMaxItems) {
if ('maxItems' in schema) {
delete schema.maxItems;
}
if ('minItems' in schema) {
delete schema.minItems;
}
if (!isArrayType(schema)) {
return;
}
if ('minItems' in schema && options.ignoreMinAndMaxItems) {
delete schema.minItems;
}
if ('maxItems' in schema && (options.ignoreMinAndMaxItems || options.maxItems === -1)) {
delete schema.maxItems;
}
});

@@ -70,8 +120,22 @@ rules.set('Normalize schema.minItems', function (schema, _fileName, options) {

// make sure we only add the props onto array types
if (isArrayType(schema)) {
var minItems = schema.minItems;
schema.minItems = typeof minItems === 'number' ? minItems : 0;
if (!isArrayType(schema)) {
return;
}
var minItems = schema.minItems;
schema.minItems = typeof minItems === 'number' ? minItems : 0;
// cannot normalize maxItems because maxItems = 0 has an actual meaning
});
rules.set('Remove maxItems if it is big enough to likely cause OOMs', function (schema, _fileName, options) {
if (options.ignoreMinAndMaxItems || options.maxItems === -1) {
return;
}
if (!isArrayType(schema)) {
return;
}
var maxItems = schema.maxItems, minItems = schema.minItems;
// minItems is guaranteed to be a number after the previous rule runs
if (maxItems !== undefined && maxItems - minItems > options.maxItems) {
delete schema.maxItems;
}
});
rules.set('Normalize schema.items', function (schema, _fileName, options) {

@@ -129,4 +193,4 @@ if (options.ignoreMinAndMaxItems) {

});
function normalize(rootSchema, filename, options) {
rules.forEach(function (rule) { return utils_1.traverse(rootSchema, function (schema) { return rule(schema, filename, options); }); });
function normalize(rootSchema, dereferencedPaths, filename, options) {
rules.forEach(function (rule) { return (0, utils_1.traverse)(rootSchema, function (schema, key) { return rule(schema, filename, options, key, dereferencedPaths); }); });
return rootSchema;

@@ -133,0 +197,0 @@ }

@@ -0,2 +1,3 @@

import { Options } from '.';
import { AST } from './types/AST';
export declare function optimize(ast: AST, processed?: Set<AST>): AST;
export declare function optimize(ast: AST, options: Options, processed?: Set<AST>): AST;
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.optimize = void 0;
var stringify = require("json-stringify-safe");
var lodash_1 = require("lodash");
var generator_1 = require("./generator");
var AST_1 = require("./types/AST");
var utils_1 = require("./utils");
function optimize(ast, processed) {
function optimize(ast, options, processed) {
if (processed === void 0) { processed = new Set(); }
utils_1.log('cyan', 'optimizer', ast, processed.has(ast) ? '(FROM CACHE)' : '');
if (processed.has(ast)) {

@@ -18,25 +28,38 @@ return ast;

return Object.assign(ast, {
params: ast.params.map(function (_) { return Object.assign(_, { ast: optimize(_.ast, processed) }); })
params: ast.params.map(function (_) { return Object.assign(_, { ast: optimize(_.ast, options, processed) }); })
});
case 'INTERSECTION':
case 'UNION':
// Start with the leaves...
var optimizedAST_1 = Object.assign(ast, {
params: ast.params.map(function (_) { return optimize(_, options, processed); })
});
// [A, B, C, Any] -> Any
if (ast.params.some(function (_) { return _.type === 'ANY'; })) {
utils_1.log('cyan', 'optimizer', '[A, B, C, Any] -> Any', ast);
if (optimizedAST_1.params.some(function (_) { return _.type === 'ANY'; })) {
(0, utils_1.log)('cyan', 'optimizer', '[A, B, C, Any] -> Any', optimizedAST_1);
return AST_1.T_ANY;
}
// [A, B, C, Unknown] -> Unknown
if (ast.params.some(function (_) { return _.type === 'UNKNOWN'; })) {
utils_1.log('cyan', 'optimizer', '[A, B, C, Unknown] -> Unknown', ast);
if (optimizedAST_1.params.some(function (_) { return _.type === 'UNKNOWN'; })) {
(0, utils_1.log)('cyan', 'optimizer', '[A, B, C, Unknown] -> Unknown', optimizedAST_1);
return AST_1.T_UNKNOWN;
}
// [A (named), A] -> [A (named)]
if (optimizedAST_1.params.every(function (_) {
var a = (0, generator_1.generateType)(omitStandaloneName(_), options);
var b = (0, generator_1.generateType)(omitStandaloneName(optimizedAST_1.params[0]), options);
return a === b;
}) &&
optimizedAST_1.params.some(function (_) { return _.standaloneName !== undefined; })) {
(0, utils_1.log)('cyan', 'optimizer', '[A (named), A] -> [A (named)]', optimizedAST_1);
optimizedAST_1.params = optimizedAST_1.params.filter(function (_) { return _.standaloneName !== undefined; });
}
// [A, B, B] -> [A, B]
var shouldTakeStandaloneNameIntoAccount_1 = ast.params.filter(function (_) { return _.standaloneName; }).length > 1;
var params = lodash_1.uniqBy(ast.params, function (_) { return "\n " + _.type + "-\n " + (shouldTakeStandaloneNameIntoAccount_1 ? _.standaloneName : '') + "-\n " + stringify(_.params) + "\n "; });
if (params.length !== ast.params.length) {
utils_1.log('cyan', 'optimizer', '[A, B, B] -> [A, B]', ast);
ast.params = params;
var params = (0, lodash_1.uniqBy)(optimizedAST_1.params, function (_) { return (0, generator_1.generateType)(_, options); });
if (params.length !== optimizedAST_1.params.length) {
(0, utils_1.log)('cyan', 'optimizer', '[A, B, B] -> [A, B]', optimizedAST_1);
optimizedAST_1.params = params;
}
return Object.assign(ast, {
params: ast.params.map(function (_) { return optimize(_, processed); })
return Object.assign(optimizedAST_1, {
params: optimizedAST_1.params.map(function (_) { return optimize(_, options, processed); })
});

@@ -48,2 +71,11 @@ default:

exports.optimize = optimize;
// TODO: More clearly disambiguate standalone names vs. aliased names instead.
function omitStandaloneName(ast) {
switch (ast.type) {
case 'ENUM':
return ast;
default:
return __assign(__assign({}, ast), { standaloneName: undefined });
}
}
//# sourceMappingURL=optimizer.js.map

@@ -24,9 +24,9 @@ "use strict";

if (usedNames === void 0) { usedNames = new Set(); }
if (JSONSchema_1.isPrimitive(schema)) {
if ((0, JSONSchema_1.isPrimitive)(schema)) {
return parseLiteral(schema, keyName);
}
var types = typesOfSchema_1.typesOfSchema(schema);
var types = (0, typesOfSchema_1.typesOfSchema)(schema);
if (types.length === 1) {
var ast_1 = parseAsTypeWithCache(schema, types[0], options, keyName, processed, usedNames);
utils_1.log('blue', 'parser', 'Types:', types, 'Input:', schema, 'Output:', ast_1);
(0, utils_1.log)('blue', 'parser', 'Types:', types, 'Input:', schema, 'Output:', ast_1);
return ast_1;

@@ -37,5 +37,5 @@ }

var ast = parseAsTypeWithCache({
$id: schema.$id,
allOf: [],
description: schema.description,
id: schema.id,
title: schema.title

@@ -46,5 +46,5 @@ }, 'ALL_OF', options, keyName, processed, usedNames);

// to the parent intersection type, so we remove it from the children.
return parseAsTypeWithCache(utils_1.maybeStripNameHints(schema), type, options, keyName, processed, usedNames);
return parseAsTypeWithCache((0, utils_1.maybeStripNameHints)(schema), type, options, keyName, processed, usedNames);
});
utils_1.log('blue', 'parser', 'Types:', types, 'Input:', schema, 'Output:', ast);
(0, utils_1.log)('blue', 'parser', 'Types:', types, 'Input:', schema, 'Output:', ast);
return ast;

@@ -83,4 +83,4 @@ }

function parseNonLiteral(schema, type, options, keyName, processed, usedNames) {
var definitions = getDefinitionsMemoized(JSONSchema_1.getRootSchema(schema)); // TODO
var keyNameFromDefinition = lodash_1.findKey(definitions, function (_) { return _ === schema; });
var definitions = getDefinitionsMemoized((0, JSONSchema_1.getRootSchema)(schema)); // TODO
var keyNameFromDefinition = (0, lodash_1.findKey)(definitions, function (_) { return _ === schema; });
switch (type) {

@@ -163,3 +163,3 @@ case 'ALL_OF':

case 'REFERENCE':
throw Error(util_1.format('Refs should have been resolved by the resolver!', schema));
throw Error((0, util_1.format)('Refs should have been resolved by the resolver!', schema));
case 'STRING':

@@ -209,4 +209,4 @@ return {

params: schema.type.map(function (type) {
var member = __assign(__assign({}, lodash_1.omit(schema, 'description', 'id', 'title')), { type: type });
return parse(utils_1.maybeStripDefault(member), options, undefined, processed, usedNames);
var member = __assign(__assign({}, (0, lodash_1.omit)(schema, '$id', 'description', 'title')), { type: type });
return parse((0, utils_1.maybeStripDefault)(member), options, undefined, processed, usedNames);
}),

@@ -257,5 +257,5 @@ type: 'UNION'

function standaloneName(schema, keyNameFromDefinition, usedNames) {
var name = schema.title || schema.id || keyNameFromDefinition;
var name = schema.title || schema.$id || keyNameFromDefinition;
if (name) {
return utils_1.generateName(name, usedNames);
return (0, utils_1.generateName)(name, usedNames);
}

@@ -287,6 +287,6 @@ }

function parseSchema(schema, options, processed, usedNames, parentSchemaName) {
var asts = lodash_1.map(schema.properties, function (value, key) { return ({
var asts = (0, lodash_1.map)(schema.properties, function (value, key) { return ({
ast: parse(value, options, key, processed, usedNames),
isPatternProperty: false,
isRequired: lodash_1.includes(schema.required || [], key),
isRequired: (0, lodash_1.includes)(schema.required || [], key),
isUnreachableDefinition: false,

@@ -301,10 +301,10 @@ keyName: key

singlePatternProperty = !schema.additionalProperties && Object.keys(schema.patternProperties).length === 1;
asts = asts.concat(lodash_1.map(schema.patternProperties, function (value, key) {
asts = asts.concat((0, lodash_1.map)(schema.patternProperties, function (value, key) {
var ast = parse(value, options, key, processed, usedNames);
var comment = "This interface was referenced by `" + parentSchemaName + "`'s JSON-Schema definition\nvia the `patternProperty` \"" + key + "\".";
ast.comment = ast.comment ? ast.comment + "\n\n" + comment : comment;
var comment = "This interface was referenced by `".concat(parentSchemaName, "`'s JSON-Schema definition\nvia the `patternProperty` \"").concat(key, "\".");
ast.comment = ast.comment ? "".concat(ast.comment, "\n\n").concat(comment) : comment;
return {
ast: ast,
isPatternProperty: !singlePatternProperty,
isRequired: singlePatternProperty || lodash_1.includes(schema.required || [], key),
isRequired: singlePatternProperty || (0, lodash_1.includes)(schema.required || [], key),
isUnreachableDefinition: false,

@@ -316,10 +316,10 @@ keyName: singlePatternProperty ? '[k: string]' : key

if (options.unreachableDefinitions) {
asts = asts.concat(lodash_1.map(schema.definitions, function (value, key) {
asts = asts.concat((0, lodash_1.map)(schema.definitions, function (value, key) {
var ast = parse(value, options, key, processed, usedNames);
var comment = "This interface was referenced by `" + parentSchemaName + "`'s JSON-Schema\nvia the `definition` \"" + key + "\".";
ast.comment = ast.comment ? ast.comment + "\n\n" + comment : comment;
var comment = "This interface was referenced by `".concat(parentSchemaName, "`'s JSON-Schema\nvia the `definition` \"").concat(key, "\".");
ast.comment = ast.comment ? "".concat(ast.comment, "\n\n").concat(comment) : comment;
return {
ast: ast,
isPatternProperty: false,
isRequired: lodash_1.includes(schema.required || [], key),
isRequired: (0, lodash_1.includes)(schema.required || [], key),
isUnreachableDefinition: true,

@@ -368,3 +368,3 @@ keyName: key

}
if (lodash_1.isPlainObject(schema)) {
if ((0, lodash_1.isPlainObject)(schema)) {
return __assign(__assign({}, (isSchema && hasDefinitions(schema) ? schema.definitions : {})), Object.keys(schema).reduce(function (prev, cur) { return (__assign(__assign({}, prev), getDefinitions(schema[cur], false, processed))); }, {}));

@@ -374,3 +374,3 @@ }

}
var getDefinitionsMemoized = lodash_1.memoize(getDefinitions);
var getDefinitionsMemoized = (0, lodash_1.memoize)(getDefinitions);
/**

@@ -377,0 +377,0 @@ * TODO: Reduce rate of false positives

@@ -1,6 +0,10 @@

import $RefParser = require('json-schema-ref-parser');
import $RefParser = require('@apidevtools/json-schema-ref-parser');
import { JSONSchema } from './types/JSONSchema';
export declare type DereferencedPaths = WeakMap<$RefParser.JSONSchemaObject, string>;
export declare function dereference(schema: JSONSchema, { cwd, $refOptions }: {
cwd: string;
$refOptions: $RefParser.Options;
}): Promise<JSONSchema>;
}): Promise<{
dereferencedPaths: DereferencedPaths;
dereferencedSchema: JSONSchema;
}>;
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {

@@ -40,3 +51,3 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }

exports.dereference = void 0;
var $RefParser = require("json-schema-ref-parser");
var $RefParser = require("@apidevtools/json-schema-ref-parser");
var utils_1 = require("./utils");

@@ -46,7 +57,17 @@ function dereference(schema, _a) {

return __awaiter(this, void 0, void 0, function () {
var parser;
var parser, dereferencedPaths, dereferencedSchema;
return __generator(this, function (_b) {
utils_1.log('green', 'dereferencer', 'Dereferencing input schema:', cwd, schema);
parser = new $RefParser();
return [2 /*return*/, parser.dereference(cwd, schema, $refOptions)];
switch (_b.label) {
case 0:
(0, utils_1.log)('green', 'dereferencer', 'Dereferencing input schema:', cwd, schema);
parser = new $RefParser();
dereferencedPaths = new WeakMap();
return [4 /*yield*/, parser.dereference(cwd, schema, __assign(__assign({}, $refOptions), { dereference: __assign(__assign({}, $refOptions.dereference), { onDereference: function ($ref, schema) {
dereferencedPaths.set(schema, $ref);
} }) }))];
case 1:
dereferencedSchema = _b.sent() // TODO: fix types
;
return [2 /*return*/, { dereferencedPaths: dereferencedPaths, dereferencedSchema: dereferencedSchema }];
}
});

@@ -53,0 +74,0 @@ });

@@ -65,2 +65,3 @@ /// <reference types="lodash" />

required: string[];
id: never;
}

@@ -67,0 +68,0 @@ export interface EnumJSONSchema extends NormalizedJSONSchema {

@@ -6,3 +6,3 @@ "use strict";

exports.Parent = Symbol('Parent');
exports.getRootSchema = lodash_1.memoize(function (schema) {
exports.getRootSchema = (0, lodash_1.memoize)(function (schema) {
var parent = schema[exports.Parent];

@@ -12,6 +12,6 @@ if (!parent) {

}
return exports.getRootSchema(parent);
return (0, exports.getRootSchema)(parent);
});
function isPrimitive(schema) {
return !lodash_1.isPlainObject(schema);
return !(0, lodash_1.isPlainObject)(schema);
}

@@ -18,0 +18,0 @@ exports.isPrimitive = isPrimitive;

@@ -56,3 +56,3 @@ "use strict";

}
if (!JSONSchema_1.isCompound(schema) && typeof schema.default === 'boolean') {
if (!(0, JSONSchema_1.isCompound)(schema) && typeof schema.default === 'boolean') {
return true;

@@ -69,3 +69,4 @@ }

NAMED_SCHEMA: function (schema) {
return 'id' in schema && ('patternProperties' in schema || 'properties' in schema);
// 8.2.1. The presence of "$id" in a subschema indicates that the subschema constitutes a distinct schema resource within a single schema document.
return '$id' in schema && ('patternProperties' in schema || 'properties' in schema);
},

@@ -82,3 +83,3 @@ NULL: function (schema) {

}
if (!JSONSchema_1.isCompound(schema) && typeof schema.default === 'number') {
if (!(0, JSONSchema_1.isCompound)(schema) && typeof schema.default === 'number') {
return true;

@@ -90,3 +91,3 @@ }

return (schema.type === 'object' &&
!lodash_1.isPlainObject(schema.additionalProperties) &&
!(0, lodash_1.isPlainObject)(schema.additionalProperties) &&
!schema.allOf &&

@@ -112,3 +113,3 @@ !schema.anyOf &&

}
if (!JSONSchema_1.isCompound(schema) && typeof schema.default === 'string') {
if (!(0, JSONSchema_1.isCompound)(schema) && typeof schema.default === 'string') {
return true;

@@ -115,0 +116,0 @@ }

import { JSONSchema, LinkedJSONSchema } from './types/JSONSchema';
export declare function Try<T>(fn: () => T, err: (e: Error) => any): T;
export declare function mapDeep(object: object, fn: (value: object, key?: string) => object, key?: string): object;
export declare function traverse(schema: LinkedJSONSchema, callback: (schema: LinkedJSONSchema) => void, processed?: Set<LinkedJSONSchema>): void;
export declare function traverse(schema: LinkedJSONSchema, callback: (schema: LinkedJSONSchema, key: string | null) => void, processed?: Set<LinkedJSONSchema>, key?: string): void;
/**

@@ -35,3 +34,3 @@ * Eg. `foo/bar/baz.json` => `baz`

/**
* Removes the schema's `id`, `name`, and `description` properties
* Removes the schema's `$id`, `name`, and `description` properties
* if they exist.

@@ -43,2 +42,4 @@ * Useful when parsing intersections.

export declare function maybeStripNameHints(schema: JSONSchema): JSONSchema;
export declare function appendToDescription(existingDescription: string | undefined, ...values: string[]): string;
export declare function isSchemaLike(schema: LinkedJSONSchema): boolean;
export {};
"use strict";
var __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.maybeStripNameHints = exports.maybeStripDefault = exports.pathTransform = exports.escapeBlockComment = exports.log = exports.error = exports.generateName = exports.toSafeString = exports.stripExtension = exports.justName = exports.traverse = exports.mapDeep = exports.Try = void 0;
exports.isSchemaLike = exports.appendToDescription = exports.maybeStripNameHints = exports.maybeStripDefault = exports.pathTransform = exports.escapeBlockComment = exports.log = exports.error = exports.generateName = exports.toSafeString = exports.stripExtension = exports.justName = exports.traverse = exports.Try = void 0;
var lodash_1 = require("lodash");
var path_1 = require("path");
var JSONSchema_1 = require("./types/JSONSchema");
// TODO: pull out into a separate package
// eslint-disable-next-line
function Try(fn, err) {

@@ -24,19 +26,2 @@ try {

exports.Try = Try;
function mapDeep(object, fn, key) {
return fn(lodash_1.mapValues(object, function (_, key) {
if (lodash_1.isPlainObject(_)) {
return mapDeep(_, fn, key);
}
else if (Array.isArray(_)) {
return _.map(function (item) {
if (lodash_1.isPlainObject(item)) {
return mapDeep(item, fn, key);
}
return item;
});
}
return _;
}), key);
}
exports.mapDeep = mapDeep;
// keys that shouldn't be traversed by the catchall step

@@ -81,3 +66,3 @@ var BLACKLISTED_KEYS = new Set([

if (obj[k] && typeof obj[k] === 'object' && !Array.isArray(obj[k])) {
traverse(obj[k], callback, processed);
traverse(obj[k], callback, processed, k);
}

@@ -87,5 +72,5 @@ });

function traverseArray(arr, callback, processed) {
arr.forEach(function (i) { return traverse(i, callback, processed); });
arr.forEach(function (s, k) { return traverse(s, callback, processed, k.toString()); });
}
function traverse(schema, callback, processed) {
function traverse(schema, callback, processed, key) {
if (processed === void 0) { processed = new Set(); }

@@ -97,3 +82,3 @@ // Handle recursive schemas

processed.add(schema);
callback(schema);
callback(schema, key !== null && key !== void 0 ? key : null);
if (schema.anyOf) {

@@ -159,3 +144,3 @@ traverseArray(schema.anyOf, callback, processed);

if (filename === void 0) { filename = ''; }
return stripExtension(path_1.basename(filename));
return stripExtension((0, path_1.basename)(filename));
}

@@ -167,3 +152,3 @@ exports.justName = justName;

function stripExtension(filename) {
return filename.replace(path_1.extname(filename), '');
return filename.replace((0, path_1.extname)(filename), '');
}

@@ -179,5 +164,5 @@ exports.stripExtension = stripExtension;

// Rest: a-zA-Z | _ | $ | 0-9
return lodash_1.upperFirst(
return (0, lodash_1.upperFirst)(
// remove accents, umlauts, ... by their basic latin letters
lodash_1.deburr(string)
(0, lodash_1.deburr)(string)
// replace chars which are not valid for typescript identifiers with whitespace

@@ -192,3 +177,3 @@ .replace(/(^\s*[^a-zA-Z_$])|([^a-zA-Z_$\d])/g, ' ')

// uppercase first letter after whitespace
.replace(/\s+([a-zA-Z])/g, function (match) { return lodash_1.trim(match.toUpperCase()); })
.replace(/\s+([a-zA-Z])/g, function (match) { return (0, lodash_1.trim)(match.toUpperCase()); })
// remove remaining whitespace

@@ -206,5 +191,5 @@ .replace(/\s/g, ''));

var counter = 1;
var nameWithCounter = "" + name + counter;
var nameWithCounter = "".concat(name).concat(counter);
while (usedNames.has(nameWithCounter)) {
nameWithCounter = "" + name + counter;
nameWithCounter = "".concat(name).concat(counter);
counter++;

@@ -227,3 +212,3 @@ }

}
console.error.apply(console, __spreadArrays([(_a = getStyledTextForLogging('red')) === null || _a === void 0 ? void 0 : _a('error')], messages));
console.error.apply(console, __spreadArray([(_a = getStyledTextForLogging('red')) === null || _a === void 0 ? void 0 : _a('error')], messages, false));
}

@@ -244,3 +229,3 @@ exports.error = error;

}
console.info.apply(console, __spreadArrays([require('cli-color').whiteBright.bgCyan('debug'), (_a = getStyledTextForLogging(style)) === null || _a === void 0 ? void 0 : _a(title)], messages));
console.info.apply(console, __spreadArray([require('cli-color').whiteBright.bgCyan('debug'), (_a = getStyledTextForLogging(style)) === null || _a === void 0 ? void 0 : _a(title)], messages, false));
if (lastMessage) {

@@ -300,6 +285,6 @@ console.dir(lastMessage, { depth: 6, maxArrayLength: 6 });

function pathTransform(outputPath, inputPath, filePath) {
var inPathList = path_1.normalize(inputPath).split(path_1.sep);
var filePathList = path_1.dirname(path_1.normalize(filePath)).split(path_1.sep);
var inPathList = (0, path_1.normalize)(inputPath).split(path_1.sep);
var filePathList = (0, path_1.dirname)((0, path_1.normalize)(filePath)).split(path_1.sep);
var filePathRel = filePathList.filter(function (f, i) { return f !== inPathList[i]; });
return path_1.join.apply(void 0, __spreadArrays([path_1.normalize(outputPath)], filePathRel));
return path_1.posix.join.apply(path_1.posix, __spreadArray([path_1.posix.normalize(outputPath)], filePathRel, false));
}

@@ -345,3 +330,3 @@ exports.pathTransform = pathTransform;

case 'object':
if (lodash_1.isPlainObject(schema.default)) {
if ((0, lodash_1.isPlainObject)(schema.default)) {
return schema;

@@ -356,3 +341,3 @@ }

/**
* Removes the schema's `id`, `name`, and `description` properties
* Removes the schema's `$id`, `name`, and `description` properties
* if they exist.

@@ -364,8 +349,8 @@ * Useful when parsing intersections.

function maybeStripNameHints(schema) {
if ('$id' in schema) {
delete schema.$id;
}
if ('description' in schema) {
delete schema.description;
}
if ('id' in schema) {
delete schema.id;
}
if ('name' in schema) {

@@ -377,2 +362,39 @@ delete schema.name;

exports.maybeStripNameHints = maybeStripNameHints;
function appendToDescription(existingDescription) {
var values = [];
for (var _i = 1; _i < arguments.length; _i++) {
values[_i - 1] = arguments[_i];
}
if (existingDescription) {
return "".concat(existingDescription, "\n\n").concat(values.join('\n'));
}
return values.join('\n');
}
exports.appendToDescription = appendToDescription;
function isSchemaLike(schema) {
if (!(0, lodash_1.isPlainObject)(schema)) {
return false;
}
var parent = schema[JSONSchema_1.Parent];
if (parent === null) {
return true;
}
var JSON_SCHEMA_KEYWORDS = [
'allOf',
'anyOf',
'dependencies',
'enum',
'oneOf',
'definitions',
'not',
'patternProperties',
'properties',
'required'
];
if (JSON_SCHEMA_KEYWORDS.some(function (_) { return parent[_] === schema; })) {
return false;
}
return true;
}
exports.isSchemaLike = isSchemaLike;
//# sourceMappingURL=utils.js.map

@@ -1,2 +0,2 @@

import { JSONSchema } from './types/JSONSchema';
export declare function validate(schema: JSONSchema, filename: string): string[];
import { LinkedJSONSchema } from './types/JSONSchema';
export declare function validate(schema: LinkedJSONSchema, filename: string): string[];

@@ -37,5 +37,5 @@ "use strict";

rules.forEach(function (rule, ruleName) {
utils_1.mapDeep(schema, function (schema, key) {
(0, utils_1.traverse)(schema, function (schema, key) {
if (rule(schema) === false) {
errors.push("Error at key \"" + key + "\" in file \"" + filename + "\": " + ruleName);
errors.push("Error at key \"".concat(key, "\" in file \"").concat(filename, "\": ").concat(ruleName));
}

@@ -42,0 +42,0 @@ return schema;

{
"name": "json-schema-to-typescript",
"version": "10.1.5",
"version": "11.0.0",
"description": "compile json schema to typescript typings",

@@ -11,3 +11,3 @@ "main": "dist/src/index.js",

"engines": {
"node": ">=10.0.0"
"node": ">=12.0.0"
},

@@ -50,40 +50,39 @@ "scripts": {

"dependencies": {
"@types/json-schema": "^7.0.6",
"@types/lodash": "^4.14.168",
"@types/prettier": "^2.1.5",
"cli-color": "^2.0.0",
"@apidevtools/json-schema-ref-parser": "https://github.com/bcherny/json-schema-ref-parser.git#984282d3",
"@types/json-schema": "^7.0.11",
"@types/lodash": "^4.14.182",
"@types/prettier": "^2.6.1",
"cli-color": "^2.0.2",
"get-stdin": "^8.0.0",
"glob": "^7.1.6",
"glob-promise": "^3.4.0",
"is-glob": "^4.0.1",
"json-schema-ref-parser": "^9.0.6",
"json-stringify-safe": "^5.0.1",
"lodash": "^4.17.20",
"minimist": "^1.2.5",
"glob-promise": "^4.2.2",
"is-glob": "^4.0.3",
"lodash": "^4.17.21",
"minimist": "^1.2.6",
"mkdirp": "^1.0.4",
"mz": "^2.7.0",
"prettier": "^2.2.0"
"prettier": "^2.6.2"
},
"devDependencies": {
"@types/cli-color": "^2.0.0",
"@types/glob": "^7.1.3",
"@types/is-glob": "^4.0.1",
"@types/minimist": "^1.2.1",
"@types/mkdirp": "^1.0.1",
"@types/mz": "^2.7.2",
"@types/node": "^14.14.10",
"@types/rimraf": "^3.0.0",
"@typescript-eslint/eslint-plugin": "^4.8.2",
"@typescript-eslint/parser": "^4.8.2",
"ava": "^3.13.0",
"@types/cli-color": "^2.0.2",
"@types/glob": "^7.2.0",
"@types/is-glob": "^4.0.2",
"@types/minimist": "^1.2.2",
"@types/mkdirp": "^1.0.2",
"@types/mz": "^2.7.4",
"@types/node": "^17.0.33",
"@types/rimraf": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^5.23.0",
"@typescript-eslint/parser": "^5.23.0",
"ava": "^4.2.0",
"browserify": "^17.0.0",
"browserify-shim": "^3.8.14",
"concurrently": "^5.3.0",
"eslint": "^7.14.0",
"eslint-config-prettier": "^6.15.0",
"eslint-plugin-prettier": "^3.1.4",
"concurrently": "^7.2.0",
"eslint": "^8.15.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.0.0",
"rimraf": "^3.0.2",
"shx": "^0.3.3",
"tsify": "^5.0.2",
"typescript": "^4.1.2"
"shx": "^0.3.4",
"tsify": "^5.0.4",
"typescript": "^4.6.4"
},

@@ -97,4 +96,3 @@ "ava": {

],
"snapshotDir": "./test/__snapshots__",
"vebose": true
"snapshotDir": "./test/__snapshots__"
},

@@ -101,0 +99,0 @@ "browserify": {

@@ -86,2 +86,3 @@ # json-schema-to-typescript [![Build Status][build]](https://github.com/bcherny/json-schema-to-typescript/actions?query=branch%3Amaster+workflow%3ACI) [![npm]](https://www.npmjs.com/package/json-schema-to-typescript) [![mit]](https://opensource.org/licenses/MIT)

|-|-|-|-|
| additionalProperties | boolean | `true` | Default value for `additionalProperties`, when it is not explicitly set |
| bannerComment | string | `"/* tslint:disable */\n/**\n* This file was automatically generated by json-schema-to-typescript.\n* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,\n* and run json-schema-to-typescript to regenerate this file.\n*/"` | Disclaimer comment prepended to the top of each generated file |

@@ -93,2 +94,3 @@ | cwd | string | `process.cwd()` | Root directory for resolving [`$ref`](https://tools.ietf.org/id/draft-pbryan-zyp-json-ref-03.html)s |

| ignoreMinAndMaxItems | boolean | `false` | Ignore maxItems and minItems for `array` types, preventing tuples being generated. |
| maxItems | number | `20` | Maximum number of unioned tuples to emit when representing bounded-size array types, before falling back to emitting unbounded arrays. Increase this to improve precision of emitted types, decrease it to improve performance, or set it to `-1` to ignore `maxItems`.
| style | object | `{ bracketSpacing: false, printWidth: 120, semi: true, singleQuote: false, tabWidth: 2, trailingComma: 'none', useTabs: false }` | A [Prettier](https://prettier.io/docs/en/options.html) configuration |

@@ -95,0 +97,0 @@ | unknownAny | boolean | `true` | Use `unknown` instead of `any` where possible |

@@ -154,2 +154,4 @@ #!/usr/bin/env node

--additionalProperties
Default value for additionalProperties, when it is not explicitly set
--cwd=XXX

@@ -163,2 +165,7 @@ Root directory for resolving $ref

Format code? Set this to false to improve performance.
--maxItems
Maximum number of unioned tuples to emit when representing bounded-size
array types, before falling back to emitting unbounded arrays. Increase
this to improve precision of emitted types, decrease it to improve
performance, or set it to -1 to ignore minItems and maxItems.
--style.XXX=YYY

@@ -165,0 +172,0 @@ Prettier configuration

@@ -1,2 +0,2 @@

import {omit} from 'lodash'
import {memoize, omit} from 'lodash'
import {DEFAULT_OPTIONS, Options} from './index'

@@ -110,7 +110,6 @@ import {

processed.add(ast)
let type = ''
switch (ast.type) {
case 'ARRAY':
type = [
return [
declareNamedTypes(ast.params, options, rootASTName, processed),

@@ -121,8 +120,6 @@ hasStandaloneName(ast) ? generateStandaloneType(ast, options) : undefined

.join('\n')
break
case 'ENUM':
type = ''
break
return ''
case 'INTERFACE':
type = getSuperTypesAndParams(ast)
return getSuperTypesAndParams(ast)
.map(

@@ -135,7 +132,6 @@ ast =>

.join('\n')
break
case 'INTERSECTION':
case 'TUPLE':
case 'UNION':
type = [
return [
hasStandaloneName(ast) ? generateStandaloneType(ast, options) : undefined,

@@ -152,13 +148,11 @@ ast.params

.join('\n')
break
default:
if (hasStandaloneName(ast)) {
type = generateStandaloneType(ast, options)
return generateStandaloneType(ast, options)
}
return ''
}
return type
}
function generateType(ast: AST, options: Options): string {
function generateTypeUnmemoized(ast: AST, options: Options): string {
const type = generateRawType(ast, options)

@@ -172,2 +166,3 @@

}
export const generateType = memoize(generateTypeUnmemoized)

@@ -174,0 +169,0 @@ function generateRawType(ast: AST, options: Options): string {

import {readFileSync} from 'fs'
import {JSONSchema4} from 'json-schema'
import {Options as $RefOptions} from 'json-schema-ref-parser'
import {endsWith, merge} from 'lodash'
import {Options as $RefOptions} from '@apidevtools/json-schema-ref-parser'
import {cloneDeep, endsWith, merge} from 'lodash'
import {dirname} from 'path'

@@ -17,2 +17,3 @@ import {Options as PrettierOptions} from 'prettier'

import {link} from './linker'
import {validateOptions} from './optionValidator'

@@ -23,2 +24,10 @@ export {EnumJSONSchema, JSONSchema, NamedEnumJSONSchema, CustomTypeJSONSchema} from './types/JSONSchema'

/**
* [$RefParser](https://github.com/BigstickCarpet/json-schema-ref-parser) Options, used when resolving `$ref`s
*/
$refOptions: $RefOptions
/**
* Default value for additionalProperties, when it is not explicitly set.
*/
additionalProperties: boolean
/**
* Disclaimer comment prepended to the top of each generated file.

@@ -48,2 +57,9 @@ */

/**
* Maximum number of unioned tuples to emit when representing bounded-size array types,
* before falling back to emitting unbounded arrays. Increase this to improve precision
* of emitted types, decrease it to improve performance, or set it to `-1` to ignore
* `minItems` and `maxItems`.
*/
maxItems: number
/**
* Append all index signatures with `| undefined` so that they are strictly typed.

@@ -66,6 +82,2 @@ *

unknownAny: boolean
/**
* [$RefParser](https://github.com/BigstickCarpet/json-schema-ref-parser) Options, used when resolving `$ref`s
*/
$refOptions: $RefOptions
}

@@ -75,2 +87,3 @@

$refOptions: {},
additionalProperties: true, // TODO: default to empty schema (as per spec) instead
bannerComment: `/* tslint:disable */

@@ -87,2 +100,3 @@ /**

ignoreMinAndMaxItems: false,
maxItems: 20,
strictIndexSignatures: false,

@@ -119,2 +133,4 @@ style: {

export async function compile(schema: JSONSchema4, name: string, options: Partial<Options> = {}): Promise<string> {
validateOptions(options)
const _options = merge({}, DEFAULT_OPTIONS, options)

@@ -127,11 +143,2 @@

const errors = validate(schema, name)
if (errors.length) {
errors.forEach(_ => error(_))
throw new ValidationError()
}
if (process.env.VERBOSE) {
log('green', 'validator', time(), '✅ No change')
}
// normalize options

@@ -142,12 +149,15 @@ if (!endsWith(_options.cwd, '/')) {

const dereferenced = await dereference(schema, _options)
// Initial clone to avoid mutating the input
const _schema = cloneDeep(schema)
const {dereferencedPaths, dereferencedSchema} = await dereference(_schema, _options)
if (process.env.VERBOSE) {
if (isDeepStrictEqual(schema, dereferenced)) {
if (isDeepStrictEqual(_schema, dereferencedSchema)) {
log('green', 'dereferencer', time(), '✅ No change')
} else {
log('green', 'dereferencer', time(), '✅ Result:', dereferenced)
log('green', 'dereferencer', time(), '✅ Result:', dereferencedSchema)
}
}
const linked = link(dereferenced)
const linked = link(dereferencedSchema)
if (process.env.VERBOSE) {

@@ -157,22 +167,19 @@ log('green', 'linker', time(), '✅ No change')

const normalized = normalize(linked, name, _options)
const errors = validate(linked, name)
if (errors.length) {
errors.forEach(_ => error(_))
throw new ValidationError()
}
if (process.env.VERBOSE) {
if (isDeepStrictEqual(linked, normalized)) {
log('yellow', 'normalizer', time(), '✅ No change')
} else {
log('yellow', 'normalizer', time(), '✅ Result:', normalized)
}
log('green', 'validator', time(), '✅ No change')
}
const normalized = normalize(linked, dereferencedPaths, name, _options)
log('yellow', 'normalizer', time(), '✅ Result:', normalized)
const parsed = parse(normalized, _options)
log('blue', 'parser', time(), '✅ Result:', parsed)
const optimized = optimize(parsed)
if (process.env.VERBOSE) {
if (isDeepStrictEqual(parsed, optimized)) {
log('cyan', 'optimizer', time(), '✅ No change')
} else {
log('cyan', 'optimizer', time(), '✅ Result:', optimized)
}
}
const optimized = optimize(parsed, _options)
log('cyan', 'optimizer', time(), '✅ Result:', optimized)

@@ -179,0 +186,0 @@ const generated = generate(optimized, _options)

import {JSONSchemaTypeName, LinkedJSONSchema, NormalizedJSONSchema, Parent} from './types/JSONSchema'
import {escapeBlockComment, justName, toSafeString, traverse} from './utils'
import {appendToDescription, escapeBlockComment, isSchemaLike, justName, toSafeString, traverse} from './utils'
import {Options} from './'
import {DereferencedPaths} from './resolver'
type Rule = (schema: LinkedJSONSchema, fileName: string, options: Options) => void
type Rule = (
schema: LinkedJSONSchema,
fileName: string,
options: Options,
key: string | null,
dereferencedPaths: DereferencedPaths
) => void
const rules = new Map<string, Rule>()

@@ -47,29 +54,78 @@

// TODO: default to empty schema (as per spec) instead
rules.set('Default additionalProperties to true', schema => {
rules.set('Default additionalProperties', (schema, _, options) => {
if (isObjectType(schema) && !('additionalProperties' in schema) && schema.patternProperties === undefined) {
schema.additionalProperties = true
schema.additionalProperties = options.additionalProperties
}
})
rules.set('Default top level `id`', (schema, fileName) => {
const isRoot = schema[Parent] === null
if (isRoot && !schema.id) {
schema.id = toSafeString(justName(fileName))
rules.set('Transform id to $id', (schema, fileName) => {
if (!isSchemaLike(schema)) {
return
}
if (schema.id && schema.$id && schema.id !== schema.$id) {
throw ReferenceError(
`Schema must define either id or $id, not both. Given id=${schema.id}, $id=${schema.$id} in ${fileName}`
)
}
if (schema.id) {
schema.$id = schema.id
delete schema.id
}
})
rules.set('Escape closing JSDoc Comment', schema => {
rules.set('Add an $id to anything that needs it', (schema, fileName, _options, _key, dereferencedPaths) => {
if (!isSchemaLike(schema)) {
return
}
// Top-level schema
if (!schema.$id && !schema[Parent]) {
schema.$id = toSafeString(justName(fileName))
return
}
// Sub-schemas with references
if (!isArrayType(schema) && !isObjectType(schema)) {
return
}
// We'll infer from $id and title downstream
// TODO: Normalize upstream
const dereferencedName = dereferencedPaths.get(schema)
if (!schema.$id && !schema.title && dereferencedName) {
schema.$id = toSafeString(justName(dereferencedName))
}
if (dereferencedName) {
dereferencedPaths.delete(schema)
}
})
rules.set('Escape closing JSDoc comment', schema => {
escapeBlockComment(schema)
})
rules.set('Add JSDoc comments for minItems and maxItems', schema => {
if (!isArrayType(schema)) {
return
}
const commentsToAppend = [
'minItems' in schema ? `@minItems ${schema.minItems}` : '',
'maxItems' in schema ? `@maxItems ${schema.maxItems}` : ''
].filter(Boolean)
if (commentsToAppend.length) {
schema.description = appendToDescription(schema.description, ...commentsToAppend)
}
})
rules.set('Optionally remove maxItems and minItems', (schema, _fileName, options) => {
if (options.ignoreMinAndMaxItems) {
if ('maxItems' in schema) {
delete schema.maxItems
}
if ('minItems' in schema) {
delete schema.minItems
}
if (!isArrayType(schema)) {
return
}
if ('minItems' in schema && options.ignoreMinAndMaxItems) {
delete schema.minItems
}
if ('maxItems' in schema && (options.ignoreMinAndMaxItems || options.maxItems === -1)) {
delete schema.maxItems
}
})

@@ -82,9 +138,24 @@

// make sure we only add the props onto array types
if (isArrayType(schema)) {
const {minItems} = schema
schema.minItems = typeof minItems === 'number' ? minItems : 0
if (!isArrayType(schema)) {
return
}
const {minItems} = schema
schema.minItems = typeof minItems === 'number' ? minItems : 0
// cannot normalize maxItems because maxItems = 0 has an actual meaning
})
rules.set('Remove maxItems if it is big enough to likely cause OOMs', (schema, _fileName, options) => {
if (options.ignoreMinAndMaxItems || options.maxItems === -1) {
return
}
if (!isArrayType(schema)) {
return
}
const {maxItems, minItems} = schema
// minItems is guaranteed to be a number after the previous rule runs
if (maxItems !== undefined && maxItems - (minItems as number) > options.maxItems) {
delete schema.maxItems
}
})
rules.set('Normalize schema.items', (schema, _fileName, options) => {

@@ -150,5 +221,10 @@ if (options.ignoreMinAndMaxItems) {

export function normalize(rootSchema: LinkedJSONSchema, filename: string, options: Options): NormalizedJSONSchema {
rules.forEach(rule => traverse(rootSchema, schema => rule(schema, filename, options)))
export function normalize(
rootSchema: LinkedJSONSchema,
dereferencedPaths: DereferencedPaths,
filename: string,
options: Options
): NormalizedJSONSchema {
rules.forEach(rule => traverse(rootSchema, (schema, key) => rule(schema, filename, options, key, dereferencedPaths)))
return rootSchema as NormalizedJSONSchema
}

@@ -1,9 +0,8 @@

import stringify = require('json-stringify-safe')
import {uniqBy} from 'lodash'
import {Options} from '.'
import {generateType} from './generator'
import {AST, T_ANY, T_UNKNOWN} from './types/AST'
import {log} from './utils'
export function optimize(ast: AST, processed = new Set<AST>()): AST {
log('cyan', 'optimizer', ast, processed.has(ast) ? '(FROM CACHE)' : '')
export function optimize(ast: AST, options: Options, processed = new Set<AST>()): AST {
if (processed.has(ast)) {

@@ -18,9 +17,14 @@ return ast

return Object.assign(ast, {
params: ast.params.map(_ => Object.assign(_, {ast: optimize(_.ast, processed)}))
params: ast.params.map(_ => Object.assign(_, {ast: optimize(_.ast, options, processed)}))
})
case 'INTERSECTION':
case 'UNION':
// Start with the leaves...
const optimizedAST = Object.assign(ast, {
params: ast.params.map(_ => optimize(_, options, processed))
})
// [A, B, C, Any] -> Any
if (ast.params.some(_ => _.type === 'ANY')) {
log('cyan', 'optimizer', '[A, B, C, Any] -> Any', ast)
if (optimizedAST.params.some(_ => _.type === 'ANY')) {
log('cyan', 'optimizer', '[A, B, C, Any] -> Any', optimizedAST)
return T_ANY

@@ -30,24 +34,29 @@ }

// [A, B, C, Unknown] -> Unknown
if (ast.params.some(_ => _.type === 'UNKNOWN')) {
log('cyan', 'optimizer', '[A, B, C, Unknown] -> Unknown', ast)
if (optimizedAST.params.some(_ => _.type === 'UNKNOWN')) {
log('cyan', 'optimizer', '[A, B, C, Unknown] -> Unknown', optimizedAST)
return T_UNKNOWN
}
// [A (named), A] -> [A (named)]
if (
optimizedAST.params.every(_ => {
const a = generateType(omitStandaloneName(_), options)
const b = generateType(omitStandaloneName(optimizedAST.params[0]), options)
return a === b
}) &&
optimizedAST.params.some(_ => _.standaloneName !== undefined)
) {
log('cyan', 'optimizer', '[A (named), A] -> [A (named)]', optimizedAST)
optimizedAST.params = optimizedAST.params.filter(_ => _.standaloneName !== undefined)
}
// [A, B, B] -> [A, B]
const shouldTakeStandaloneNameIntoAccount = ast.params.filter(_ => _.standaloneName).length > 1
const params = uniqBy(
ast.params,
_ => `
${_.type}-
${shouldTakeStandaloneNameIntoAccount ? _.standaloneName : ''}-
${stringify((_ as any).params)}
`
)
if (params.length !== ast.params.length) {
log('cyan', 'optimizer', '[A, B, B] -> [A, B]', ast)
ast.params = params
const params = uniqBy(optimizedAST.params, _ => generateType(_, options))
if (params.length !== optimizedAST.params.length) {
log('cyan', 'optimizer', '[A, B, B] -> [A, B]', optimizedAST)
optimizedAST.params = params
}
return Object.assign(ast, {
params: ast.params.map(_ => optimize(_, processed))
return Object.assign(optimizedAST, {
params: optimizedAST.params.map(_ => optimize(_, options, processed))
})

@@ -58,1 +67,11 @@ default:

}
// TODO: More clearly disambiguate standalone names vs. aliased names instead.
function omitStandaloneName<A extends AST>(ast: A): A {
switch (ast.type) {
case 'ENUM':
return ast
default:
return {...ast, standaloneName: undefined}
}
}

@@ -54,5 +54,5 @@ import {JSONSchema4Type, JSONSchema4TypeName} from 'json-schema'

{
$id: schema.$id,
allOf: [],
description: schema.description,
id: schema.id,
title: schema.title

@@ -251,3 +251,3 @@ },

params: (schema.type as JSONSchema4TypeName[]).map(type => {
const member: LinkedJSONSchema = {...omit(schema, 'description', 'id', 'title'), type}
const member: LinkedJSONSchema = {...omit(schema, '$id', 'description', 'title'), type}
return parse(maybeStripDefault(member as any), options, undefined, processed, usedNames)

@@ -305,3 +305,3 @@ }),

): string | undefined {
const name = schema.title || schema.id || keyNameFromDefinition
const name = schema.title || schema.$id || keyNameFromDefinition
if (name) {

@@ -308,0 +308,0 @@ return generateName(name, usedNames)

@@ -1,12 +0,24 @@

import $RefParser = require('json-schema-ref-parser')
import $RefParser = require('@apidevtools/json-schema-ref-parser')
import {JSONSchema} from './types/JSONSchema'
import {log} from './utils'
export type DereferencedPaths = WeakMap<$RefParser.JSONSchemaObject, string>
export async function dereference(
schema: JSONSchema,
{cwd, $refOptions}: {cwd: string; $refOptions: $RefParser.Options}
): Promise<JSONSchema> {
): Promise<{dereferencedPaths: DereferencedPaths; dereferencedSchema: JSONSchema}> {
log('green', 'dereferencer', 'Dereferencing input schema:', cwd, schema)
const parser = new $RefParser()
return parser.dereference(cwd, schema, $refOptions)
const dereferencedPaths: DereferencedPaths = new WeakMap()
const dereferencedSchema = await parser.dereference(cwd, schema as any, {
...$refOptions,
dereference: {
...$refOptions.dereference,
onDereference($ref, schema) {
dereferencedPaths.set(schema, $ref)
}
}
}) as any // TODO: fix types
return {dereferencedPaths, dereferencedSchema}
}

@@ -90,2 +90,5 @@ import {JSONSchema4, JSONSchema4Type, JSONSchema4TypeName} from 'json-schema'

required: string[]
// Removed by normalizer
id: never
}

@@ -118,11 +121,9 @@

export const getRootSchema = memoize(
(schema: LinkedJSONSchema): LinkedJSONSchema => {
const parent = schema[Parent]
if (!parent) {
return schema
}
return getRootSchema(parent)
export const getRootSchema = memoize((schema: LinkedJSONSchema): LinkedJSONSchema => {
const parent = schema[Parent]
if (!parent) {
return schema
}
)
return getRootSchema(parent)
})

@@ -129,0 +130,0 @@ export function isPrimitive(schema: LinkedJSONSchema | JSONSchemaType): schema is JSONSchemaType {

@@ -68,3 +68,4 @@ import {isPlainObject} from 'lodash'

NAMED_SCHEMA(schema) {
return 'id' in schema && ('patternProperties' in schema || 'properties' in schema)
// 8.2.1. The presence of "$id" in a subschema indicates that the subschema constitutes a distinct schema resource within a single schema document.
return '$id' in schema && ('patternProperties' in schema || 'properties' in schema)
},

@@ -71,0 +72,0 @@ NULL(schema) {

@@ -1,7 +0,6 @@

import {deburr, isPlainObject, mapValues, trim, upperFirst} from 'lodash'
import {basename, dirname, extname, join, normalize, sep} from 'path'
import {JSONSchema, LinkedJSONSchema} from './types/JSONSchema'
import {deburr, isPlainObject, trim, upperFirst} from 'lodash'
import {basename, dirname, extname, normalize, sep, posix} from 'path'
import {JSONSchema, LinkedJSONSchema, Parent} from './types/JSONSchema'
// TODO: pull out into a separate package
// eslint-disable-next-line
export function Try<T>(fn: () => T, err: (e: Error) => any): T {

@@ -15,21 +14,2 @@ try {

export function mapDeep(object: object, fn: (value: object, key?: string) => object, key?: string): object {
return fn(
mapValues(object, (_: unknown, key) => {
if (isPlainObject(_)) {
return mapDeep(_ as object, fn, key)
} else if (Array.isArray(_)) {
return _.map(item => {
if (isPlainObject(item)) {
return mapDeep(item as object, fn, key)
}
return item
})
}
return _
}),
key
)
}
// keys that shouldn't be traversed by the catchall step

@@ -74,3 +54,3 @@ const BLACKLISTED_KEYS = new Set([

obj: Record<string, LinkedJSONSchema>,
callback: (schema: LinkedJSONSchema) => void,
callback: (schema: LinkedJSONSchema, key: string | null) => void,
processed: Set<LinkedJSONSchema>

@@ -80,17 +60,20 @@ ) {

if (obj[k] && typeof obj[k] === 'object' && !Array.isArray(obj[k])) {
traverse(obj[k], callback, processed)
traverse(obj[k], callback, processed, k)
}
})
}
function traverseArray(
arr: LinkedJSONSchema[],
callback: (schema: LinkedJSONSchema) => void,
callback: (schema: LinkedJSONSchema, key: string | null) => void,
processed: Set<LinkedJSONSchema>
) {
arr.forEach(i => traverse(i, callback, processed))
arr.forEach((s, k) => traverse(s, callback, processed, k.toString()))
}
export function traverse(
schema: LinkedJSONSchema,
callback: (schema: LinkedJSONSchema) => void,
processed = new Set<LinkedJSONSchema>()
callback: (schema: LinkedJSONSchema, key: string | null) => void,
processed = new Set<LinkedJSONSchema>(),
key?: string
): void {

@@ -103,3 +86,3 @@ // Handle recursive schemas

processed.add(schema)
callback(schema)
callback(schema, key ?? null)

@@ -298,3 +281,3 @@ if (schema.anyOf) {

return join(normalize(outputPath), ...filePathRel)
return posix.join(posix.normalize(outputPath), ...filePathRel)
}

@@ -351,3 +334,3 @@

/**
* Removes the schema's `id`, `name`, and `description` properties
* Removes the schema's `$id`, `name`, and `description` properties
* if they exist.

@@ -359,8 +342,8 @@ * Useful when parsing intersections.

export function maybeStripNameHints(schema: JSONSchema): JSONSchema {
if ('$id' in schema) {
delete schema.$id
}
if ('description' in schema) {
delete schema.description
}
if ('id' in schema) {
delete schema.id
}
if ('name' in schema) {

@@ -371,1 +354,36 @@ delete schema.name

}
export function appendToDescription(existingDescription: string | undefined, ...values: string[]): string {
if (existingDescription) {
return `${existingDescription}\n\n${values.join('\n')}`
}
return values.join('\n')
}
export function isSchemaLike(schema: LinkedJSONSchema) {
if (!isPlainObject(schema)) {
return false
}
const parent = schema[Parent]
if (parent === null) {
return true
}
const JSON_SCHEMA_KEYWORDS = [
'allOf',
'anyOf',
'dependencies',
'enum',
'oneOf',
'definitions',
'not',
'patternProperties',
'properties',
'required'
]
if (JSON_SCHEMA_KEYWORDS.some(_ => parent[_] === schema)) {
return false
}
return true
}

@@ -1,3 +0,3 @@

import {JSONSchema} from './types/JSONSchema'
import {mapDeep} from './utils'
import {JSONSchema, LinkedJSONSchema} from './types/JSONSchema'
import {traverse} from './utils'

@@ -40,6 +40,6 @@ type Rule = (schema: JSONSchema) => boolean | void

export function validate(schema: JSONSchema, filename: string): string[] {
export function validate(schema: LinkedJSONSchema, filename: string): string[] {
const errors: string[] = []
rules.forEach((rule, ruleName) => {
mapDeep(schema, (schema, key) => {
traverse(schema, (schema, key) => {
if (rule(schema) === false) {

@@ -46,0 +46,0 @@ errors.push(`Error at key "${key}" in file "${filename}": ${ruleName}`)

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc