Skip to content

Commit b7fee29

Browse files
committedJun 2, 2024
Add yaml support in cli (fix #598)
1 parent 5d24faa commit b7fee29

File tree

9 files changed

+126
-43
lines changed

9 files changed

+126
-43
lines changed
 

‎src/cli.ts

+15-8
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import {readFile, writeFile, existsSync, lstatSync, readdirSync} from 'mz/fs'
55
import * as mkdirp from 'mkdirp'
66
import {glob} from 'glob'
77
import isGlob from 'is-glob'
8-
import {join, resolve, dirname, basename} from 'path'
8+
import {join, resolve, dirname} from 'path'
99
import {compile, DEFAULT_OPTIONS, Options} from './index'
10-
import {pathTransform, error} from './utils'
10+
import {pathTransform, error, parseFileAsJSONSchema, justName} from './utils'
1111

1212
main(
1313
minimist(process.argv.slice(2), {
@@ -88,7 +88,7 @@ async function processGlob(argIn: string, argOut: string | undefined, argv: Part
8888

8989
// careful to do this serially
9090
results.forEach(([file, result]) => {
91-
const outputPath = argOut && `${argOut}/${basename(file, '.json')}.d.ts`
91+
const outputPath = argOut && `${argOut}/${justName(file)}.d.ts`
9292
outputResult(result, outputPath)
9393
})
9494
}
@@ -110,7 +110,7 @@ async function processDir(argIn: string, argOut: string | undefined, argv: Parti
110110

111111
// careful to do this serially
112112
results.forEach(([file, result, outputPath]) =>
113-
outputResult(result, outputPath ? `${outputPath}/${basename(file, '.json')}.d.ts` : undefined),
113+
outputResult(result, outputPath ? `${outputPath}/${justName(file)}.d.ts` : undefined),
114114
)
115115
}
116116

@@ -126,7 +126,8 @@ async function outputResult(result: string, outputPath: string | undefined): Pro
126126
}
127127

128128
async function processFile(argIn: string, argv: Partial<Options>): Promise<string> {
129-
const schema = JSON.parse(await readInput(argIn))
129+
const {filename, contents} = await readInput(argIn)
130+
const schema = parseFileAsJSONSchema(filename, contents)
130131
return compile(schema, argIn, argv)
131132
}
132133

@@ -140,11 +141,17 @@ function getPaths(path: string, paths: string[] = []) {
140141
return paths
141142
}
142143

143-
async function readInput(argIn?: string): Promise<string> {
144+
async function readInput(argIn?: string): Promise<{filename: string | null; contents: string}> {
144145
if (!argIn) {
145-
return readStream(process.stdin)
146+
return {
147+
filename: null,
148+
contents: await readStream(process.stdin),
149+
}
150+
}
151+
return {
152+
filename: argIn,
153+
contents: await readFile(resolve(process.cwd(), argIn), 'utf-8'),
146154
}
147-
return readFile(resolve(process.cwd(), argIn), 'utf-8')
148155
}
149156

150157
async function readStream(stream: NodeJS.ReadStream): Promise<string> {

‎src/index.ts

+2-22
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ import {normalize} from './normalizer'
1010
import {optimize} from './optimizer'
1111
import {parse} from './parser'
1212
import {dereference} from './resolver'
13-
import {error, stripExtension, Try, log} from './utils'
13+
import {error, stripExtension, Try, log, parseFileAsJSONSchema} from './utils'
1414
import {validate} from './validator'
1515
import {isDeepStrictEqual} from 'util'
1616
import {link} from './linker'
1717
import {validateOptions} from './optionValidator'
1818
import {JSONSchema as LinkedJSONSchema} from './types/JSONSchema'
19-
import yaml from 'js-yaml'
2019

2120
export {EnumJSONSchema, JSONSchema, NamedEnumJSONSchema, CustomTypeJSONSchema} from './types/JSONSchema'
2221

@@ -125,26 +124,7 @@ function parseAsJSONSchema(filename: string): JSONSchema4 {
125124
throw new ReferenceError(`Unable to read file "${filename}"`)
126125
},
127126
)
128-
129-
if (isYaml(filename)) {
130-
return Try(
131-
() => yaml.load(contents.toString()) as JSONSchema4,
132-
() => {
133-
throw new TypeError(`Error parsing YML in file "${filename}"`)
134-
},
135-
)
136-
}
137-
138-
return Try(
139-
() => JSON.parse(contents.toString()),
140-
() => {
141-
throw new TypeError(`Error parsing JSON in file "${filename}"`)
142-
},
143-
)
144-
}
145-
146-
function isYaml(filename: string) {
147-
return filename.endsWith('.yaml') || filename.endsWith('.yml')
127+
return parseFileAsJSONSchema(filename, contents.toString())
148128
}
149129

150130
export async function compile(schema: JSONSchema4, name: string, options: Partial<Options> = {}): Promise<string> {

‎src/utils.ts

+24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {deburr, isPlainObject, trim, upperFirst} from 'lodash'
22
import {basename, dirname, extname, normalize, sep, posix} from 'path'
33
import {JSONSchema, LinkedJSONSchema, Parent} from './types/JSONSchema'
4+
import {JSONSchema4} from 'json-schema'
5+
import yaml from 'js-yaml'
46

57
// TODO: pull out into a separate package
68
export function Try<T>(fn: () => T, err: (e: Error) => any): T {
@@ -384,3 +386,25 @@ export function isSchemaLike(schema: LinkedJSONSchema) {
384386

385387
return true
386388
}
389+
390+
export function parseFileAsJSONSchema(filename: string | null, contents: string): JSONSchema4 {
391+
if (filename != null && isYaml(filename)) {
392+
return Try(
393+
() => yaml.load(contents.toString()) as JSONSchema4,
394+
() => {
395+
throw new TypeError(`Error parsing YML in file "${filename}"`)
396+
},
397+
)
398+
}
399+
400+
return Try(
401+
() => JSON.parse(contents.toString()),
402+
() => {
403+
throw new TypeError(`Error parsing JSON in file "${filename}"`)
404+
},
405+
)
406+
}
407+
408+
function isYaml(filename: string) {
409+
return filename.endsWith('.yaml') || filename.endsWith('.yml')
410+
}

‎test/__snapshots__/test/test.ts.md

+45
Original file line numberDiff line numberDiff line change
@@ -449284,6 +449284,31 @@ Generated by [AVA](https://avajs.dev).
449284449284

449285449285
## file in (-i), pipe out (absolute path)
449286449286

449287+
> Snapshot 1
449288+
449289+
`/* eslint-disable */␊
449290+
/**␊
449291+
* This file was automatically generated by json-schema-to-typescript.␊
449292+
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,␊
449293+
* and run json-schema-to-typescript to regenerate this file.␊
449294+
*/␊
449295+
449296+
export interface ExampleSchema {␊
449297+
firstName: string;␊
449298+
lastName: string;␊
449299+
/**␊
449300+
* Age in years␊
449301+
*/␊
449302+
age?: number;␊
449303+
height?: number;␊
449304+
favoriteFoods?: unknown[];␊
449305+
likesDogs?: boolean;␊
449306+
[k: string]: unknown;␊
449307+
}␊
449308+
`
449309+
449310+
## file in (yaml), pipe out
449311+
449287449312
> Snapshot 1
449288449313

449289449314
`/* eslint-disable */␊
@@ -449522,6 +449547,26 @@ Generated by [AVA](https://avajs.dev).
449522449547
}␊
449523449548
`
449524449549

449550+
> Snapshot 5
449551+
449552+
'./test/resources/MultiSchema/out/b.yaml.d.ts'
449553+
449554+
> Snapshot 6
449555+
449556+
`/* eslint-disable */␊
449557+
/**␊
449558+
* This file was automatically generated by json-schema-to-typescript.␊
449559+
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,␊
449560+
* and run json-schema-to-typescript to regenerate this file.␊
449561+
*/␊
449562+
449563+
export interface BSchema {␊
449564+
x?: string;␊
449565+
y: number;␊
449566+
[k: string]: unknown;␊
449567+
}␊
449568+
`
449569+
449525449570
## files in (-i), pipe out
449526449571

449527449572
> Snapshot 1

‎test/__snapshots__/test/test.ts.snap

34 Bytes
Binary file not shown.

‎test/resources/MultiSchema/b.json

-10
This file was deleted.

‎test/resources/MultiSchema/b.yaml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
title: B schema
2+
type: object
3+
properties:
4+
x:
5+
type: string
6+
"y":
7+
type: integer
8+
additionalProperties: true
9+
required:
10+
- "y"

‎test/resources/Schema.yaml

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
id: http://dummy.com/api/example-schema
2+
title: Example Schema
3+
type: object
4+
properties:
5+
firstName:
6+
type: string
7+
lastName:
8+
type: string
9+
age:
10+
description: Age in years
11+
type: integer
12+
minimum: 0
13+
height:
14+
type: number
15+
favoriteFoods:
16+
type: array
17+
likesDogs:
18+
type: boolean
19+
required:
20+
- firstName
21+
- lastName

‎test/testCLI.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ export function run() {
4747
t.snapshot(execSync(`node dist/src/cli.js -i ${__dirname}/../../test/resources/ReferencedType.json`).toString())
4848
})
4949

50+
test('file in (yaml), pipe out', t => {
51+
t.snapshot(execSync('node dist/src/cli.js ./test/resources/Schema.yaml').toString())
52+
})
53+
5054
test('pipe in, file out (--output)', t => {
5155
execSync('shx cat ./test/resources/ReferencedType.json | node dist/src/cli.js --output ./ReferencedType.d.ts')
5256
t.snapshot(readFileSync('./ReferencedType.d.ts', 'utf-8'))
@@ -92,7 +96,9 @@ export function run() {
9296
})
9397

9498
test('files in (-i), files out (-o)', t => {
95-
execSync(`node dist/src/cli.js -i "./test/resources/MultiSchema/**/*.json" -o ./test/resources/MultiSchema/out`)
99+
execSync(
100+
`node dist/src/cli.js -i "./test/resources/MultiSchema/**/*.{json,yaml,yml}" -o ./test/resources/MultiSchema/out`,
101+
)
96102

97103
readdirSync('./test/resources/MultiSchema/out').forEach(f => {
98104
const path = `./test/resources/MultiSchema/out/${f}`
@@ -104,12 +110,12 @@ export function run() {
104110
})
105111

106112
test('files in (-i), pipe out', t => {
107-
t.snapshot(execSync(`node dist/src/cli.js -i "./test/resources/MultiSchema/**/*.json"`).toString())
113+
t.snapshot(execSync(`node dist/src/cli.js -i "./test/resources/MultiSchema/**/*.{json,yaml,yml}"`).toString())
108114
})
109115

110116
test('files in (-i), files out (-o) nested dir does not exist', t => {
111117
execSync(
112-
`node dist/src/cli.js -i "./test/resources/MultiSchema/**/*.json" -o ./test/resources/MultiSchema/foo/bar/out`,
118+
`node dist/src/cli.js -i "./test/resources/MultiSchema/**/*.{json,yaml,yml}" -o ./test/resources/MultiSchema/foo/bar/out`,
113119
)
114120
readdirSync('./test/resources/MultiSchema/foo/bar/out').forEach(f => {
115121
const path = `./test/resources/MultiSchema/foo/bar/out/${f}`

0 commit comments

Comments
 (0)
Failed to load comments.