@@ -247,6 +247,42 @@ function makeNullable(def: Definition) {
247
247
return def ;
248
248
}
249
249
250
+ /**
251
+ * Given a Symbol, returns a canonical Definition. That can be either:
252
+ * 1) The Symbol's valueDeclaration parameter if defined, or
253
+ * 2) The sole entry in the Symbol's declarations array, provided that array has a length of 1.
254
+ *
255
+ * valueDeclaration is listed as a required parameter in the definition of a Symbol, but I've
256
+ * experienced crashes when it's undefined at runtime, which is the reason for this function's
257
+ * existence. Not sure if that's a compiler API bug or what.
258
+ */
259
+ function getCanonicalDeclaration ( sym : ts . Symbol ) : ts . Declaration {
260
+ if ( sym . valueDeclaration !== undefined ) {
261
+ return sym . valueDeclaration ;
262
+ } else if ( sym . declarations . length === 1 ) {
263
+ return sym . declarations [ 0 ] ;
264
+ }
265
+
266
+ throw new Error ( `Symbol "${ sym . name } " has no valueDeclaration and ${ sym . declarations . length } declarations.` ) ;
267
+ }
268
+
269
+ /**
270
+ * Given a Symbol, finds the place it was declared and chases parent pointers until we find a
271
+ * node where SyntaxKind === SourceFile.
272
+ */
273
+ function getSourceFile ( sym : ts . Symbol ) : ts . SourceFile {
274
+ let currentDecl : ts . Node = getCanonicalDeclaration ( sym ) ;
275
+
276
+ while ( currentDecl . kind !== ts . SyntaxKind . SourceFile ) {
277
+ if ( currentDecl . parent === undefined ) {
278
+ throw new Error ( `Unable to locate source file for declaration "${ sym . name } ".` ) ;
279
+ }
280
+ currentDecl = currentDecl . parent ;
281
+ }
282
+
283
+ return currentDecl as ts . SourceFile ;
284
+ }
285
+
250
286
/**
251
287
* JSDoc keywords that should be used to annotate the JSON schema.
252
288
*
@@ -950,16 +986,30 @@ export class JsonSchemaGenerator {
950
986
951
987
let fullTypeName = "" ;
952
988
if ( asTypeAliasRef ) {
953
- fullTypeName = this . makeTypeNameUnique (
954
- typ ,
955
- this . tc . getFullyQualifiedName (
956
- reffedType ! . getFlags ( ) & ts . SymbolFlags . Alias ?
957
- this . tc . getAliasedSymbol ( reffedType ! ) :
958
- reffedType !
959
- ) . replace ( REGEX_FILE_NAME_OR_SPACE , "" )
960
- ) ;
989
+ const typeName = this . tc . getFullyQualifiedName (
990
+ reffedType ! . getFlags ( ) & ts . SymbolFlags . Alias ?
991
+ this . tc . getAliasedSymbol ( reffedType ! ) :
992
+ reffedType !
993
+ ) . replace ( REGEX_FILE_NAME_OR_SPACE , "" ) ;
994
+ if ( this . args . uniqueNames ) {
995
+ const sourceFile = getSourceFile ( reffedType ! ) ;
996
+ const relativePath = path . relative ( process . cwd ( ) , sourceFile . fileName ) ;
997
+ fullTypeName = `${ typeName } .${ generateHashOfNode ( getCanonicalDeclaration ( reffedType ! ) , relativePath ) } ` ;
998
+ } else {
999
+ fullTypeName = this . makeTypeNameUnique (
1000
+ typ ,
1001
+ typeName
1002
+ ) ;
1003
+ }
961
1004
} else if ( asRef ) {
962
- fullTypeName = this . getTypeName ( typ ) ;
1005
+ if ( this . args . uniqueNames ) {
1006
+ const sym = typ . symbol ;
1007
+ const sourceFile = getSourceFile ( sym ) ;
1008
+ const relativePath = path . relative ( process . cwd ( ) , sourceFile . fileName ) ;
1009
+ fullTypeName = `${ this . getTypeName ( typ ) } .${ generateHashOfNode ( getCanonicalDeclaration ( sym ) , relativePath ) } ` ;
1010
+ } else {
1011
+ fullTypeName = this . getTypeName ( typ ) ;
1012
+ }
963
1013
}
964
1014
965
1015
if ( asRef ) {
@@ -1236,6 +1286,13 @@ export function generateSchema(program: ts.Program, fullTypeName: string, args:
1236
1286
1237
1287
if ( fullTypeName === "*" ) { // All types in file(s)
1238
1288
return generator . getSchemaForSymbols ( generator . getMainFileSymbols ( program , onlyIncludeFiles ) ) ;
1289
+ } else if ( args . uniqueNames ) { // Find the hashed type name to use as the root object
1290
+ const matchingSymbols = generator . getSymbols ( fullTypeName ) ;
1291
+ if ( matchingSymbols . length === 1 ) {
1292
+ return generator . getSchemaForSymbol ( matchingSymbols [ 0 ] . name ) ;
1293
+ } else {
1294
+ throw new Error ( `${ matchingSymbols . length } definitions found for requested type "${ fullTypeName } ".` ) ;
1295
+ }
1239
1296
} else { // Use specific type as root object
1240
1297
return generator . getSchemaForSymbol ( fullTypeName ) ;
1241
1298
}
0 commit comments