Skip to content
This repository was archived by the owner on Jun 22, 2023. It is now read-only.

Commit 69905fe

Browse files
committed
fix cycles - 1st cut
1 parent 8643dc4 commit 69905fe

File tree

9 files changed

+270
-226
lines changed

9 files changed

+270
-226
lines changed

.vscode/launch.json

+14-17
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,19 @@
22
"version": "0.2.5",
33
"configurations": [
44
{
5-
// Name of configuration; appears in the launch configuration drop down menu.
6-
"name": "Run mocha",
7-
// Type of configuration. Possible values: "node", "mono".
8-
"type": "node",
9-
// Workspace relative or absolute path to the program.
10-
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
11-
// Automatically stop program after launch.
12-
"stopOnEntry": false,
13-
// Command line arguments passed to the program.
14-
"args": ["out/test/test.js"],
15-
// Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace.
16-
"cwd": "${workspaceRoot}",
17-
// Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH.
18-
"runtimeExecutable": null,
19-
// Environment variables passed to the program.
20-
"env": { "NODE_ENV": "production"}
21-
}
5+
"type": "node",
6+
"request": "launch",
7+
"name": "Launch Program",
8+
"program": "${workspaceRoot}/node_modules/.bin/ava ./dist/test/test.js",
9+
"cwd": "${workspaceRoot}"
10+
},
11+
{
12+
"type": "node",
13+
"request": "attach",
14+
"name": "Attach to Process",
15+
"processId": "${command:PickProcess}",
16+
"port": 5858,
17+
"sourceMaps": true
18+
}
2219
]
2320
}

src/generator.ts

+17-5
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,28 @@ function declareNamedInterfaces(ast: AST, options: Options, rootASTName: string,
4646
}
4747
}
4848

49-
function declareNamedTypes(ast: AST, options: Options): string {
49+
let last: AST | null = null
50+
function declareNamedTypes(ast: AST, options: Options, processed = new Map<AST, string>()): string {
51+
console.log('declareNamedTypes', processed.has(ast), ast, ast === last)
52+
last = ast
53+
if (processed.has(ast)) {
54+
return processed.get(ast)!
55+
}
56+
processed.set(ast, '[PLACEHOLDER]')
57+
let type = ''
5058
switch (ast.type) {
51-
case 'ENUM': return ''
52-
case 'INTERFACE': return ast.params.map(_ => declareNamedTypes(_, options)).filter(Boolean).join('\n')
59+
case 'ENUM':
60+
type = ''
61+
break
62+
case 'INTERFACE':
63+
type = ast.params.map(_ => declareNamedTypes(_, options, processed)).filter(Boolean).join('\n')
64+
break
5365
default:
5466
if (hasStandaloneName(ast)) {
55-
return generateStandaloneType(ast, options)
67+
type = generateStandaloneType(ast, options)
5668
}
57-
return ''
5869
}
70+
return type
5971
}
6072

6173
function generateType(ast: AST, options: Options): string {

src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { dirname } from 'path'
44
import { generate } from './generator'
55
import { normalize } from './normalizer'
66
import { parse } from './parser'
7-
import { dereference } from './refResolver'
7+
import { dereference } from './resolver'
88
import { error, stripExtension, Try } from './utils'
99
import { validate } from './validator'
1010

src/parser.ts

+68-16
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,47 @@ export function parse(
1010
schema: JSONSchema,
1111
name?: string,
1212
rootSchema: JSONSchema = schema,
13-
isRequired = false
13+
isRequired = false,
14+
processed = new Map<JSONSchema, AST>()
15+
): AST {
16+
log(whiteBright.bgBlue('parser'), schema, '<-' + typeOfSchema(schema), processed.has(schema) ? '(CACHED)' : '')
17+
if (processed.has(schema)) {
18+
return processed.get(schema)!
19+
}
20+
const ast = parseSchema(schema, name, rootSchema, isRequired, processed)
21+
processed.set(schema, ast)
22+
return ast
23+
}
24+
25+
function parseSchema(
26+
schema: JSONSchema,
27+
name: string | undefined,
28+
rootSchema: JSONSchema,
29+
isRequired: boolean,
30+
processed: Map<JSONSchema, AST>
1431
): AST {
15-
log(whiteBright.bgBlue('parser'), JSON.stringify(schema, null, 2), '<-' + typeOfSchema(schema), name)
1632
switch (typeOfSchema(schema)) {
1733
case 'ALL_OF':
1834
// TODO: support schema.properties
19-
return { comment: schema.description, name, isRequired, params: schema.allOf!.map(_ => parse(_)), standaloneName: schema.title, type: 'INTERSECTION' }
35+
return {
36+
comment: schema.description,
37+
isRequired,
38+
name,
39+
get params() { return schema.allOf!.map(_ => parse(_, undefined, rootSchema, undefined, processed)) },
40+
standaloneName: schema.title,
41+
type: 'INTERSECTION'
42+
}
2043
case 'ANY':
2144
return { comment: schema.description, name, isRequired, standaloneName: schema.title, type: 'ANY' }
2245
case 'ANY_OF':
23-
return { comment: schema.description, name, isRequired, params: schema.anyOf!.map(_ => parse(_)), standaloneName: schema.title, type: 'UNION' }
46+
return {
47+
comment: schema.description,
48+
isRequired,
49+
name,
50+
get params() { return schema.anyOf!.map(_ => parse(_, undefined, rootSchema, undefined, processed)) },
51+
standaloneName: schema.title,
52+
type: 'UNION'
53+
}
2454
case 'BOOLEAN':
2555
return { comment: schema.description, name, isRequired, standaloneName: schema.title, type: 'BOOLEAN' }
2656
case 'LITERAL':
@@ -30,7 +60,7 @@ export function parse(
3060
comment: schema.description,
3161
isRequired,
3262
name,
33-
params: schema.enum!.map((_, n) => parse(_, schema.tsEnumNames![n], rootSchema) as ASTWithName),
63+
get params() { return schema.enum!.map((_, n) => parse(_, schema.tsEnumNames![n], rootSchema, undefined, processed) as ASTWithName) },
3464
standaloneName: name!,
3565
type: 'ENUM'
3666
}
@@ -39,7 +69,7 @@ export function parse(
3969
comment: schema.description,
4070
isRequired,
4171
name,
42-
params: parseSchemaSchema(schema as SchemaSchema, rootSchema),
72+
get params() { return parseSchemaSchema(schema as SchemaSchema, rootSchema, processed) },
4373
standaloneName: computeSchemaName(schema as SchemaSchema, name),
4474
type: 'INTERFACE'
4575
}
@@ -50,21 +80,35 @@ export function parse(
5080
case 'OBJECT':
5181
return { comment: schema.description, name, isRequired, standaloneName: schema.title, type: 'OBJECT' }
5282
case 'REFERENCE':
53-
return parse(resolveReference(schema.$ref as string, rootSchema), '', schema)
83+
return parse(resolveReference(schema.$ref as string, rootSchema), '', schema, undefined, processed)
5484
case 'STRING':
5585
return { comment: schema.description, name, isRequired, standaloneName: schema.title, type: 'STRING' }
5686
case 'TYPED_ARRAY':
5787
if (Array.isArray(schema.items)) {
58-
return { comment: schema.description, name, isRequired, params: schema.items.map(_ => parse(_)), standaloneName: schema.title, type: 'TUPLE' }
88+
return {
89+
comment: schema.description,
90+
name,
91+
isRequired,
92+
get params() { return schema.items.map(_ => parse(_, undefined, rootSchema, undefined, processed)) },
93+
standaloneName: schema.title,
94+
type: 'TUPLE'
95+
}
5996
} else {
60-
return { comment: schema.description, name, isRequired, params: parse(schema.items!), standaloneName: schema.title, type: 'ARRAY' }
97+
return {
98+
comment: schema.description,
99+
name,
100+
isRequired,
101+
get params() { return parse(schema.items!, undefined, rootSchema, undefined, processed) },
102+
standaloneName: schema.title,
103+
type: 'ARRAY'
104+
}
61105
}
62106
case 'UNION':
63107
return {
64108
comment: schema.description,
65109
name,
66110
isRequired,
67-
params: (schema.type as JSONSchema4TypeName[]).map(_ => parse({ required: [], type: _ })),
111+
get params() { return (schema.type as JSONSchema4TypeName[]).map(_ => parse({ required: [], type: _ }, undefined, rootSchema, undefined, processed)) },
68112
standaloneName: schema.title,
69113
type: 'UNION'
70114
}
@@ -73,7 +117,7 @@ export function parse(
73117
comment: schema.description,
74118
isRequired,
75119
name,
76-
params: schema.enum!.map(_ => parse(_)),
120+
get params() { return schema.enum!.map(_ => parse(_, undefined, rootSchema, undefined, processed)) },
77121
standaloneName: schema.title,
78122
type: 'UNION'
79123
}
@@ -82,12 +126,19 @@ export function parse(
82126
comment: schema.description,
83127
isRequired,
84128
name,
85-
params: parseSchemaSchema(schema as SchemaSchema, rootSchema),
129+
get params() { return parseSchemaSchema(schema as SchemaSchema, rootSchema, processed) },
86130
standaloneName: computeSchemaName(schema as SchemaSchema, name),
87131
type: 'INTERFACE'
88132
}
89133
case 'UNTYPED_ARRAY':
90-
return { comment: schema.description, name, isRequired, params: T_ANY, standaloneName: schema.title, type: 'ARRAY' }
134+
return {
135+
comment: schema.description,
136+
name,
137+
isRequired,
138+
params: T_ANY,
139+
standaloneName: schema.title,
140+
type: 'ARRAY'
141+
}
91142
}
92143
}
93144

@@ -106,10 +157,11 @@ function computeSchemaName(
106157
*/
107158
function parseSchemaSchema(
108159
schema: SchemaSchema,
109-
rootSchema: JSONSchema
160+
rootSchema: JSONSchema,
161+
processed: Map<JSONSchema, AST>
110162
): ASTWithName[] {
111163
const asts = map(schema.properties, (value, key) =>
112-
parse(value, key, rootSchema, (schema.required || []).includes(key!)) as ASTWithName
164+
parse(value, key, rootSchema, (schema.required || []).includes(key!), processed) as ASTWithName
113165
)
114166
// handle additionalProperties
115167
switch (schema.additionalProperties) {
@@ -123,7 +175,7 @@ function parseSchemaSchema(
123175
// pass "true" as the last param because in TS, properties
124176
// defined via index signatures are already optional
125177
default:
126-
return asts.concat(parse(schema.additionalProperties, '[k: string]', rootSchema, true) as ASTWithName)
178+
return asts.concat(parse(schema.additionalProperties, '[k: string]', rootSchema, true, processed) as ASTWithName)
127179
}
128180
}
129181

src/refResolver.ts

-44
This file was deleted.

src/resolver.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import $RefParser = require('json-schema-ref-parser')
2+
import { whiteBright } from 'cli-color'
3+
import { JSONSchema } from './types/JSONSchema'
4+
import { log } from './utils'
5+
6+
export async function dereference(schema: JSONSchema, cwd: string): Promise<JSONSchema> {
7+
log(whiteBright.bgGreen('resolver'), schema, cwd)
8+
const parser = new $RefParser
9+
return parser.dereference(schema, cwd)
10+
}

src/types/AST.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export type AST = TAny | TArray | TBoolean | TEnum | TInterface
88
| TIntersection | TLiteral | TNumber | TNull | TObject | TReference
99
| TString | TTuple | TUnion
1010

11-
interface AbstractAST {
11+
export interface AbstractAST {
1212
comment?: string
1313
isRequired: boolean
1414
name?: string

0 commit comments

Comments
 (0)