mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
The `mergeOverridenImplementation` processor tries to ensure that any doc used with the `@overrideImplementation` annotation is not itself public (and explicitly marks is as internal if it is). Previously, it determined the public/private status of a doc by only checking the value of the doc's `internal` property (which is mainly set via the `@internal` annotation). This failed to account for docs marked as "private exports" (such as those prefixed with `ɵ`), which are essentially also treated as internal. This would result in incorrect warnings. [Example warning][1]: > Constructor doc forms/ɵFormControlCtor was not marked '@internal'; > adding this annotation. This commit prevents the incorrect warning by also checking the value of the doc's `privateExport` property to determine its public/private status. [1]: https://circleci.com/gh/angular/angular/1215057#step-104-164 PR Close #47201
78 lines
3 KiB
JavaScript
78 lines
3 KiB
JavaScript
/**
|
|
* 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>`)
|
|
*
|
|
* This looks like the following:
|
|
*
|
|
* ```
|
|
* /**
|
|
* * @overriddenImplementation FooCtor
|
|
* *\/
|
|
* export interface Foo {
|
|
* bar();
|
|
* }
|
|
*
|
|
* type FooInterface = Foo;
|
|
*
|
|
* export class FooImpl { ... }
|
|
*
|
|
* export interface FooCtor {
|
|
* new(): Foo;
|
|
* }
|
|
*
|
|
* export const Foo: FooCtor =
|
|
(class Foo implements FooInterface { ... }
|
|
* ```
|
|
*
|
|
* This processor will extend the docs for symbol `Foo` by copying all documented constructor overrides from `FooCtor`.
|
|
*
|
|
* In order to use this processor, annotate the exported interface with `@overriddenImplementation`. Place the desired
|
|
* documentation on the interface and constructor signatures.
|
|
*/
|
|
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 => {
|
|
if (doc.overriddenImplementation) {
|
|
// 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?`);
|
|
}
|
|
if (ctorDocArray.length >= 2) {
|
|
throw new Error(`@overriddenImplementation found multiple docs for ${doc.overriddenImplementation}. You may only have one documented symbol for each.`);
|
|
}
|
|
|
|
const ctorDoc = ctorDocArray[0];
|
|
|
|
// Copy the constructor overrides from the constructor doc, if any are present.
|
|
if (!ctorDoc.members || ctorDoc.members.length === 0 || !ctorDoc.members[0].name.includes('new')) {
|
|
throw new Error(`@overriddenImplementation requires that the provided constructor ${ctorDoc.id} have a member called "new", possibly with multiple overrides.`);
|
|
}
|
|
doc.constructorDoc = ctorDoc.members[0];
|
|
|
|
// Mark the constructor doc internal.
|
|
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.');
|
|
ctorDoc.internal = true;
|
|
}
|
|
|
|
// The exported doc should not be private.
|
|
doc.privateExport = false;
|
|
doc.internal = false;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
};
|