Skip to content

Commit 4c1bd1f

Browse files
committed
bugfix: make declareExternallyReferenced option work consistently
1 parent 31993de commit 4c1bd1f

File tree

6 files changed

+92
-40
lines changed

6 files changed

+92
-40
lines changed

src/generator.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ function declareEnums(ast: AST, options: Options, processed = new Set<AST>()): s
3535
}
3636

3737
processed.add(ast)
38-
let type = ''
38+
39+
if (ast.isExternalSchema && !options.declareExternallyReferenced) {
40+
return ''
41+
}
3942

4043
switch (ast.type) {
4144
case 'ENUM':
@@ -46,7 +49,7 @@ function declareEnums(ast: AST, options: Options, processed = new Set<AST>()): s
4649
case 'INTERSECTION':
4750
return ast.params.reduce((prev, ast) => prev + declareEnums(ast, options, processed), '')
4851
case 'TUPLE':
49-
type = ast.params.reduce((prev, ast) => prev + declareEnums(ast, options, processed), '')
52+
let type = ast.params.reduce((prev, ast) => prev + declareEnums(ast, options, processed), '')
5053
if (ast.spreadParam) {
5154
type += declareEnums(ast.spreadParam, options, processed)
5255
}
@@ -66,15 +69,17 @@ function declareNamedInterfaces(ast: AST, options: Options, rootASTName: string,
6669
processed.add(ast)
6770
let type = ''
6871

72+
if (ast.isExternalSchema && !options.declareExternallyReferenced) {
73+
return ''
74+
}
75+
6976
switch (ast.type) {
7077
case 'ARRAY':
7178
type = declareNamedInterfaces((ast as TArray).params, options, rootASTName, processed)
7279
break
7380
case 'INTERFACE':
7481
type = [
75-
hasStandaloneName(ast) &&
76-
(ast.standaloneName === rootASTName || options.declareExternallyReferenced) &&
77-
generateStandaloneInterface(ast, options),
82+
hasStandaloneName(ast) && generateStandaloneInterface(ast, options),
7883
getSuperTypesAndParams(ast)
7984
.map(ast => declareNamedInterfaces(ast, options, rootASTName, processed))
8085
.filter(Boolean)
@@ -108,6 +113,10 @@ function declareNamedTypes(ast: AST, options: Options, rootASTName: string, proc
108113

109114
processed.add(ast)
110115

116+
if (ast.isExternalSchema && !options.declareExternallyReferenced) {
117+
return ''
118+
}
119+
111120
switch (ast.type) {
112121
case 'ARRAY':
113122
return [
@@ -120,11 +129,7 @@ function declareNamedTypes(ast: AST, options: Options, rootASTName: string, proc
120129
return ''
121130
case 'INTERFACE':
122131
return getSuperTypesAndParams(ast)
123-
.map(
124-
ast =>
125-
(ast.standaloneName === rootASTName || options.declareExternallyReferenced) &&
126-
declareNamedTypes(ast, options, rootASTName, processed),
127-
)
132+
.map(ast => declareNamedTypes(ast, options, rootASTName, processed))
128133
.filter(Boolean)
129134
.join('\n')
130135
case 'INTERSECTION':

src/normalizer.ts

+22-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {JSONSchemaTypeName, LinkedJSONSchema, NormalizedJSONSchema, Parent} from './types/JSONSchema'
1+
import {IsExternalSchema, JSONSchemaTypeName, LinkedJSONSchema, NormalizedJSONSchema, Parent} from './types/JSONSchema'
22
import {appendToDescription, escapeBlockComment, isSchemaLike, justName, toSafeString, traverse} from './utils'
33
import {Options} from './'
44
import {DereferencedPaths} from './resolver'
@@ -73,6 +73,27 @@ rules.set('Transform id to $id', (schema, fileName) => {
7373
}
7474
})
7575

76+
rules.set(
77+
'Add an ExternalRef flag to anything that needs it',
78+
(schema, _fileName, _options, _key, dereferencedPaths) => {
79+
if (!isSchemaLike(schema)) {
80+
return
81+
}
82+
83+
// Top-level schema
84+
if (!schema[Parent]) {
85+
return
86+
}
87+
88+
const dereferencedName = dereferencedPaths.get(schema)
89+
Object.defineProperty(schema, IsExternalSchema, {
90+
enumerable: false,
91+
value: dereferencedName && !dereferencedName.startsWith('#'),
92+
writable: false,
93+
})
94+
},
95+
)
96+
7697
rules.set('Add an $id to anything that needs it', (schema, fileName, _options, _key, dereferencedPaths) => {
7798
if (!isSchemaLike(schema)) {
7899
return
@@ -95,10 +116,6 @@ rules.set('Add an $id to anything that needs it', (schema, fileName, _options, _
95116
if (!schema.$id && !schema.title && dereferencedName) {
96117
schema.$id = toSafeString(justName(dereferencedName))
97118
}
98-
99-
if (dereferencedName) {
100-
dereferencedPaths.delete(schema)
101-
}
102119
})
103120

104121
rules.set('Escape closing JSDoc comment', schema => {

src/parser.ts

+41-18
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ import {
2323
JSONSchemaWithDefinitions,
2424
SchemaSchema,
2525
SchemaType,
26+
IsExternalSchema,
27+
NormalizedJSONSchema,
28+
Parent,
2629
} from './types/JSONSchema'
2730
import {generateName, log, maybeStripDefault, maybeStripNameHints} from './utils'
2831

@@ -31,7 +34,7 @@ export type Processed = Map<LinkedJSONSchema, Map<SchemaType, AST>>
3134
export type UsedNames = Set<string>
3235

3336
export function parse(
34-
schema: LinkedJSONSchema | JSONSchema4Type,
37+
schema: NormalizedJSONSchema | JSONSchema4Type,
3538
options: Options,
3639
keyName?: string,
3740
processed: Processed = new Map(),
@@ -54,19 +57,17 @@ export function parse(
5457

5558
// Be careful to first process the intersection before processing its params,
5659
// so that it gets first pick for standalone name.
57-
const ast = parseAsTypeWithCache(
58-
{
59-
$id: schema.$id,
60-
allOf: [],
61-
description: schema.description,
62-
title: schema.title,
63-
},
64-
'ALL_OF',
65-
options,
66-
keyName,
67-
processed,
68-
usedNames,
69-
) as TIntersection
60+
const allOf: NormalizedJSONSchema = {
61+
[IsExternalSchema]: schema[IsExternalSchema],
62+
[Parent]: schema[Parent],
63+
$id: schema.$id,
64+
allOf: [],
65+
description: schema.description,
66+
title: schema.title,
67+
additionalProperties: schema.additionalProperties,
68+
required: schema.required,
69+
}
70+
const ast = parseAsTypeWithCache(allOf, 'ALL_OF', options, keyName, processed, usedNames) as TIntersection
7071

7172
ast.params = types.map(type =>
7273
// We hoist description (for comment) and id/title (for standaloneName)
@@ -79,7 +80,7 @@ export function parse(
7980
}
8081

8182
function parseAsTypeWithCache(
82-
schema: LinkedJSONSchema,
83+
schema: NormalizedJSONSchema,
8384
type: SchemaType,
8485
options: Options,
8586
keyName?: string,
@@ -111,27 +112,30 @@ function parseAsTypeWithCache(
111112
function parseBooleanSchema(schema: boolean, keyName: string | undefined, options: Options): AST {
112113
if (schema) {
113114
return {
115+
isExternalSchema: false,
114116
keyName,
115117
type: options.unknownAny ? 'UNKNOWN' : 'ANY',
116118
}
117119
}
118120

119121
return {
122+
isExternalSchema: false,
120123
keyName,
121124
type: 'NEVER',
122125
}
123126
}
124127

125128
function parseLiteral(schema: JSONSchema4Type, keyName: string | undefined): AST {
126129
return {
130+
isExternalSchema: false,
127131
keyName,
128132
params: schema,
129133
type: 'LITERAL',
130134
}
131135
}
132136

133137
function parseNonLiteral(
134-
schema: LinkedJSONSchema,
138+
schema: NormalizedJSONSchema,
135139
type: SchemaType,
136140
options: Options,
137141
keyName: string | undefined,
@@ -146,6 +150,7 @@ function parseNonLiteral(
146150
return {
147151
comment: schema.description,
148152
deprecated: schema.deprecated,
153+
isExternalSchema: schema[IsExternalSchema],
149154
keyName,
150155
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
151156
params: schema.allOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
@@ -156,13 +161,15 @@ function parseNonLiteral(
156161
...(options.unknownAny ? T_UNKNOWN : T_ANY),
157162
comment: schema.description,
158163
deprecated: schema.deprecated,
164+
isExternalSchema: schema[IsExternalSchema],
159165
keyName,
160166
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
161167
}
162168
case 'ANY_OF':
163169
return {
164170
comment: schema.description,
165171
deprecated: schema.deprecated,
172+
isExternalSchema: schema[IsExternalSchema],
166173
keyName,
167174
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
168175
params: schema.anyOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
@@ -172,6 +179,7 @@ function parseNonLiteral(
172179
return {
173180
comment: schema.description,
174181
deprecated: schema.deprecated,
182+
isExternalSchema: schema[IsExternalSchema],
175183
keyName,
176184
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
177185
type: 'BOOLEAN',
@@ -180,6 +188,7 @@ function parseNonLiteral(
180188
return {
181189
comment: schema.description,
182190
deprecated: schema.deprecated,
191+
isExternalSchema: schema[IsExternalSchema],
183192
keyName,
184193
params: schema.tsType!,
185194
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
@@ -189,9 +198,10 @@ function parseNonLiteral(
189198
return {
190199
comment: schema.description,
191200
deprecated: schema.deprecated,
201+
isExternalSchema: schema[IsExternalSchema],
192202
keyName,
193203
standaloneName: standaloneName(schema, keyNameFromDefinition ?? keyName, usedNames, options)!,
194-
params: schema.enum!.map((_, n) => ({
204+
params: schema.enum!.map((_: any, n: number) => ({
195205
ast: parseLiteral(_, undefined),
196206
keyName: schema.tsEnumNames![n],
197207
})),
@@ -203,6 +213,7 @@ function parseNonLiteral(
203213
return {
204214
comment: schema.description,
205215
deprecated: schema.deprecated,
216+
isExternalSchema: schema[IsExternalSchema],
206217
keyName,
207218
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
208219
type: 'NEVER',
@@ -211,6 +222,7 @@ function parseNonLiteral(
211222
return {
212223
comment: schema.description,
213224
deprecated: schema.deprecated,
225+
isExternalSchema: schema[IsExternalSchema],
214226
keyName,
215227
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
216228
type: 'NULL',
@@ -219,13 +231,15 @@ function parseNonLiteral(
219231
return {
220232
comment: schema.description,
221233
deprecated: schema.deprecated,
234+
isExternalSchema: schema[IsExternalSchema],
222235
keyName,
223236
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
224237
type: 'NUMBER',
225238
}
226239
case 'OBJECT':
227240
return {
228241
comment: schema.description,
242+
isExternalSchema: schema[IsExternalSchema],
229243
keyName,
230244
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
231245
type: 'OBJECT',
@@ -235,6 +249,7 @@ function parseNonLiteral(
235249
return {
236250
comment: schema.description,
237251
deprecated: schema.deprecated,
252+
isExternalSchema: schema[IsExternalSchema],
238253
keyName,
239254
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
240255
params: schema.oneOf!.map(_ => parse(_, options, undefined, processed, usedNames)),
@@ -246,6 +261,7 @@ function parseNonLiteral(
246261
return {
247262
comment: schema.description,
248263
deprecated: schema.deprecated,
264+
isExternalSchema: schema[IsExternalSchema],
249265
keyName,
250266
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
251267
type: 'STRING',
@@ -258,6 +274,7 @@ function parseNonLiteral(
258274
const arrayType: TTuple = {
259275
comment: schema.description,
260276
deprecated: schema.deprecated,
277+
isExternalSchema: schema[IsExternalSchema],
261278
keyName,
262279
maxItems,
263280
minItems,
@@ -275,6 +292,7 @@ function parseNonLiteral(
275292
return {
276293
comment: schema.description,
277294
deprecated: schema.deprecated,
295+
isExternalSchema: schema[IsExternalSchema],
278296
keyName,
279297
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
280298
params: parse(schema.items!, options, `{keyNameFromDefinition}Items`, processed, usedNames),
@@ -285,6 +303,7 @@ function parseNonLiteral(
285303
return {
286304
comment: schema.description,
287305
deprecated: schema.deprecated,
306+
isExternalSchema: schema[IsExternalSchema],
288307
keyName,
289308
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
290309
params: (schema.type as JSONSchema4TypeName[]).map(type => {
@@ -297,9 +316,10 @@ function parseNonLiteral(
297316
return {
298317
comment: schema.description,
299318
deprecated: schema.deprecated,
319+
isExternalSchema: schema[IsExternalSchema],
300320
keyName,
301321
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
302-
params: schema.enum!.map(_ => parseLiteral(_, undefined)),
322+
params: schema.enum!.map((_: any) => parseLiteral(_, undefined)),
303323
type: 'UNION',
304324
}
305325
case 'UNNAMED_SCHEMA':
@@ -313,6 +333,7 @@ function parseNonLiteral(
313333
return {
314334
comment: schema.description,
315335
deprecated: schema.deprecated,
336+
isExternalSchema: schema[IsExternalSchema],
316337
keyName,
317338
maxItems: schema.maxItems,
318339
minItems,
@@ -328,6 +349,7 @@ function parseNonLiteral(
328349
return {
329350
comment: schema.description,
330351
deprecated: schema.deprecated,
352+
isExternalSchema: schema[IsExternalSchema],
331353
keyName,
332354
params,
333355
standaloneName: standaloneName(schema, keyNameFromDefinition, usedNames, options),
@@ -364,6 +386,7 @@ function newInterface(
364386
return {
365387
comment: schema.description,
366388
deprecated: schema.deprecated,
389+
isExternalSchema: schema[IsExternalSchema],
367390
keyName,
368391
params: parseSchema(schema, options, processed, usedNames, name),
369392
standaloneName: name,

src/types/AST.ts

+5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export type AST =
2424

2525
export interface AbstractAST {
2626
comment?: string
27+
isExternalSchema: boolean
2728
keyName?: string
2829
standaloneName?: string
2930
type: AST_TYPE
@@ -154,18 +155,22 @@ export interface TCustomType extends AbstractAST {
154155

155156
export const T_ANY: TAny = {
156157
type: 'ANY',
158+
isExternalSchema: false,
157159
}
158160

159161
export const T_ANY_ADDITIONAL_PROPERTIES: TAny & ASTWithName = {
160162
keyName: '[k: string]',
161163
type: 'ANY',
164+
isExternalSchema: false,
162165
}
163166

164167
export const T_UNKNOWN: TUnknown = {
165168
type: 'UNKNOWN',
169+
isExternalSchema: false,
166170
}
167171

168172
export const T_UNKNOWN_ADDITIONAL_PROPERTIES: TUnknown & ASTWithName = {
169173
keyName: '[k: string]',
170174
type: 'UNKNOWN',
175+
isExternalSchema: false,
171176
}

0 commit comments

Comments
 (0)