Skip to content

Commit 9f88141

Browse files
enhancement bcherny#543
added `--usePrefixItems` cli argument. When passed, JSON schemata containing `prefixItems` tuple validation key will parsed correctly.
1 parent 6adcad9 commit 9f88141

File tree

1 file changed

+189
-189
lines changed

1 file changed

+189
-189
lines changed

src/cli.ts

Lines changed: 189 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -1,189 +1,189 @@
1-
#!/usr/bin/env node
2-
3-
import minimist = require('minimist')
4-
import getStdin from 'get-stdin'
5-
import {readFile, writeFile, existsSync, lstatSync, readdirSync} from 'mz/fs'
6-
import * as mkdirp from 'mkdirp'
7-
import glob from 'glob-promise'
8-
import isGlob = require('is-glob')
9-
import {join, resolve, dirname, basename} from 'path'
10-
import {compile, DEFAULT_OPTIONS, Options} from './index'
11-
import {pathTransform, error} from './utils'
12-
13-
main(
14-
minimist(process.argv.slice(2), {
15-
alias: {
16-
help: ['h'],
17-
input: ['i'],
18-
output: ['o'],
19-
},
20-
boolean: [
21-
'additionalProperties',
22-
'declareExternallyReferenced',
23-
'enableConstEnums',
24-
'format',
25-
'ignoreMinAndMaxItems',
26-
'strictIndexSignatures',
27-
'unknownAny',
28-
'unreachableDefinitions',
29-
],
30-
default: DEFAULT_OPTIONS,
31-
string: ['bannerComment', 'cwd'],
32-
}),
33-
)
34-
35-
async function main(argv: minimist.ParsedArgs) {
36-
if (argv.help) {
37-
printHelp()
38-
process.exit(0)
39-
}
40-
41-
const argIn: string = argv._[0] || argv.input
42-
const argOut: string | undefined = argv._[1] || argv.output // the output can be omitted so this can be undefined
43-
44-
const ISGLOB = isGlob(argIn)
45-
const ISDIR = isDir(argIn)
46-
47-
if ((ISGLOB || ISDIR) && argOut && argOut.includes('.d.ts')) {
48-
throw new ReferenceError(
49-
`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)`,
50-
)
51-
}
52-
53-
try {
54-
// Process input as either glob, directory, or single file
55-
if (ISGLOB) {
56-
await processGlob(argIn, argOut, argv as Partial<Options>)
57-
} else if (ISDIR) {
58-
await processDir(argIn, argOut, argv as Partial<Options>)
59-
} else {
60-
const result = await processFile(argIn, argv as Partial<Options>)
61-
outputResult(result, argOut)
62-
}
63-
} catch (e) {
64-
error(e)
65-
process.exit(1)
66-
}
67-
}
68-
69-
// check if path is an existing directory
70-
function isDir(path: string): boolean {
71-
return existsSync(path) && lstatSync(path).isDirectory()
72-
}
73-
74-
async function processGlob(argIn: string, argOut: string | undefined, argv: Partial<Options>) {
75-
const files = await glob(argIn) // execute glob pattern match
76-
77-
if (files.length === 0) {
78-
throw ReferenceError(
79-
`You passed a glob pattern "${argIn}", but there are no files that match that pattern in ${process.cwd()}`,
80-
)
81-
}
82-
83-
// we can do this concurrently for perf
84-
const results = await Promise.all(
85-
files.map(async file => {
86-
return [file, await processFile(file, argv)] as const
87-
}),
88-
)
89-
90-
// careful to do this serially
91-
results.forEach(([file, result]) => {
92-
const outputPath = argOut && `${argOut}/${basename(file, '.json')}.d.ts`
93-
outputResult(result, outputPath)
94-
})
95-
}
96-
97-
async function processDir(argIn: string, argOut: string | undefined, argv: Partial<Options>) {
98-
const files = getPaths(argIn)
99-
100-
// we can do this concurrently for perf
101-
const results = await Promise.all(
102-
files.map(async file => {
103-
if (!argOut) {
104-
return [file, await processFile(file, argv)] as const
105-
} else {
106-
const outputPath = pathTransform(argOut, argIn, file)
107-
return [file, await processFile(file, argv), outputPath] as const
108-
}
109-
}),
110-
)
111-
112-
// careful to do this serially
113-
results.forEach(([file, result, outputPath]) =>
114-
outputResult(result, outputPath ? `${outputPath}/${basename(file, '.json')}.d.ts` : undefined),
115-
)
116-
}
117-
118-
async function outputResult(result: string, outputPath: string | undefined): Promise<void> {
119-
if (!outputPath) {
120-
process.stdout.write(result)
121-
} else {
122-
if (!isDir(dirname(outputPath))) {
123-
mkdirp.sync(dirname(outputPath))
124-
}
125-
return await writeFile(outputPath, result)
126-
}
127-
}
128-
129-
async function processFile(argIn: string, argv: Partial<Options>): Promise<string> {
130-
const schema = JSON.parse(await readInput(argIn))
131-
return compile(schema, argIn, argv)
132-
}
133-
134-
function getPaths(path: string, paths: string[] = []) {
135-
if (existsSync(path) && lstatSync(path).isDirectory()) {
136-
readdirSync(resolve(path)).forEach(item => getPaths(join(path, item), paths))
137-
} else {
138-
paths.push(path)
139-
}
140-
141-
return paths
142-
}
143-
144-
function readInput(argIn?: string) {
145-
if (!argIn) {
146-
return getStdin()
147-
}
148-
return readFile(resolve(process.cwd(), argIn), 'utf-8')
149-
}
150-
151-
function printHelp() {
152-
const pkg = require('../../package.json')
153-
154-
process.stdout.write(
155-
`
156-
${pkg.name} ${pkg.version}
157-
Usage: json2ts [--input, -i] [IN_FILE] [--output, -o] [OUT_FILE] [OPTIONS]
158-
159-
With no IN_FILE, or when IN_FILE is -, read standard input.
160-
With no OUT_FILE and when IN_FILE is specified, create .d.ts file in the same directory.
161-
With no OUT_FILE nor IN_FILE, write to standard output.
162-
163-
You can use any of the following options by adding them at the end.
164-
Boolean values can be set to false using the 'no-' prefix.
165-
166-
--additionalProperties
167-
Default value for additionalProperties, when it is not explicitly set
168-
--cwd=XXX
169-
Root directory for resolving $ref
170-
--declareExternallyReferenced
171-
Declare external schemas referenced via '$ref'?
172-
--enableConstEnums
173-
Prepend enums with 'const'?
174-
--format
175-
Format code? Set this to false to improve performance.
176-
--maxItems
177-
Maximum number of unioned tuples to emit when representing bounded-size
178-
array types, before falling back to emitting unbounded arrays. Increase
179-
this to improve precision of emitted types, decrease it to improve
180-
performance, or set it to -1 to ignore minItems and maxItems.
181-
--style.XXX=YYY
182-
Prettier configuration
183-
--unknownAny
184-
Output unknown type instead of any type
185-
--unreachableDefinitions
186-
Generates code for definitions that aren't referenced by the schema
187-
`,
188-
)
189-
}
1+
#!/usr/bin/env node
2+
import minimist = require('minimist')
3+
import getStdin from 'get-stdin'
4+
import {readFile, writeFile, existsSync, lstatSync, readdirSync} from 'mz/fs'
5+
import * as mkdirp from 'mkdirp'
6+
import glob from 'glob-promise'
7+
import isGlob = require('is-glob')
8+
import {join, resolve, dirname, basename} from 'path'
9+
import {compile, DEFAULT_OPTIONS, Options} from './index'
10+
import {pathTransform, error} from './utils'
11+
12+
main(
13+
minimist(process.argv.slice(2), {
14+
alias: {
15+
help: ['h'],
16+
input: ['i'],
17+
output: ['o'],
18+
},
19+
boolean: [
20+
'additionalProperties',
21+
'declareExternallyReferenced',
22+
'enableConstEnums',
23+
'format',
24+
'ignoreMinAndMaxItems',
25+
'strictIndexSignatures',
26+
'unknownAny',
27+
'unreachableDefinitions',
28+
'usePrefixItems',
29+
],
30+
default: DEFAULT_OPTIONS,
31+
string: ['bannerComment', 'cwd'],
32+
}),
33+
)
34+
35+
async function main(argv: minimist.ParsedArgs) {
36+
if (argv.help) {
37+
printHelp()
38+
process.exit(0)
39+
}
40+
41+
const argIn: string = argv._[0] || argv.input
42+
const argOut: string | undefined = argv._[1] || argv.output // the output can be omitted so this can be undefined
43+
44+
const ISGLOB = isGlob(argIn)
45+
const ISDIR = isDir(argIn)
46+
47+
if ((ISGLOB || ISDIR) && argOut && argOut.includes('.d.ts')) {
48+
throw new ReferenceError(
49+
`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)`,
50+
)
51+
}
52+
53+
try {
54+
// Process input as either glob, directory, or single file
55+
if (ISGLOB) {
56+
await processGlob(argIn, argOut, argv as Partial<Options>)
57+
} else if (ISDIR) {
58+
await processDir(argIn, argOut, argv as Partial<Options>)
59+
} else {
60+
const result = await processFile(argIn, argv as Partial<Options>)
61+
outputResult(result, argOut)
62+
}
63+
} catch (e) {
64+
error(e)
65+
process.exit(1)
66+
}
67+
}
68+
69+
// check if path is an existing directory
70+
function isDir(path: string): boolean {
71+
return existsSync(path) && lstatSync(path).isDirectory()
72+
}
73+
74+
async function processGlob(argIn: string, argOut: string | undefined, argv: Partial<Options>) {
75+
const files = await glob(argIn) // execute glob pattern match
76+
77+
if (files.length === 0) {
78+
throw ReferenceError(
79+
`You passed a glob pattern "${argIn}", but there are no files that match that pattern in ${process.cwd()}`,
80+
)
81+
}
82+
83+
// we can do this concurrently for perf
84+
const results = await Promise.all(
85+
files.map(async file => {
86+
return [file, await processFile(file, argv)] as const
87+
}),
88+
)
89+
90+
// careful to do this serially
91+
results.forEach(([file, result]) => {
92+
const outputPath = argOut && `${argOut}/${basename(file, '.json')}.d.ts`
93+
outputResult(result, outputPath)
94+
})
95+
}
96+
97+
async function processDir(argIn: string, argOut: string | undefined, argv: Partial<Options>) {
98+
const files = getPaths(argIn)
99+
100+
// we can do this concurrently for perf
101+
const results = await Promise.all(
102+
files.map(async file => {
103+
if (!argOut) {
104+
return [file, await processFile(file, argv)] as const
105+
} else {
106+
const outputPath = pathTransform(argOut, argIn, file)
107+
return [file, await processFile(file, argv), outputPath] as const
108+
}
109+
}),
110+
)
111+
112+
// careful to do this serially
113+
results.forEach(([file, result, outputPath]) =>
114+
outputResult(result, outputPath ? `${outputPath}/${basename(file, '.json')}.d.ts` : undefined),
115+
)
116+
}
117+
118+
async function outputResult(result: string, outputPath: string | undefined): Promise<void> {
119+
if (!outputPath) {
120+
process.stdout.write(result)
121+
} else {
122+
if (!isDir(dirname(outputPath))) {
123+
mkdirp.sync(dirname(outputPath))
124+
}
125+
return await writeFile(outputPath, result)
126+
}
127+
}
128+
129+
async function processFile(argIn: string, argv: Partial<Options>): Promise<string> {
130+
const schema = JSON.parse(await readInput(argIn))
131+
return compile(schema, argIn, argv)
132+
}
133+
134+
function getPaths(path: string, paths: string[] = []) {
135+
if (existsSync(path) && lstatSync(path).isDirectory()) {
136+
readdirSync(resolve(path)).forEach(item => getPaths(join(path, item), paths))
137+
} else {
138+
paths.push(path)
139+
}
140+
141+
return paths
142+
}
143+
144+
function readInput(argIn?: string) {
145+
if (!argIn) {
146+
return getStdin()
147+
}
148+
return readFile(resolve(process.cwd(), argIn), 'utf-8')
149+
}
150+
151+
function printHelp() {
152+
const pkg = require('../../package.json')
153+
154+
process.stdout.write(
155+
`
156+
${pkg.name} ${pkg.version}
157+
Usage: json2ts [--input, -i] [IN_FILE] [--output, -o] [OUT_FILE] [OPTIONS]
158+
159+
With no IN_FILE, or when IN_FILE is -, read standard input.
160+
With no OUT_FILE and when IN_FILE is specified, create .d.ts file in the same directory.
161+
With no OUT_FILE nor IN_FILE, write to standard output.
162+
163+
You can use any of the following options by adding them at the end.
164+
Boolean values can be set to false using the 'no-' prefix.
165+
166+
--additionalProperties
167+
Default value for additionalProperties, when it is not explicitly set
168+
--cwd=XXX
169+
Root directory for resolving $ref
170+
--declareExternallyReferenced
171+
Declare external schemas referenced via '$ref'?
172+
--enableConstEnums
173+
Prepend enums with 'const'?
174+
--format
175+
Format code? Set this to false to improve performance.
176+
--maxItems
177+
Maximum number of unioned tuples to emit when representing bounded-size
178+
array types, before falling back to emitting unbounded arrays. Increase
179+
this to improve precision of emitted types, decrease it to improve
180+
performance, or set it to -1 to ignore minItems and maxItems.
181+
--style.XXX=YYY
182+
Prettier configuration
183+
--unknownAny
184+
Output unknown type instead of any type
185+
--unreachableDefinitions
186+
Generates code for definitions that aren't referenced by the schema
187+
`,
188+
)
189+
}

0 commit comments

Comments
 (0)