json-schema-to-typescript
Advanced tools
Comparing version
# 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
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
1347200
0.86%38299
0.63%253
34.57%15
36.36%18
12.5%3
50%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed