2022-01-11 21:53:42 +00:00
/ * *
* In some cases it is desirable to override the exported implementation and constructor for a symbol .
* This is useful for a few reasons :
* - A symbol could have multiple constructors
* - It is possible to disambiguate multiple different signatures from a single polymorphic constructor
* - The return type of the overridden constructor can differ ( e . g . ` Foo<T|null> ` vs ` Foo<T> ` )
2022-08-20 08:58:20 +00:00
*
2022-01-11 21:53:42 +00:00
* This looks like the following :
2022-08-20 08:58:20 +00:00
*
2022-01-11 21:53:42 +00:00
* ` ` `
2021-11-30 19:46:46 +00:00
* / * *
* * @ overriddenImplementation FooCtor
* * \ /
2022-01-11 21:53:42 +00:00
* export interface Foo {
* bar ( ) ;
* }
2022-08-20 08:58:20 +00:00
*
2021-11-30 19:46:46 +00:00
* type FooInterface = Foo ;
2022-08-20 08:58:20 +00:00
*
2021-11-30 19:46:46 +00:00
* export class FooImpl { ... }
2022-08-20 08:58:20 +00:00
*
2022-01-11 21:53:42 +00:00
* export interface FooCtor {
* new ( ) : Foo ;
* }
2022-08-20 08:58:20 +00:00
*
2021-11-30 19:46:46 +00:00
* export const Foo : FooCtor =
( class Foo implements FooInterface { ... }
2022-01-11 21:53:42 +00:00
* ` ` `
2022-08-20 08:58:20 +00:00
*
2021-11-30 19:46:46 +00:00
* This processor will extend the docs for symbol ` Foo ` by copying all documented constructor overrides from ` FooCtor ` .
2022-08-20 08:58:20 +00:00
*
2021-11-30 19:46:46 +00:00
* In order to use this processor , annotate the exported interface with ` @overriddenImplementation ` . Place the desired
* documentation on the interface and constructor signatures .
2022-01-11 21:53:42 +00:00
* /
module . exports = function mergeOverriddenImplementation ( getDocFromAlias , log ) {
return {
$runAfter : [ 'tags-extracted' , 'ids-computed' ] ,
$runBefore : [ 'filterPrivateDocs' ] ,
propertiesToKeep : [
'name' , 'id' , 'aliases' , 'fileInfo' , 'startingLine' , 'endingLine' ,
'path' , 'originalModule' , 'outputPath' , 'privateExport' , 'moduleDoc'
] ,
$process ( docs ) {
docs . forEach ( doc => {
2022-08-20 08:58:20 +00:00
if ( doc . overriddenImplementation ) {
2021-11-30 19:46:46 +00:00
// Convert the specified name into a doc.
const ctorDocArray = getDocFromAlias ( doc . overriddenImplementation ) ;
if ( ctorDocArray . length === 0 ) {
throw new Error ( ` @overriddenImplementation failed to find a doc for ${ doc . overriddenImplementation } . Are you sure this symbol is documented and exported? ` ) ;
2022-01-11 21:53:42 +00:00
}
2021-11-30 19:46:46 +00:00
if ( ctorDocArray . length >= 2 ) {
throw new Error ( ` @overriddenImplementation found multiple docs for ${ doc . overriddenImplementation } . You may only have one documented symbol for each. ` ) ;
2022-01-11 21:53:42 +00:00
}
2022-08-20 08:58:20 +00:00
2021-11-30 19:46:46 +00:00
const ctorDoc = ctorDocArray [ 0 ] ;
2022-01-11 21:53:42 +00:00
// Copy the constructor overrides from the constructor doc, if any are present.
2021-11-30 19:46:46 +00:00
if ( ! ctorDoc . members || ctorDoc . members . length === 0 || ! ctorDoc . members [ 0 ] . name . includes ( 'new' ) ) {
2022-08-20 08:58:20 +00:00
throw new Error ( ` @overriddenImplementation requires that the provided constructor ${ ctorDoc . id } have a member called "new", possibly with multiple overrides. ` ) ;
2022-01-11 21:53:42 +00:00
}
2021-11-30 19:46:46 +00:00
doc . constructorDoc = ctorDoc . members [ 0 ] ;
2022-01-11 21:53:42 +00:00
2021-11-30 19:46:46 +00:00
// Mark the constructor doc internal.
2022-08-20 09:11:19 +00:00
if ( ! ctorDoc . internal && ! ctorDoc . privateExport ) {
log . warn (
` Constructor doc ${ ctorDoc . id } was not marked as internal (either via an ` +
'\'@internal\' annotation or a \'ɵ\' prefix). Marking it as internal.' ) ;
2022-01-11 21:53:42 +00:00
ctorDoc . internal = true ;
}
2021-11-30 19:46:46 +00:00
// The exported doc should not be private.
doc . privateExport = false ;
doc . internal = false ;
2022-01-11 21:53:42 +00:00
}
} ) ;
}
} ;
} ;