json-schema-to-typescript
Advanced tools
Comparing version 8.1.0 to 8.2.0
# Changelog | ||
## 8.2.0 | ||
- a0257d8 Add support for directories and globs as inputs (#238) | ||
## 8.1.0 | ||
@@ -4,0 +8,0 @@ |
@@ -42,5 +42,22 @@ #!/usr/bin/env node | ||
var fs_1 = require("mz/fs"); | ||
var _mkdirp = require("mkdirp"); | ||
var _glob = require("glob"); | ||
var isGlob = require("is-glob"); | ||
var util_1 = require("util"); | ||
var path_1 = require("path"); | ||
var stdin = require("stdin"); | ||
var index_1 = require("./index"); | ||
var utils_1 = require("./utils"); | ||
// Promisify mkdirp & glob | ||
var mkdirp = function (path) { | ||
return new Promise(function (res, rej) { | ||
_mkdirp(path, function (err, made) { | ||
if (err) | ||
rej(err); | ||
else | ||
res(made === null ? undefined : made); | ||
}); | ||
}); | ||
}; | ||
var glob = util_1.promisify(_glob); | ||
main(minimist(process.argv.slice(2), { | ||
@@ -55,5 +72,5 @@ alias: { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var argIn, argOut, schema, _a, _b, ts, e_1; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
var argIn, argOut, ISGLOB, ISDIR, e_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
@@ -65,23 +82,34 @@ if (argv.help) { | ||
argIn = argv._[0] || argv.input; | ||
argOut = argv._[1] || argv.output; | ||
_c.label = 1; | ||
argOut = argv._[1] || argv.output // the output can be omitted so this can be undefined | ||
; | ||
ISGLOB = isGlob(argIn); | ||
ISDIR = isDir(argIn); | ||
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)"); | ||
} | ||
_a.label = 1; | ||
case 1: | ||
_c.trys.push([1, 5, , 6]); | ||
_b = (_a = JSON).parse; | ||
return [4 /*yield*/, readInput(argIn)]; | ||
_a.trys.push([1, 8, , 9]); | ||
if (!ISGLOB) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, processGlob(argIn, argOut, argv)]; | ||
case 2: | ||
schema = _b.apply(_a, [_c.sent()]); | ||
return [4 /*yield*/, index_1.compile(schema, argIn, argv)]; | ||
_a.sent(); | ||
return [3 /*break*/, 7]; | ||
case 3: | ||
ts = _c.sent(); | ||
return [4 /*yield*/, writeOutput(ts, argOut)]; | ||
if (!ISDIR) return [3 /*break*/, 5]; | ||
return [4 /*yield*/, processDir(argIn, argOut, argv)]; | ||
case 4: | ||
_c.sent(); | ||
return [3 /*break*/, 6]; | ||
case 5: | ||
e_1 = _c.sent(); | ||
_a.sent(); | ||
return [3 /*break*/, 7]; | ||
case 5: return [4 /*yield*/, processFile(argIn, argOut, argv)]; | ||
case 6: | ||
_a.sent(); | ||
_a.label = 7; | ||
case 7: return [3 /*break*/, 9]; | ||
case 8: | ||
e_1 = _a.sent(); | ||
console.error(cli_color_1.whiteBright.bgRedBright('error'), e_1); | ||
process.exit(1); | ||
return [3 /*break*/, 6]; | ||
case 6: return [2 /*return*/]; | ||
return [3 /*break*/, 9]; | ||
case 9: return [2 /*return*/]; | ||
} | ||
@@ -91,2 +119,94 @@ }); | ||
} | ||
// check if path is an existing directory | ||
function isDir(path) { | ||
return fs_1.existsSync(path) && fs_1.lstatSync(path).isDirectory(); | ||
} | ||
function processGlob(argIn, argOut, argv) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var files; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, glob(argIn)]; // execute glob pattern match | ||
case 1: | ||
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()); | ||
} | ||
if (!(argOut && !fs_1.existsSync(argOut))) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, mkdirp(argOut)]; | ||
case 2: | ||
_a.sent(); | ||
_a.label = 3; | ||
case 3: return [4 /*yield*/, Promise.all(files.map(function (file) { | ||
var outPath = argOut && argOut + "/" + path_1.basename(file, '.json') + ".d.ts"; | ||
return processFile(file, outPath, argv); | ||
}))]; | ||
case 4: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
function processDir(argIn, argOut, argv) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var files; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
files = getPaths(argIn); | ||
return [4 /*yield*/, Promise.all(files.map(function (file) { | ||
if (!argOut) { | ||
return processFile(file, argOut, argv); | ||
} | ||
else { | ||
var outPath = utils_1.pathTransform(argOut, file); | ||
if (!isDir(path_1.dirname(outPath))) { | ||
_mkdirp.sync(path_1.dirname(outPath)); | ||
} | ||
outPath = outPath.replace('.json', '.d.ts'); | ||
return processFile(file, outPath, argv); | ||
} | ||
}))]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
function processFile(argIn, argOut, argv) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var schema, _a, _b, ts; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
_b = (_a = JSON).parse; | ||
return [4 /*yield*/, readInput(argIn)]; | ||
case 1: | ||
schema = _b.apply(_a, [_c.sent()]); | ||
return [4 /*yield*/, index_1.compile(schema, argIn, argv)]; | ||
case 2: | ||
ts = _c.sent(); | ||
if (!!argOut) return [3 /*break*/, 3]; | ||
process.stdout.write(ts); | ||
return [3 /*break*/, 5]; | ||
case 3: return [4 /*yield*/, fs_1.writeFile(argOut, ts)]; | ||
case 4: return [2 /*return*/, _c.sent()]; | ||
case 5: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
} | ||
function getPaths(path, paths) { | ||
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); }); | ||
} | ||
else { | ||
paths.push(path); | ||
} | ||
return paths; | ||
} | ||
function readInput(argIn) { | ||
@@ -98,14 +218,2 @@ if (!argIn) { | ||
} | ||
function writeOutput(ts, argOut) { | ||
if (!argOut) { | ||
try { | ||
process.stdout.write(ts); | ||
return Promise.resolve(); | ||
} | ||
catch (err) { | ||
return Promise.reject(err); | ||
} | ||
} | ||
return fs_1.writeFile(argOut, ts); | ||
} | ||
function printHelp() { | ||
@@ -112,0 +220,0 @@ var pkg = require('../../package.json'); |
@@ -31,1 +31,2 @@ import { JSONSchema } from './types/JSONSchema'; | ||
export declare function escapeBlockComment(schema: JSONSchema): void; | ||
export declare function pathTransform(o: string, i: string): string; |
@@ -231,2 +231,23 @@ "use strict"; | ||
exports.escapeBlockComment = escapeBlockComment; | ||
/* | ||
the following logic determines the out path by comparing the in path to the users specified out path. | ||
For example, if input directory MultiSchema looks like: | ||
MultiSchema/foo/a.json | ||
MultiSchema/bar/fuzz/c.json | ||
MultiSchema/bar/d.json | ||
And the user wants the outputs to be in MultiSchema/Out, then this code will be able to map the inner directories foo, bar, and fuzz into the intended Out directory like so: | ||
MultiSchema/Out/foo/a.json | ||
MultiSchema/Out/bar/fuzz/c.json | ||
MultiSchema/Out/bar/d.json | ||
*/ | ||
function pathTransform(o, i) { | ||
var outPathList = o.split('/'); | ||
var inPathList = i.split('/'); | ||
var intersection = outPathList.filter(function (x) { return inPathList.includes(x); }); | ||
var symmetricDifference = outPathList | ||
.filter(function (x) { return !inPathList.includes(x); }) | ||
.concat(inPathList.filter(function (x) { return !outPathList.includes(x); })); | ||
return path_1.join.apply(void 0, intersection.concat(symmetricDifference)); | ||
} | ||
exports.pathTransform = pathTransform; | ||
//# sourceMappingURL=utils.js.map |
{ | ||
"name": "json-schema-to-typescript", | ||
"version": "8.1.0", | ||
"version": "8.2.0", | ||
"description": "compile json schema to typescript typings", | ||
@@ -48,6 +48,9 @@ "main": "dist/src/index.js", | ||
"dependencies": { | ||
"@types/is-glob": "^4.0.1", | ||
"@types/json-schema": "^7.0.3", | ||
"@types/node": ">=4.5.0", | ||
"@types/mkdirp": "^0.5.2", | ||
"@types/prettier": "^1.16.1", | ||
"cli-color": "^1.4.0", | ||
"glob": "^7.1.4", | ||
"is-glob": "^4.0.1", | ||
"json-schema-ref-parser": "^6.1.0", | ||
@@ -57,2 +60,3 @@ "json-stringify-safe": "^5.0.1", | ||
"minimist": "^1.2.0", | ||
"mkdirp": "^0.5.1", | ||
"mz": "^2.7.0", | ||
@@ -64,5 +68,7 @@ "prettier": "^1.19.1", | ||
"@types/cli-color": "^0.3.29", | ||
"@types/glob": "^7.1.1", | ||
"@types/lodash": "^4.14.121", | ||
"@types/minimist": "^1.2.0", | ||
"@types/mz": "0.0.32", | ||
"@types/node": "^12.12.29", | ||
"@typescript-eslint/eslint-plugin": "^2.9.0", | ||
@@ -69,0 +75,0 @@ "@typescript-eslint/parser": "^2.9.0", |
@@ -97,3 +97,3 @@ # json-schema-to-typescript [![Build Status][build]](https://circleci.com/gh/bcherny/json-schema-to-typescript) [![npm]](https://www.npmjs.com/package/json-schema-to-typescript) [![mit]](https://opensource.org/licenses/MIT) | ||
A simple CLI utility is provided with this package. | ||
A CLI utility is provided with this package. | ||
@@ -121,2 +121,67 @@ ```sh | ||
The CLI supports directory of definitions as well. It supports directory paths, glob patterns, and output directories. | ||
Example 1: Directory of type definitions to an output directory | ||
Input Directory | ||
``` | ||
schemas / | ||
| a.json | ||
| b.json | ||
``` | ||
```sh | ||
json2ts -i schemas/ -o types/ | ||
``` | ||
Output Directory | ||
``` | ||
types / | ||
| a.d.ts | ||
| b.d.ts | ||
``` | ||
Example 2: Directory to pipe out | ||
Input Directory | ||
``` | ||
schemas / | ||
| a.json | ||
| b.json | ||
``` | ||
```sh | ||
json2ts -i schemas/ | ||
``` | ||
Example 3: Nested input directory mapped to nested output | ||
Input Directory | ||
``` | ||
schemas / | ||
foo / | ||
| a.json | ||
bar / | ||
| b.json | ||
fuzz / | ||
c.json | ||
buzz / | ||
d.json | ||
``` | ||
```sh | ||
json2ts -i schemas/ -o types/ | ||
``` | ||
Output Directory | ||
``` | ||
types / | ||
foo / | ||
| a.d.ts | ||
bar / | ||
| b.d.ts | ||
fuzz / | ||
c.d.ts | ||
buzz / | ||
d.d.ts | ||
``` | ||
## Tests | ||
@@ -123,0 +188,0 @@ |
121
src/cli.ts
#!/usr/bin/env node | ||
import {whiteBright} from 'cli-color' | ||
import {JSONSchema4} from 'json-schema' | ||
import minimist = require('minimist') | ||
import {readFile, writeFile} from 'mz/fs' | ||
import {resolve} from 'path' | ||
import {readFile, writeFile, existsSync, lstatSync, readdirSync} from 'mz/fs' | ||
import * as _mkdirp from 'mkdirp' | ||
import * as _glob from 'glob' | ||
import isGlob = require('is-glob') | ||
import {promisify} from 'util' | ||
import {join, resolve, dirname, basename} from 'path' | ||
import stdin = require('stdin') | ||
import {compile, Options} from './index' | ||
import {pathTransform} from './utils' | ||
// Promisify mkdirp & glob | ||
const mkdirp = (path: string): Promise<_mkdirp.Made> => | ||
new Promise((res, rej) => { | ||
_mkdirp(path, (err, made) => { | ||
if (err) rej(err) | ||
else res(made === null ? undefined : made) | ||
}) | ||
}) | ||
const glob = promisify(_glob) | ||
main( | ||
@@ -28,8 +43,22 @@ minimist(process.argv.slice(2), { | ||
const argIn: string = argv._[0] || argv.input | ||
const argOut: string = argv._[1] || argv.output | ||
const argOut: string | undefined = argv._[1] || argv.output // the output can be omitted so this can be undefined | ||
const ISGLOB = isGlob(argIn) | ||
const ISDIR = isDir(argIn) | ||
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)` | ||
) | ||
} | ||
try { | ||
const schema: JSONSchema4 = JSON.parse(await readInput(argIn)) | ||
const ts = await compile(schema, argIn, argv as Partial<Options>) | ||
await writeOutput(ts, argOut) | ||
// Process input as either glob, directory, or single file | ||
if (ISGLOB) { | ||
await processGlob(argIn, argOut, argv as Partial<Options>) | ||
} else if (ISDIR) { | ||
await processDir(argIn, argOut, argv as Partial<Options>) | ||
} else { | ||
await processFile(argIn, argOut, argv as Partial<Options>) | ||
} | ||
} catch (e) { | ||
@@ -41,2 +70,68 @@ console.error(whiteBright.bgRedBright('error'), e) | ||
// check if path is an existing directory | ||
function isDir(path: string): boolean { | ||
return existsSync(path) && lstatSync(path).isDirectory() | ||
} | ||
async function processGlob(argIn: string, argOut: string | undefined, argv: Partial<Options>) { | ||
const files = await glob(argIn) // 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()}` | ||
) | ||
} | ||
// create output directory if it does not exist | ||
if (argOut && !existsSync(argOut)) { | ||
await mkdirp(argOut) | ||
} | ||
await Promise.all( | ||
files.map(file => { | ||
const outPath = argOut && `${argOut}/${basename(file, '.json')}.d.ts` | ||
return processFile(file, outPath, argv) | ||
}) | ||
) | ||
} | ||
async function processDir(argIn: string, argOut: string | undefined, argv: Partial<Options>) { | ||
const files = getPaths(argIn) | ||
await Promise.all( | ||
files.map(file => { | ||
if (!argOut) { | ||
return processFile(file, argOut, argv) | ||
} else { | ||
let outPath = pathTransform(argOut, file) | ||
if (!isDir(dirname(outPath))) { | ||
_mkdirp.sync(dirname(outPath)) | ||
} | ||
outPath = outPath.replace('.json', '.d.ts') | ||
return processFile(file, outPath, argv) | ||
} | ||
}) | ||
) | ||
} | ||
async function processFile(argIn: string, argOut: string | undefined, argv: Partial<Options>): Promise<void> { | ||
const schema = JSON.parse(await readInput(argIn)) | ||
const ts = await compile(schema, argIn, argv) | ||
if (!argOut) { | ||
process.stdout.write(ts) | ||
} else { | ||
return await writeFile(argOut, ts) | ||
} | ||
} | ||
function getPaths(path: string, paths: string[] = []) { | ||
if (existsSync(path) && lstatSync(path).isDirectory()) { | ||
readdirSync(resolve(path)).forEach(item => getPaths(join(path, item), paths)) | ||
} else { | ||
paths.push(path) | ||
} | ||
return paths | ||
} | ||
function readInput(argIn?: string) { | ||
@@ -49,14 +144,2 @@ if (!argIn) { | ||
function writeOutput(ts: string, argOut: string): Promise<void> { | ||
if (!argOut) { | ||
try { | ||
process.stdout.write(ts) | ||
return Promise.resolve() | ||
} catch (err) { | ||
return Promise.reject(err) | ||
} | ||
} | ||
return writeFile(argOut, ts) | ||
} | ||
function printHelp() { | ||
@@ -63,0 +146,0 @@ const pkg = require('../../package.json') |
import {whiteBright} from 'cli-color' | ||
import {deburr, isPlainObject, mapValues, trim, upperFirst} from 'lodash' | ||
import {basename, extname} from 'path' | ||
import {basename, extname, join} from 'path' | ||
import {JSONSchema} from './types/JSONSchema' | ||
@@ -224,1 +224,24 @@ | ||
} | ||
/* | ||
the following logic determines the out path by comparing the in path to the users specified out path. | ||
For example, if input directory MultiSchema looks like: | ||
MultiSchema/foo/a.json | ||
MultiSchema/bar/fuzz/c.json | ||
MultiSchema/bar/d.json | ||
And the user wants the outputs to be in MultiSchema/Out, then this code will be able to map the inner directories foo, bar, and fuzz into the intended Out directory like so: | ||
MultiSchema/Out/foo/a.json | ||
MultiSchema/Out/bar/fuzz/c.json | ||
MultiSchema/Out/bar/d.json | ||
*/ | ||
export function pathTransform(o: string, i: string): string { | ||
const outPathList = o.split('/') | ||
const inPathList = i.split('/') | ||
const intersection = outPathList.filter(x => inPathList.includes(x)) | ||
const symmetricDifference = outPathList | ||
.filter(x => !inPathList.includes(x)) | ||
.concat(inPathList.filter(x => !outPathList.includes(x))) | ||
return join(...intersection, ...symmetricDifference) | ||
} |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1347200
38299
253
15
18
+ Added@types/is-glob@^4.0.1
+ Added@types/mkdirp@^0.5.2
+ Addedglob@^7.1.4
+ Addedis-glob@^4.0.1
+ Addedmkdirp@^0.5.1
+ Added@types/is-glob@4.0.4(transitive)
+ Added@types/mkdirp@0.5.2(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedglob@7.2.3(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedis-extglob@2.1.1(transitive)
+ Addedis-glob@4.0.3(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedmkdirp@0.5.6(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedwrappy@1.0.2(transitive)
- Removed@types/node@>=4.5.0