Skip to content

Commit

Permalink
Add yaml support in cli (fix bcherny#598)
Browse files Browse the repository at this point in the history
  • Loading branch information
bcherny committed Jun 2, 2024
1 parent 5d24faa commit b7fee29
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 43 deletions.
23 changes: 15 additions & 8 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {readFile, writeFile, existsSync, lstatSync, readdirSync} from 'mz/fs'
import * as mkdirp from 'mkdirp'
import {glob} from 'glob'
import isGlob from 'is-glob'
import {join, resolve, dirname, basename} from 'path'
import {join, resolve, dirname} from 'path'
import {compile, DEFAULT_OPTIONS, Options} from './index'
import {pathTransform, error} from './utils'
import {pathTransform, error, parseFileAsJSONSchema, justName} from './utils'

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

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

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

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

async function processFile(argIn: string, argv: Partial<Options>): Promise<string> {
const schema = JSON.parse(await readInput(argIn))
const {filename, contents} = await readInput(argIn)
const schema = parseFileAsJSONSchema(filename, contents)
return compile(schema, argIn, argv)
}

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

async function readInput(argIn?: string): Promise<string> {
async function readInput(argIn?: string): Promise<{filename: string | null; contents: string}> {
if (!argIn) {
return readStream(process.stdin)
return {
filename: null,
contents: await readStream(process.stdin),
}
}
return {
filename: argIn,
contents: await readFile(resolve(process.cwd(), argIn), 'utf-8'),
}
return readFile(resolve(process.cwd(), argIn), 'utf-8')
}

async function readStream(stream: NodeJS.ReadStream): Promise<string> {
Expand Down
24 changes: 2 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@ import {normalize} from './normalizer'
import {optimize} from './optimizer'
import {parse} from './parser'
import {dereference} from './resolver'
import {error, stripExtension, Try, log} from './utils'
import {error, stripExtension, Try, log, parseFileAsJSONSchema} from './utils'
import {validate} from './validator'
import {isDeepStrictEqual} from 'util'
import {link} from './linker'
import {validateOptions} from './optionValidator'
import {JSONSchema as LinkedJSONSchema} from './types/JSONSchema'
import yaml from 'js-yaml'

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

Expand Down Expand Up @@ -125,26 +124,7 @@ function parseAsJSONSchema(filename: string): JSONSchema4 {
throw new ReferenceError(`Unable to read file "${filename}"`)
},
)

if (isYaml(filename)) {
return Try(
() => yaml.load(contents.toString()) as JSONSchema4,
() => {
throw new TypeError(`Error parsing YML in file "${filename}"`)
},
)
}

return Try(
() => JSON.parse(contents.toString()),
() => {
throw new TypeError(`Error parsing JSON in file "${filename}"`)
},
)
}

function isYaml(filename: string) {
return filename.endsWith('.yaml') || filename.endsWith('.yml')
return parseFileAsJSONSchema(filename, contents.toString())
}

export async function compile(schema: JSONSchema4, name: string, options: Partial<Options> = {}): Promise<string> {
Expand Down
24 changes: 24 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {deburr, isPlainObject, trim, upperFirst} from 'lodash'
import {basename, dirname, extname, normalize, sep, posix} from 'path'
import {JSONSchema, LinkedJSONSchema, Parent} from './types/JSONSchema'
import {JSONSchema4} from 'json-schema'
import yaml from 'js-yaml'

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

return true
}

export function parseFileAsJSONSchema(filename: string | null, contents: string): JSONSchema4 {
if (filename != null && isYaml(filename)) {
return Try(
() => yaml.load(contents.toString()) as JSONSchema4,
() => {
throw new TypeError(`Error parsing YML in file "${filename}"`)
},
)
}

return Try(
() => JSON.parse(contents.toString()),
() => {
throw new TypeError(`Error parsing JSON in file "${filename}"`)
},
)
}

function isYaml(filename: string) {
return filename.endsWith('.yaml') || filename.endsWith('.yml')
}
45 changes: 45 additions & 0 deletions test/__snapshots__/test/test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -449284,6 +449284,31 @@ Generated by [AVA](https://avajs.dev).

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

> Snapshot 1

`/* eslint-disable */␊
/**␊
* This file was automatically generated by json-schema-to-typescript.␊
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,␊
* and run json-schema-to-typescript to regenerate this file.␊
*/␊
export interface ExampleSchema {␊
firstName: string;␊
lastName: string;␊
/**␊
* Age in years␊
*/␊
age?: number;␊
height?: number;␊
favoriteFoods?: unknown[];␊
likesDogs?: boolean;␊
[k: string]: unknown;␊
}␊
`

## file in (yaml), pipe out

> Snapshot 1

`/* eslint-disable */␊
Expand Down Expand Up @@ -449522,6 +449547,26 @@ Generated by [AVA](https://avajs.dev).
}␊
`

> Snapshot 5

'./test/resources/MultiSchema/out/b.yaml.d.ts'

> Snapshot 6

`/* eslint-disable */␊
/**␊
* This file was automatically generated by json-schema-to-typescript.␊
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,␊
* and run json-schema-to-typescript to regenerate this file.␊
*/␊
export interface BSchema {␊
x?: string;␊
y: number;␊
[k: string]: unknown;␊
}␊
`

## files in (-i), pipe out

> Snapshot 1
Expand Down
Binary file modified test/__snapshots__/test/test.ts.snap
Binary file not shown.
10 changes: 0 additions & 10 deletions test/resources/MultiSchema/b.json

This file was deleted.

10 changes: 10 additions & 0 deletions test/resources/MultiSchema/b.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
title: B schema
type: object
properties:
x:
type: string
"y":
type: integer
additionalProperties: true
required:
- "y"
21 changes: 21 additions & 0 deletions test/resources/Schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
id: http://dummy.com/api/example-schema
title: Example Schema
type: object
properties:
firstName:
type: string
lastName:
type: string
age:
description: Age in years
type: integer
minimum: 0
height:
type: number
favoriteFoods:
type: array
likesDogs:
type: boolean
required:
- firstName
- lastName
12 changes: 9 additions & 3 deletions test/testCLI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ export function run() {
t.snapshot(execSync(`node dist/src/cli.js -i ${__dirname}/../../test/resources/ReferencedType.json`).toString())
})

test('file in (yaml), pipe out', t => {
t.snapshot(execSync('node dist/src/cli.js ./test/resources/Schema.yaml').toString())
})

test('pipe in, file out (--output)', t => {
execSync('shx cat ./test/resources/ReferencedType.json | node dist/src/cli.js --output ./ReferencedType.d.ts')
t.snapshot(readFileSync('./ReferencedType.d.ts', 'utf-8'))
Expand Down Expand Up @@ -92,7 +96,9 @@ export function run() {
})

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

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

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

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

0 comments on commit b7fee29

Please sign in to comment.