From 8a1cbd3df306be9ed543ab0a109c3976de4044ea Mon Sep 17 00:00:00 2001 From: Frank Wienberg Date: Fri, 2 Sep 2022 21:50:50 +0200 Subject: [PATCH 01/14] Diff after building: Update build-test exportImportStarAs2 result --- .../etc/exportImportStarAs2/api-extractor-scenarios.api.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json index b2c90df8919..fc67ce054b7 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json @@ -197,7 +197,7 @@ { "kind": "Reference", "text": "ForgottenClass", - "canonicalReference": "api-extractor-scenarios!~ForgottenClass:class" + "canonicalReference": "api-extractor-scenarios!ForgottenClass:class" }, { "kind": "Content", From 5b2d7ad645a4ced1863426ca0408b7953c1feb0f Mon Sep 17 00:00:00 2001 From: Frank Wienberg Date: Fri, 26 Aug 2022 13:47:57 +0200 Subject: [PATCH 02/14] [api-extractor] Fix line endings when running build-tests under Linux Maybe newlineKind: 'os' should be the default for api-extractor config (ExtractorConfig.ts) anyway, but for now, let's make it explicit in the test configuration. Another possible scenario is that the tests are run under Windows, but git has been configured to used 'lf'. Then, using 'os' line endings would still lead to a git diff. But detecting the git setting is probably beyond what the test runner should do. --- build-tests/api-extractor-scenarios/src/runScenarios.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build-tests/api-extractor-scenarios/src/runScenarios.ts b/build-tests/api-extractor-scenarios/src/runScenarios.ts index a973ed03b0e..54863dc9863 100644 --- a/build-tests/api-extractor-scenarios/src/runScenarios.ts +++ b/build-tests/api-extractor-scenarios/src/runScenarios.ts @@ -58,6 +58,8 @@ export function runScenarios(buildConfigPath: string): void { apiJsonFilePath: `/etc/${scenarioFolderName}/.api.json` }, + newlineKind: 'os', + messages: { extractorMessageReporting: { // For test purposes, write these warnings into .api.md From 6bc0676bda1f4e597106ab1ac51688fe4ddd68f4 Mon Sep 17 00:00:00 2001 From: Frank Wienberg Date: Mon, 29 Aug 2022 13:25:34 +0200 Subject: [PATCH 03/14] Ignore IntelliJ IDEA files --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 4dd985482a1..a6561a5f6a9 100644 --- a/.gitignore +++ b/.gitignore @@ -80,3 +80,7 @@ dist # Heft .heft + +# IntelliJ IDEA +.idea +*.iml From b11cc2485a225d7bb0e78e3115a77db56276e003 Mon Sep 17 00:00:00 2001 From: Frank Wienberg Date: Fri, 26 Aug 2022 13:22:19 +0200 Subject: [PATCH 04/14] [api-extractor] Fix 'export __ as __' part of #3593 "Incorrect canonical reference to aliased class in .api.json" https://github.com/microsoft/rushstack/issues/3593 Fix first scenario from #3593. Added test scenario "docReferencesAlias", inspired by the test repo referenced in the GitHub issue: `Item` and `Options` are default exports of two separate source files, and `index` re-exports both, renaming `Options` to `renamed_Options` (preserving the "namespacing" information of the directory). `DeclarationReferenceGenerator` now receives a function to resolve a Symbol to its emit name. `ApiModelGenerator` fetches this function from `Collector`, which now maintains another map from symbol to `CollectorEntity`. If for a given symbol, there is a `CollectorEntity`, then its `emitName` is returned, the symbol's name otherwise. While this change fixes the `export { __ as __ }` scenario, for _some_ reason, it changes the output of one other test scenario, namely "ambientNameConflict". In its `*.api.json` file, some reference to the class re-exported as `MyPromise`, the name clash of localFile's `Promise` and the global `Promise` now leads to `Promise_2` being used instead of `Promise`, the old expected result that was also wrong, because it should have been `MyPromise`, and `MyPromise` should appear in `*.api.json`, but that is something completely different, so I'll ignore the problem for now and check in the changed expected `*.api.json` of "ambientNameConflict". --- apps/api-extractor/src/collector/Collector.ts | 16 + .../DeclarationReferenceGenerator.ts | 2 +- .../config/build-config.json | 1 + .../api-extractor-scenarios.api.json | 2 +- .../api-extractor-scenarios.api.json | 296 ++++++++++++++++++ .../api-extractor-scenarios.api.md | 23 ++ .../etc/docReferencesAlias/rollup.d.ts | 12 + .../api-extractor-scenarios.api.json | 4 +- .../src/docReferencesAlias/Item.ts | 9 + .../src/docReferencesAlias/Options.ts | 8 + .../src/docReferencesAlias/index.ts | 5 + 11 files changed, 374 insertions(+), 4 deletions(-) create mode 100644 build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.json create mode 100644 build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.md create mode 100644 build-tests/api-extractor-scenarios/etc/docReferencesAlias/rollup.d.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesAlias/Item.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesAlias/Options.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesAlias/index.ts diff --git a/apps/api-extractor/src/collector/Collector.ts b/apps/api-extractor/src/collector/Collector.ts index 06420065f3a..8ea4f65e081 100644 --- a/apps/api-extractor/src/collector/Collector.ts +++ b/apps/api-extractor/src/collector/Collector.ts @@ -82,6 +82,7 @@ export class Collector { AstEntity, CollectorEntity >(); + private readonly _entitiesBySymbol: Map = new Map(); private readonly _starExportedExternalModulePaths: string[] = []; @@ -301,6 +302,18 @@ export class Collector { return this._entitiesByAstEntity.get(astEntity); } + /** + * Returns the name to emit for the given `symbol`. + * This is usually the `name` of that `symbol`. + * Only if this `symbol` has a `CollectorEntity`, the `nameForEmit` of that entity is used. + * @param symbol the symbol to compute the name to emit for + * @returns the name to emit for the given `symbol` + */ + public getEmitName(symbol: ts.Symbol): string { + const collectorEntity: CollectorEntity | undefined = this._entitiesBySymbol.get(symbol); + return collectorEntity?.nameForEmit ?? symbol.name; + } + public fetchSymbolMetadata(astSymbol: AstSymbol): SymbolMetadata { if (astSymbol.symbolMetadata === undefined) { this._fetchSymbolMetadata(astSymbol); @@ -420,6 +433,9 @@ export class Collector { entity = new CollectorEntity(astEntity); this._entitiesByAstEntity.set(astEntity, entity); + if (astEntity instanceof AstSymbol) { + this._entitiesBySymbol.set(astEntity.followedSymbol, entity); + } this._entities.push(entity); this._collectReferenceDirectives(astEntity); } diff --git a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts index ccfe9347d26..28edbb87c77 100644 --- a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts +++ b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts @@ -228,7 +228,7 @@ export class DeclarationReferenceGenerator { return undefined; } - let localName: string = followedSymbol.name; + let localName: string = this._collector.getEmitName(followedSymbol); if (followedSymbol.escapedName === ts.InternalSymbolName.Constructor) { localName = 'constructor'; } else { diff --git a/build-tests/api-extractor-scenarios/config/build-config.json b/build-tests/api-extractor-scenarios/config/build-config.json index 9501b49e9f2..16ac8add8d3 100644 --- a/build-tests/api-extractor-scenarios/config/build-config.json +++ b/build-tests/api-extractor-scenarios/config/build-config.json @@ -14,6 +14,7 @@ "docReferences", "docReferences2", "docReferences3", + "docReferencesAlias", "dynamicImportType", "dynamicImportType2", "dynamicImportType3", diff --git a/build-tests/api-extractor-scenarios/etc/ambientNameConflict/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/ambientNameConflict/api-extractor-scenarios.api.json index 334ad0d65a5..1b66cfe5812 100644 --- a/build-tests/api-extractor-scenarios/etc/ambientNameConflict/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/ambientNameConflict/api-extractor-scenarios.api.json @@ -197,7 +197,7 @@ { "kind": "Reference", "text": "MyPromise", - "canonicalReference": "api-extractor-scenarios!~Promise:class" + "canonicalReference": "api-extractor-scenarios!~Promise_2:class" }, { "kind": "Content", diff --git a/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..4cefa690518 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.json @@ -0,0 +1,296 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1009, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!Item:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export default class Item " + } + ], + "releaseTag": "Public", + "name": "Item", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Property", + "canonicalReference": "api-extractor-scenarios!Item#options:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "options: " + }, + { + "kind": "Reference", + "text": "Options", + "canonicalReference": "api-extractor-scenarios!renamed_Options:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "options", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isStatic": false, + "isProtected": false + } + ], + "implementsTokenRanges": [] + }, + { + "kind": "Interface", + "canonicalReference": "api-extractor-scenarios!renamed_Options:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export default interface Options " + } + ], + "releaseTag": "Public", + "name": "renamed_Options", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "api-extractor-scenarios!renamed_Options#color:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "color: " + }, + { + "kind": "Content", + "text": "'red' | 'blue'" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "color", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "api-extractor-scenarios!renamed_Options#name:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "name: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "name", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ], + "extendsTokenRanges": [] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..e5dbeddb60e --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/docReferencesAlias/api-extractor-scenarios.api.md @@ -0,0 +1,23 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export class Item { + // (undocumented) + options: renamed_Options; +} + +// @public (undocumented) +export interface renamed_Options { + // (undocumented) + color: 'red' | 'blue'; + // (undocumented) + name: string; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/docReferencesAlias/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/docReferencesAlias/rollup.d.ts new file mode 100644 index 00000000000..36cac4b715d --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/docReferencesAlias/rollup.d.ts @@ -0,0 +1,12 @@ +/** @public */ +export declare class Item { + options: renamed_Options; +} + +/** @public */ +export declare interface renamed_Options { + name: string; + color: 'red' | 'blue'; +} + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json index 95a94902c4a..01ca45f142e 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json @@ -440,7 +440,7 @@ { "kind": "Reference", "text": "DuplicateName", - "canonicalReference": "api-extractor-scenarios!~DuplicateName:type" + "canonicalReference": "api-extractor-scenarios!~DuplicateName_2:type" }, { "kind": "Content", @@ -556,7 +556,7 @@ { "kind": "Reference", "text": "AnotherDuplicateName", - "canonicalReference": "api-extractor-scenarios!~AnotherDuplicateName:class" + "canonicalReference": "api-extractor-scenarios!~AnotherDuplicateName_2:class" }, { "kind": "Content", diff --git a/build-tests/api-extractor-scenarios/src/docReferencesAlias/Item.ts b/build-tests/api-extractor-scenarios/src/docReferencesAlias/Item.ts new file mode 100644 index 00000000000..fd9fe5ecfe5 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesAlias/Item.ts @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import Options from './Options'; + +/** @public */ +export default class Item { + options: Options; +} diff --git a/build-tests/api-extractor-scenarios/src/docReferencesAlias/Options.ts b/build-tests/api-extractor-scenarios/src/docReferencesAlias/Options.ts new file mode 100644 index 00000000000..a2586a59a87 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesAlias/Options.ts @@ -0,0 +1,8 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** @public */ +export default interface Options { + name: string; + color: 'red' | 'blue'; +} diff --git a/build-tests/api-extractor-scenarios/src/docReferencesAlias/index.ts b/build-tests/api-extractor-scenarios/src/docReferencesAlias/index.ts new file mode 100644 index 00000000000..5eb64bd7644 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesAlias/index.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export { default as renamed_Options } from './Options'; +export { default as Item } from './Item'; From ef387a79b49beec1f6bdd444d43c80e08400356d Mon Sep 17 00:00:00 2001 From: Frank Wienberg Date: Tue, 30 Aug 2022 13:54:08 +0200 Subject: [PATCH 05/14] [api-extractor] Fix 'export * as __' part of #3593 "Incorrect canonical reference to aliased class in .api.json" https://github.com/microsoft/rushstack/issues/3593 This fixes the other aliasing, namely through (nested) namespaces. Added a test scenario "docReferencesNamespaceAlias", which consists of nested namespaces that have their own index.d.ts, re-exporting the default export of each sibling file and their respective sub-namespace (top-level -> 'renamed' -> 'sub'). The *.api.json canonicalReferences now correctly use the nested namespace "fully-qualified name". Again, some other test result changed unexpectedly, maybe revealing an existing bug / wrong expected result. The scenarios are "exportImportStarAs2", "importEquals" and "includeForgottenExports". The pattern is that before, namespace and class were separate tokens, but are now aggregated to one dot-separated expression. I hope that these are improvements, but I am not sure, so this should be reviewed. Implementation: --------------- When DeclarationReferenceGenerator#_getParentReference() looks for a parent symbol, it must also resolve namespace parent symbols the Collector derived from 'import * as ___'. To that end, Collector now has a method getExportingNamespace() that pulls namespace imports from the CollectorEntities of a namespace symbol (if present). For that, namespace imports are now also registered in the _entitiesBySymbol Map. In DeclarationReferenceGenerator, the handling of namespaces had to be improved. _getNavigationToSymbol() now detects namespaces, even behind an alias, and then always returns "." as separator. To find the parent, it also considers namespaces now. _symbolToDeclarationReference() takes care not to follow an alias that leads to a *-imported namespace, because the target is the imported source file that no longer allows to retrieve the namespace name. --- apps/api-extractor/src/collector/Collector.ts | 21 + .../src/collector/CollectorEntity.ts | 13 + .../DeclarationReferenceGenerator.ts | 26 +- .../config/build-config.json | 1 + .../api-extractor-scenarios.api.json | 390 ++++++++++++++++++ .../api-extractor-scenarios.api.md | 45 ++ .../docReferencesNamespaceAlias/rollup.d.ts | 32 ++ .../api-extractor-scenarios.api.json | 8 +- .../api-extractor-scenarios.api.json | 4 +- .../api-extractor-scenarios.api.json | 10 +- .../src/docReferencesNamespaceAlias/Item.ts | 9 + .../src/docReferencesNamespaceAlias/index.ts | 6 + .../renamed/Options.ts | 11 + .../renamed/index.ts | 6 + .../renamed/sub/SubOptions.ts | 7 + .../renamed/sub/index.ts | 4 + 16 files changed, 574 insertions(+), 19 deletions(-) create mode 100644 build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.json create mode 100644 build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.md create mode 100644 build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/rollup.d.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/Item.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/index.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/Options.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/index.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/SubOptions.ts create mode 100644 build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/index.ts diff --git a/apps/api-extractor/src/collector/Collector.ts b/apps/api-extractor/src/collector/Collector.ts index 8ea4f65e081..9fa64ee8177 100644 --- a/apps/api-extractor/src/collector/Collector.ts +++ b/apps/api-extractor/src/collector/Collector.ts @@ -314,6 +314,25 @@ export class Collector { return collectorEntity?.nameForEmit ?? symbol.name; } + /** + * Returns (the symbol of) the namespace that exports the given `symbol`, if applicable. + * @param symbol the symbol representing the namespace that exports the given `symbol`, + * or undefined if there is no such namespace + */ + public getExportingNamespace(symbol: ts.Symbol): ts.Symbol | undefined { + const collectorEntity: CollectorEntity | undefined = this._entitiesBySymbol.get(symbol); + if (collectorEntity) { + const exportingNamespace: AstNamespaceImport | undefined = collectorEntity.getExportingNamespace(); + if (exportingNamespace) { + return TypeScriptInternals.tryGetSymbolForDeclaration( + exportingNamespace.declaration, + this.typeChecker + ); + } + } + return undefined; + } + public fetchSymbolMetadata(astSymbol: AstSymbol): SymbolMetadata { if (astSymbol.symbolMetadata === undefined) { this._fetchSymbolMetadata(astSymbol); @@ -435,6 +454,8 @@ export class Collector { this._entitiesByAstEntity.set(astEntity, entity); if (astEntity instanceof AstSymbol) { this._entitiesBySymbol.set(astEntity.followedSymbol, entity); + } else if (astEntity instanceof AstNamespaceImport) { + this._entitiesBySymbol.set((astEntity.declaration as unknown as ts.Type).symbol, entity); } this._entities.push(entity); this._collectReferenceDirectives(astEntity); diff --git a/apps/api-extractor/src/collector/CollectorEntity.ts b/apps/api-extractor/src/collector/CollectorEntity.ts index 1ec6e772a5b..bb52da90656 100644 --- a/apps/api-extractor/src/collector/CollectorEntity.ts +++ b/apps/api-extractor/src/collector/CollectorEntity.ts @@ -7,6 +7,7 @@ import { AstSymbol } from '../analyzer/AstSymbol'; import { Collector } from './Collector'; import { Sort } from '@rushstack/node-core-library'; import { AstEntity } from '../analyzer/AstEntity'; +import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; /** * This is a data structure used by the Collector to track an AstEntity that may be emitted in the *.d.ts file. @@ -189,6 +190,18 @@ export class CollectorEntity { return this._localExportNamesByParent.size > 0; } + /** + * Return the first namespace that exports this entity. + */ + public getExportingNamespace(): AstNamespaceImport | undefined { + for (const [parent, localExportNames] of this._localExportNamesByParent) { + if (parent.consumable && localExportNames.size > 0 && parent.astEntity instanceof AstNamespaceImport) { + return parent.astEntity; + } + } + return undefined; + } + /** * Adds a new export name to the entity. */ diff --git a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts index 28edbb87c77..2aec240f83a 100644 --- a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts +++ b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts @@ -88,9 +88,18 @@ export class DeclarationReferenceGenerator { } private _getNavigationToSymbol(symbol: ts.Symbol): Navigation { + // resolve alias first: + if (symbol.flags & ts.SymbolFlags.Alias) { + symbol = this._collector.typeChecker.getAliasedSymbol(symbol); + } + // namespace always uses ".": + if (symbol.flags & ts.SymbolFlags.Namespace) { + return Navigation.Exports; + } const declaration: ts.Declaration | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile(); - const parent: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(symbol); + const parent: ts.Symbol | undefined = + TypeScriptInternals.getSymbolParent(symbol) ?? this._collector.getExportingNamespace(symbol); // If it's global or from an external library, then use either Members or Exports. It's not possible for // global symbols or external library symbols to be Locals. @@ -208,7 +217,11 @@ export class DeclarationReferenceGenerator { followedSymbol = this._collector.typeChecker.getExportSymbolOfSymbol(followedSymbol); } if (followedSymbol.flags & ts.SymbolFlags.Alias) { - followedSymbol = this._collector.typeChecker.getAliasedSymbol(followedSymbol); + const nextFollowedSymbol: ts.Symbol = this._collector.typeChecker.getAliasedSymbol(followedSymbol); + // do not follow alias to namespace, as it results in a source file that no longer knows its short name: + if (!(nextFollowedSymbol.flags & ts.SymbolFlags.Namespace)) { + followedSymbol = nextFollowedSymbol; + } } if (DeclarationReferenceGenerator._isExternalModuleSymbol(followedSymbol)) { @@ -266,9 +279,13 @@ export class DeclarationReferenceGenerator { } private _getParentReference(symbol: ts.Symbol): DeclarationReference | undefined { - const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); + // First case: the symbol is exported via a namespace + const exportingNamespace: ts.Symbol | undefined = this._collector.getExportingNamespace(symbol); + if (exportingNamespace) { + return this._symbolToDeclarationReference(exportingNamespace, exportingNamespace.flags, true); + } - // First, try to find a parent symbol via the symbol tree. + // Next, try to find a parent symbol via the symbol tree. const parentSymbol: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(symbol); if (parentSymbol) { return this._symbolToDeclarationReference( @@ -290,6 +307,7 @@ export class DeclarationReferenceGenerator { // // In the example above, `SomeType` doesn't have a parent symbol per the TS internal API above, // but its reference still needs to be qualified with the parent reference for `n`. + const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); const grandParent: ts.Node | undefined = declaration?.parent?.parent; if (grandParent && ts.isModuleDeclaration(grandParent)) { const grandParentSymbol: ts.Symbol | undefined = TypeScriptInternals.tryGetSymbolForDeclaration( diff --git a/build-tests/api-extractor-scenarios/config/build-config.json b/build-tests/api-extractor-scenarios/config/build-config.json index 16ac8add8d3..c3f851e1fe1 100644 --- a/build-tests/api-extractor-scenarios/config/build-config.json +++ b/build-tests/api-extractor-scenarios/config/build-config.json @@ -15,6 +15,7 @@ "docReferences2", "docReferences3", "docReferencesAlias", + "docReferencesNamespaceAlias", "dynamicImportType", "dynamicImportType2", "dynamicImportType3", diff --git a/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..d04711be940 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.json @@ -0,0 +1,390 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1009, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!Item:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export default class Item " + } + ], + "releaseTag": "Public", + "name": "Item", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Property", + "canonicalReference": "api-extractor-scenarios!Item#options:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "options: " + }, + { + "kind": "Reference", + "text": "Options", + "canonicalReference": "api-extractor-scenarios!renamed.Options:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "options", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isStatic": false, + "isProtected": false + } + ], + "implementsTokenRanges": [] + }, + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!renamed:namespace", + "docComment": "", + "excerptTokens": [], + "releaseTag": "None", + "name": "renamed", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Interface", + "canonicalReference": "api-extractor-scenarios!renamed.Options:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export default interface Options " + } + ], + "releaseTag": "Public", + "name": "Options", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "api-extractor-scenarios!renamed.Options#color:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "color: " + }, + { + "kind": "Content", + "text": "'red' | 'blue'" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "color", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "api-extractor-scenarios!renamed.Options#name:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "name: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "name", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "PropertySignature", + "canonicalReference": "api-extractor-scenarios!renamed.Options#subOptions:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "subOptions: " + }, + { + "kind": "Reference", + "text": "SubOptions", + "canonicalReference": "api-extractor-scenarios!renamed.sub.SubOptions:interface" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "subOptions", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ], + "extendsTokenRanges": [] + }, + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!renamed.sub:namespace", + "docComment": "", + "excerptTokens": [], + "releaseTag": "None", + "name": "sub", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Interface", + "canonicalReference": "api-extractor-scenarios!renamed.sub.SubOptions:interface", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export default interface SubOptions " + } + ], + "releaseTag": "Public", + "name": "SubOptions", + "preserveMemberOrder": false, + "members": [ + { + "kind": "PropertySignature", + "canonicalReference": "api-extractor-scenarios!renamed.sub.SubOptions#count:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "count: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isReadonly": false, + "isOptional": false, + "releaseTag": "Public", + "name": "count", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + } + ], + "extendsTokenRanges": [] + } + ] + } + ] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..090e96727ac --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/api-extractor-scenarios.api.md @@ -0,0 +1,45 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +// @public (undocumented) +export class Item { + // (undocumented) + options: Options; +} + +// @public (undocumented) +interface Options { + // (undocumented) + color: 'red' | 'blue'; + // (undocumented) + name: string; + // (undocumented) + subOptions: SubOptions; +} + +declare namespace renamed { + export { + sub, + Options + } +} +export { renamed } + +declare namespace sub { + export { + SubOptions + } +} + +// @public (undocumented) +interface SubOptions { + // (undocumented) + count: number; +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/rollup.d.ts new file mode 100644 index 00000000000..0c7a1f4c742 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/docReferencesNamespaceAlias/rollup.d.ts @@ -0,0 +1,32 @@ +/** @public */ +export declare class Item { + options: Options; +} + +/** @public */ +declare interface Options { + name: string; + color: 'red' | 'blue'; + subOptions: SubOptions; +} + +declare namespace renamed { + export { + sub, + Options + } +} +export { renamed } + +declare namespace sub { + export { + SubOptions + } +} + +/** @public */ +declare interface SubOptions { + count: number; +} + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json index fc67ce054b7..5d20caf9169 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json @@ -190,13 +190,9 @@ "kind": "Content", "text": "export declare function exportedApi(): " }, - { - "kind": "Content", - "text": "forgottenNs." - }, { "kind": "Reference", - "text": "ForgottenClass", + "text": "forgottenNs.ForgottenClass", "canonicalReference": "api-extractor-scenarios!ForgottenClass:class" }, { @@ -206,7 +202,7 @@ ], "returnTypeTokenRange": { "startIndex": 1, - "endIndex": 3 + "endIndex": 2 }, "releaseTag": "Public", "overloadIndex": 1, diff --git a/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.json index c7fd110c62c..838ef3da2a9 100644 --- a/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/importEquals/api-extractor-scenarios.api.json @@ -183,11 +183,11 @@ }, { "kind": "Content", - "text": "typeof colors." + "text": "typeof " }, { "kind": "Reference", - "text": "zebra", + "text": "colors.zebra", "canonicalReference": "colors!zebra:var" }, { diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json index 01ca45f142e..2197aad8ba0 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json @@ -468,7 +468,7 @@ { "kind": "Reference", "text": "ForgottenExport4.ForgottenExport5", - "canonicalReference": "api-extractor-scenarios!~ForgottenExport4.ForgottenExport5:class" + "canonicalReference": "api-extractor-scenarios!ForgottenExport4.ForgottenExport5:class" }, { "kind": "Content", @@ -493,13 +493,9 @@ "kind": "Content", "text": "export declare function someFunction5(): " }, - { - "kind": "Content", - "text": "internal2." - }, { "kind": "Reference", - "text": "ForgottenExport6", + "text": "internal2.ForgottenExport6", "canonicalReference": "api-extractor-scenarios!ForgottenExport6:class" }, { @@ -509,7 +505,7 @@ ], "returnTypeTokenRange": { "startIndex": 1, - "endIndex": 3 + "endIndex": 2 }, "releaseTag": "Public", "overloadIndex": 1, diff --git a/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/Item.ts b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/Item.ts new file mode 100644 index 00000000000..247b3f48835 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/Item.ts @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import Options from './renamed/Options'; + +/** @public */ +export default class Item { + options: Options; +} diff --git a/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/index.ts b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/index.ts new file mode 100644 index 00000000000..e5ae676d355 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as renamed from './renamed'; +export { renamed }; +export { default as Item } from './Item'; diff --git a/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/Options.ts b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/Options.ts new file mode 100644 index 00000000000..d58c03679aa --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/Options.ts @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import SubOptions from './sub/SubOptions'; + +/** @public */ +export default interface Options { + name: string; + color: 'red' | 'blue'; + subOptions: SubOptions; +} diff --git a/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/index.ts b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/index.ts new file mode 100644 index 00000000000..1066c9b2a0d --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/index.ts @@ -0,0 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as sub from './sub'; +export { sub }; +export { default as Options } from './Options'; diff --git a/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/SubOptions.ts b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/SubOptions.ts new file mode 100644 index 00000000000..bc413e17cc0 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/SubOptions.ts @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** @public */ +export default interface SubOptions { + count: number; +} diff --git a/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/index.ts b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/index.ts new file mode 100644 index 00000000000..bbef81bf862 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/docReferencesNamespaceAlias/renamed/sub/index.ts @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +export { default as SubOptions } from './SubOptions'; From 97e4593826a5d36ab3bd8db96259eaab134e8374 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Wed, 7 Sep 2022 17:23:12 -0700 Subject: [PATCH 06/14] Changes to #3602 --- .../src/analyzer/AstNamespaceImport.ts | 7 + .../src/analyzer/ExportAnalyzer.ts | 3 +- apps/api-extractor/src/collector/Collector.ts | 39 +-- .../src/collector/CollectorEntity.ts | 21 +- .../DeclarationReferenceGenerator.ts | 105 ++++--- .../config/build-config.json | 2 + .../api-extractor-scenarios.api.json | 2 +- .../api-extractor-scenarios.api.json | 282 ++++++++++++++++++ .../api-extractor-scenarios.api.md | 36 +++ .../etc/namespaceImports/rollup.d.ts | 28 ++ .../api-extractor-scenarios.api.json | 233 +++++++++++++++ .../api-extractor-scenarios.api.md | 23 ++ .../etc/namespaceImports2/rollup.d.ts | 15 + .../src/namespaceImports/index.ts | 12 + .../src/namespaceImports/intermediate1.ts | 5 + .../src/namespaceImports/intermediate2.ts | 5 + .../src/namespaceImports/internal.ts | 5 + .../src/namespaceImports2/index.ts | 12 + .../src/namespaceImports2/internal.ts | 5 + 19 files changed, 753 insertions(+), 87 deletions(-) create mode 100644 build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json create mode 100644 build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.md create mode 100644 build-tests/api-extractor-scenarios/etc/namespaceImports/rollup.d.ts create mode 100644 build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.json create mode 100644 build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.md create mode 100644 build-tests/api-extractor-scenarios/etc/namespaceImports2/rollup.d.ts create mode 100644 build-tests/api-extractor-scenarios/src/namespaceImports/index.ts create mode 100644 build-tests/api-extractor-scenarios/src/namespaceImports/intermediate1.ts create mode 100644 build-tests/api-extractor-scenarios/src/namespaceImports/intermediate2.ts create mode 100644 build-tests/api-extractor-scenarios/src/namespaceImports/internal.ts create mode 100644 build-tests/api-extractor-scenarios/src/namespaceImports2/index.ts create mode 100644 build-tests/api-extractor-scenarios/src/namespaceImports2/internal.ts diff --git a/apps/api-extractor/src/analyzer/AstNamespaceImport.ts b/apps/api-extractor/src/analyzer/AstNamespaceImport.ts index 955ef903d27..acb4bd4c578 100644 --- a/apps/api-extractor/src/analyzer/AstNamespaceImport.ts +++ b/apps/api-extractor/src/analyzer/AstNamespaceImport.ts @@ -11,6 +11,7 @@ export interface IAstNamespaceImportOptions { readonly astModule: AstModule; readonly namespaceName: string; readonly declaration: ts.Declaration; + readonly symbol: ts.Symbol; } /** @@ -67,11 +68,17 @@ export class AstNamespaceImport extends AstSyntheticEntity { */ public readonly declaration: ts.Declaration; + /** + * The original `ts.SymbolFlags.Namespace` symbol. + */ + public readonly symbol: ts.Symbol; + public constructor(options: IAstNamespaceImportOptions) { super(); this.astModule = options.astModule; this.namespaceName = options.namespaceName; this.declaration = options.declaration; + this.symbol = options.symbol; } /** {@inheritdoc} */ diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index 33867923bb5..3610f828c07 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -626,7 +626,8 @@ export class ExportAnalyzer { namespaceImport = new AstNamespaceImport({ namespaceName: declarationSymbol.name, astModule: astModule, - declaration: declaration + declaration: declaration, + symbol: declarationSymbol }); this._astNamespaceImportByModule.set(astModule, namespaceImport); } diff --git a/apps/api-extractor/src/collector/Collector.ts b/apps/api-extractor/src/collector/Collector.ts index 9fa64ee8177..ea2a2dba145 100644 --- a/apps/api-extractor/src/collector/Collector.ts +++ b/apps/api-extractor/src/collector/Collector.ts @@ -296,41 +296,18 @@ export class Collector { } /** - * Returns the associated `CollectorEntity` for the given `astEntity`, if one was created during analysis. - */ - public tryGetCollectorEntity(astEntity: AstEntity): CollectorEntity | undefined { - return this._entitiesByAstEntity.get(astEntity); - } - - /** - * Returns the name to emit for the given `symbol`. - * This is usually the `name` of that `symbol`. - * Only if this `symbol` has a `CollectorEntity`, the `nameForEmit` of that entity is used. - * @param symbol the symbol to compute the name to emit for - * @returns the name to emit for the given `symbol` + * For a given analyzed ts.Symbol, return the CollectorEntity that it refers to. Returns undefined if it + * doesn't refer to anything interesting. */ - public getEmitName(symbol: ts.Symbol): string { - const collectorEntity: CollectorEntity | undefined = this._entitiesBySymbol.get(symbol); - return collectorEntity?.nameForEmit ?? symbol.name; + public tryGetEntityForSymbol(symbol: ts.Symbol): CollectorEntity | undefined { + return this._entitiesBySymbol.get(symbol); } /** - * Returns (the symbol of) the namespace that exports the given `symbol`, if applicable. - * @param symbol the symbol representing the namespace that exports the given `symbol`, - * or undefined if there is no such namespace + * Returns the associated `CollectorEntity` for the given `astEntity`, if one was created during analysis. */ - public getExportingNamespace(symbol: ts.Symbol): ts.Symbol | undefined { - const collectorEntity: CollectorEntity | undefined = this._entitiesBySymbol.get(symbol); - if (collectorEntity) { - const exportingNamespace: AstNamespaceImport | undefined = collectorEntity.getExportingNamespace(); - if (exportingNamespace) { - return TypeScriptInternals.tryGetSymbolForDeclaration( - exportingNamespace.declaration, - this.typeChecker - ); - } - } - return undefined; + public tryGetCollectorEntity(astEntity: AstEntity): CollectorEntity | undefined { + return this._entitiesByAstEntity.get(astEntity); } public fetchSymbolMetadata(astSymbol: AstSymbol): SymbolMetadata { @@ -455,7 +432,7 @@ export class Collector { if (astEntity instanceof AstSymbol) { this._entitiesBySymbol.set(astEntity.followedSymbol, entity); } else if (astEntity instanceof AstNamespaceImport) { - this._entitiesBySymbol.set((astEntity.declaration as unknown as ts.Type).symbol, entity); + this._entitiesBySymbol.set(astEntity.symbol, entity); } this._entities.push(entity); this._collectReferenceDirectives(astEntity); diff --git a/apps/api-extractor/src/collector/CollectorEntity.ts b/apps/api-extractor/src/collector/CollectorEntity.ts index bb52da90656..46f6af15357 100644 --- a/apps/api-extractor/src/collector/CollectorEntity.ts +++ b/apps/api-extractor/src/collector/CollectorEntity.ts @@ -7,7 +7,6 @@ import { AstSymbol } from '../analyzer/AstSymbol'; import { Collector } from './Collector'; import { Sort } from '@rushstack/node-core-library'; import { AstEntity } from '../analyzer/AstEntity'; -import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; /** * This is a data structure used by the Collector to track an AstEntity that may be emitted in the *.d.ts file. @@ -96,6 +95,13 @@ export class CollectorEntity { return false; } + /** + * Indicates that this entity is exported from the package entry point. Compare to `CollectorEntity.exported`. + */ + public get exportedFromEntryPoint(): boolean { + return this.exportNames.size > 0; + } + /** * Indicates that this entity is exported from its parent module (i.e. either the package entry point or * a local namespace). Compare to `CollectorEntity.consumable`. @@ -117,7 +123,7 @@ export class CollectorEntity { */ public get exported(): boolean { // Exported from top-level? - if (this.exportNames.size > 0) return true; + if (this.exportedFromEntryPoint) return true; // Exported from parent? for (const localExportNames of this._localExportNamesByParent.values()) { @@ -157,7 +163,7 @@ export class CollectorEntity { */ public get consumable(): boolean { // Exported from top-level? - if (this.exportNames.size > 0) return true; + if (this.exportedFromEntryPoint) return true; // Exported from consumable parent? for (const [parent, localExportNames] of this._localExportNamesByParent) { @@ -191,12 +197,13 @@ export class CollectorEntity { } /** - * Return the first namespace that exports this entity. + * Return the first consumable parent that exports this entity. If there is none, returns + * `undefined`. */ - public getExportingNamespace(): AstNamespaceImport | undefined { + public get firstExportingConsumableParent(): CollectorEntity | undefined { for (const [parent, localExportNames] of this._localExportNamesByParent) { - if (parent.consumable && localExportNames.size > 0 && parent.astEntity instanceof AstNamespaceImport) { - return parent.astEntity; + if (parent.consumable && localExportNames.size > 0) { + return parent; } } return undefined; diff --git a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts index 2aec240f83a..8b16a787ba5 100644 --- a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts +++ b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts @@ -15,6 +15,7 @@ import { TypeScriptHelpers } from '../analyzer/TypeScriptHelpers'; import { TypeScriptInternals } from '../analyzer/TypeScriptInternals'; import { Collector } from '../collector/Collector'; import { CollectorEntity } from '../collector/CollectorEntity'; +import { AstNamespaceImport } from '../analyzer/AstNamespaceImport'; export class DeclarationReferenceGenerator { public static readonly unknownReference: string = '?'; @@ -88,18 +89,9 @@ export class DeclarationReferenceGenerator { } private _getNavigationToSymbol(symbol: ts.Symbol): Navigation { - // resolve alias first: - if (symbol.flags & ts.SymbolFlags.Alias) { - symbol = this._collector.typeChecker.getAliasedSymbol(symbol); - } - // namespace always uses ".": - if (symbol.flags & ts.SymbolFlags.Namespace) { - return Navigation.Exports; - } const declaration: ts.Declaration | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile(); - const parent: ts.Symbol | undefined = - TypeScriptInternals.getSymbolParent(symbol) ?? this._collector.getExportingNamespace(symbol); + const parent: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(symbol); // If it's global or from an external library, then use either Members or Exports. It's not possible for // global symbols or external library symbols to be Locals. @@ -118,34 +110,25 @@ export class DeclarationReferenceGenerator { return Navigation.Exports; } - // Otherwise, this symbol is from the current package. - if (parent) { - // If we've found an exported CollectorEntity, then it's exported from the package entry point, so - // use Exports. - const namedDeclaration: ts.DeclarationName | undefined = ( - declaration as ts.NamedDeclaration | undefined - )?.name; - if (namedDeclaration && ts.isIdentifier(namedDeclaration)) { - const collectorEntity: CollectorEntity | undefined = - this._collector.tryGetEntityForNode(namedDeclaration); - if (collectorEntity && collectorEntity.exported) { - return Navigation.Exports; - } - } - - // If its parent symbol is not a source file, then use either Exports or Members. If the parent symbol - // is a source file, but it wasn't exported from the package entry point (in the check above), then the - // symbol is a local, so fall through below. - if (!DeclarationReferenceGenerator._isExternalModuleSymbol(parent)) { - if ( - parent.members && - DeclarationReferenceGenerator._isSameSymbol(parent.members.get(symbol.escapedName), symbol) - ) { - return Navigation.Members; - } + // Otherwise, this symbol is from the current package. If we've found an exported CollectorEntity, then use + // Exports. + const entity: CollectorEntity | undefined = this._collector.tryGetEntityForSymbol(symbol); + if (entity && entity.exported) { + return Navigation.Exports; + } - return Navigation.Exports; + // If its parent symbol is not a source file, then use either Exports or Members. If the parent symbol + // is a source file, but it wasn't exported from the package entry point (in the check above), then the + // symbol is a local, so fall through below. + if (parent && !DeclarationReferenceGenerator._isExternalModuleSymbol(parent)) { + if ( + parent.members && + DeclarationReferenceGenerator._isSameSymbol(parent.members.get(symbol.escapedName), symbol) + ) { + return Navigation.Members; } + + return Navigation.Exports; } // Otherwise, we have a local symbol, so use a Locals navigation. These are either: @@ -217,10 +200,12 @@ export class DeclarationReferenceGenerator { followedSymbol = this._collector.typeChecker.getExportSymbolOfSymbol(followedSymbol); } if (followedSymbol.flags & ts.SymbolFlags.Alias) { - const nextFollowedSymbol: ts.Symbol = this._collector.typeChecker.getAliasedSymbol(followedSymbol); - // do not follow alias to namespace, as it results in a source file that no longer knows its short name: - if (!(nextFollowedSymbol.flags & ts.SymbolFlags.Namespace)) { - followedSymbol = nextFollowedSymbol; + followedSymbol = this._collector.typeChecker.getAliasedSymbol(followedSymbol); + + // Without this logic, we end up following the symbol `ns` in `import * as ns from './file'` to + // the actual file `file.ts`. We don't want to do this, so revert to the original symbol. + if (followedSymbol.flags & ts.SymbolFlags.ValueModule) { + followedSymbol = symbol; } } @@ -241,7 +226,12 @@ export class DeclarationReferenceGenerator { return undefined; } - let localName: string = this._collector.getEmitName(followedSymbol); + let localName: string = followedSymbol.name; + const entity: CollectorEntity | undefined = this._collector.tryGetEntityForSymbol(followedSymbol); + if (entity?.nameForEmit) { + localName = entity.nameForEmit; + } + if (followedSymbol.escapedName === ts.InternalSymbolName.Constructor) { localName = 'constructor'; } else { @@ -279,10 +269,33 @@ export class DeclarationReferenceGenerator { } private _getParentReference(symbol: ts.Symbol): DeclarationReference | undefined { - // First case: the symbol is exported via a namespace - const exportingNamespace: ts.Symbol | undefined = this._collector.getExportingNamespace(symbol); - if (exportingNamespace) { - return this._symbolToDeclarationReference(exportingNamespace, exportingNamespace.flags, true); + const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); + const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile(); + + const entity: CollectorEntity | undefined = this._collector.tryGetEntityForSymbol(symbol); + if (entity) { + if (entity.exportedFromEntryPoint) { + return new DeclarationReference(this._sourceFileToModuleSource(sourceFile)); + } + + const firstExportingConsumableParent: CollectorEntity | undefined = + entity.firstExportingConsumableParent; + if ( + firstExportingConsumableParent && + firstExportingConsumableParent.astEntity instanceof AstNamespaceImport + ) { + const parentSymbol: ts.Symbol | undefined = TypeScriptInternals.tryGetSymbolForDeclaration( + firstExportingConsumableParent.astEntity.declaration, + this._collector.typeChecker + ); + if (parentSymbol) { + return this._symbolToDeclarationReference( + parentSymbol, + parentSymbol.flags, + /*includeModuleSymbols*/ true + ); + } + } } // Next, try to find a parent symbol via the symbol tree. @@ -307,7 +320,6 @@ export class DeclarationReferenceGenerator { // // In the example above, `SomeType` doesn't have a parent symbol per the TS internal API above, // but its reference still needs to be qualified with the parent reference for `n`. - const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); const grandParent: ts.Node | undefined = declaration?.parent?.parent; if (grandParent && ts.isModuleDeclaration(grandParent)) { const grandParentSymbol: ts.Symbol | undefined = TypeScriptInternals.tryGetSymbolForDeclaration( @@ -324,7 +336,6 @@ export class DeclarationReferenceGenerator { } // At this point, we have a local symbol in a module. - const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile(); if (sourceFile && ts.isExternalModule(sourceFile)) { return new DeclarationReference(this._sourceFileToModuleSource(sourceFile)); } else { diff --git a/build-tests/api-extractor-scenarios/config/build-config.json b/build-tests/api-extractor-scenarios/config/build-config.json index c3f851e1fe1..3c41f30a625 100644 --- a/build-tests/api-extractor-scenarios/config/build-config.json +++ b/build-tests/api-extractor-scenarios/config/build-config.json @@ -41,6 +41,8 @@ "mergedDeclarations", "mixinPattern", "namedDefaultImport", + "namespaceImports", + "namespaceImports2", "preapproved", "readonlyDeclarations", "referenceTokens", diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json index 2197aad8ba0..0e0bf04f003 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json @@ -468,7 +468,7 @@ { "kind": "Reference", "text": "ForgottenExport4.ForgottenExport5", - "canonicalReference": "api-extractor-scenarios!ForgottenExport4.ForgottenExport5:class" + "canonicalReference": "api-extractor-scenarios!~ForgottenExport4.ForgottenExport5:class" }, { "kind": "Content", diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..4f050369a44 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json @@ -0,0 +1,282 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1009, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!i1:namespace", + "docComment": "", + "excerptTokens": [], + "releaseTag": "None", + "name": "i1", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!i1.internal:namespace", + "docComment": "", + "excerptTokens": [], + "releaseTag": "None", + "name": "internal", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!i1.internal.SomeClass:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare class SomeClass " + } + ], + "releaseTag": "Public", + "name": "SomeClass", + "preserveMemberOrder": false, + "members": [], + "implementsTokenRanges": [] + } + ] + } + ] + }, + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!i2:namespace", + "docComment": "", + "excerptTokens": [], + "releaseTag": "None", + "name": "i2", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!i2.internal:namespace", + "docComment": "", + "excerptTokens": [], + "releaseTag": "None", + "name": "internal", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!i2.internal.SomeClass:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare class SomeClass " + } + ], + "releaseTag": "Public", + "name": "SomeClass", + "preserveMemberOrder": false, + "members": [], + "implementsTokenRanges": [] + } + ] + } + ] + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!someFunction:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function someFunction(): " + }, + { + "kind": "Reference", + "text": "i1.internal.SomeClass", + "canonicalReference": "api-extractor-scenarios!i1.internal.SomeClass:class" + }, + { + "kind": "Content", + "text": ";" + } + ], + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "someFunction" + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..60f137ad0d4 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.md @@ -0,0 +1,36 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +declare namespace i1 { + export { + internal + } +} +export { i1 } + +declare namespace i2 { + export { + internal + } +} +export { i2 } + +declare namespace internal { + export { + SomeClass + } +} + +// @public (undocumented) +class SomeClass { +} + +// @public (undocumented) +export function someFunction(): i1.internal.SomeClass; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/namespaceImports/rollup.d.ts new file mode 100644 index 00000000000..e19612205d5 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports/rollup.d.ts @@ -0,0 +1,28 @@ +declare namespace i1 { + export { + internal + } +} +export { i1 } + +declare namespace i2 { + export { + internal + } +} +export { i2 } + +declare namespace internal { + export { + SomeClass + } +} + +/** @public */ +declare class SomeClass { +} + +/** @public */ +export declare function someFunction(): i1.internal.SomeClass; + +export { } diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..8e0625d6aab --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.json @@ -0,0 +1,233 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1009, + "oldestForwardsCompatibleVersion": 1001, + "tsdocConfig": { + "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", + "noStandardTags": true, + "tagDefinitions": [ + { + "tagName": "@alpha", + "syntaxKind": "modifier" + }, + { + "tagName": "@beta", + "syntaxKind": "modifier" + }, + { + "tagName": "@defaultValue", + "syntaxKind": "block" + }, + { + "tagName": "@decorator", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@deprecated", + "syntaxKind": "block" + }, + { + "tagName": "@eventProperty", + "syntaxKind": "modifier" + }, + { + "tagName": "@example", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@experimental", + "syntaxKind": "modifier" + }, + { + "tagName": "@inheritDoc", + "syntaxKind": "inline" + }, + { + "tagName": "@internal", + "syntaxKind": "modifier" + }, + { + "tagName": "@label", + "syntaxKind": "inline" + }, + { + "tagName": "@link", + "syntaxKind": "inline", + "allowMultiple": true + }, + { + "tagName": "@override", + "syntaxKind": "modifier" + }, + { + "tagName": "@packageDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@param", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@privateRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@public", + "syntaxKind": "modifier" + }, + { + "tagName": "@readonly", + "syntaxKind": "modifier" + }, + { + "tagName": "@remarks", + "syntaxKind": "block" + }, + { + "tagName": "@returns", + "syntaxKind": "block" + }, + { + "tagName": "@sealed", + "syntaxKind": "modifier" + }, + { + "tagName": "@see", + "syntaxKind": "block" + }, + { + "tagName": "@throws", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@typeParam", + "syntaxKind": "block", + "allowMultiple": true + }, + { + "tagName": "@virtual", + "syntaxKind": "modifier" + }, + { + "tagName": "@betaDocumentation", + "syntaxKind": "modifier" + }, + { + "tagName": "@internalRemarks", + "syntaxKind": "block" + }, + { + "tagName": "@preapproved", + "syntaxKind": "modifier" + } + ], + "supportForTags": { + "@alpha": true, + "@beta": true, + "@defaultValue": true, + "@decorator": true, + "@deprecated": true, + "@eventProperty": true, + "@example": true, + "@experimental": true, + "@inheritDoc": true, + "@internal": true, + "@label": true, + "@link": true, + "@override": true, + "@packageDocumentation": true, + "@param": true, + "@privateRemarks": true, + "@public": true, + "@readonly": true, + "@remarks": true, + "@returns": true, + "@sealed": true, + "@see": true, + "@throws": true, + "@typeParam": true, + "@virtual": true, + "@betaDocumentation": true, + "@internalRemarks": true, + "@preapproved": true + }, + "reportUnsupportedHtmlElements": false + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "preserveMemberOrder": false, + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Namespace", + "canonicalReference": "api-extractor-scenarios!internal:namespace", + "docComment": "", + "excerptTokens": [], + "releaseTag": "None", + "name": "internal", + "preserveMemberOrder": false, + "members": [ + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!internal.SomeClass:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare class SomeClass " + } + ], + "releaseTag": "Public", + "name": "SomeClass", + "preserveMemberOrder": false, + "members": [], + "implementsTokenRanges": [] + } + ] + }, + { + "kind": "Function", + "canonicalReference": "api-extractor-scenarios!someFunction:function(1)", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export declare function someFunction(): " + }, + { + "kind": "Reference", + "text": "SomeClass", + "canonicalReference": "api-extractor-scenarios!SomeClass:class" + }, + { + "kind": "Content", + "text": ";" + } + ], + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "someFunction" + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..2c624b8845b --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports2/api-extractor-scenarios.api.md @@ -0,0 +1,23 @@ +## API Report File for "api-extractor-scenarios" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts + +declare namespace internal { + export { + SomeClass + } +} +export { internal } + +// @public (undocumented) +export class SomeClass { +} + +// @public (undocumented) +export function someFunction(): SomeClass; + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports2/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/namespaceImports2/rollup.d.ts new file mode 100644 index 00000000000..06bf37c06b6 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports2/rollup.d.ts @@ -0,0 +1,15 @@ +declare namespace internal { + export { + SomeClass + } +} +export { internal } + +/** @public */ +export declare class SomeClass { +} + +/** @public */ +export declare function someFunction(): SomeClass; + +export { } diff --git a/build-tests/api-extractor-scenarios/src/namespaceImports/index.ts b/build-tests/api-extractor-scenarios/src/namespaceImports/index.ts new file mode 100644 index 00000000000..5376cd34998 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/namespaceImports/index.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as i1 from './intermediate1'; +import * as i2 from './intermediate2'; + +export { i1, i2 }; + +/** @public */ +export function someFunction(): i1.internal.SomeClass { + return new i1.internal.SomeClass(); +} diff --git a/build-tests/api-extractor-scenarios/src/namespaceImports/intermediate1.ts b/build-tests/api-extractor-scenarios/src/namespaceImports/intermediate1.ts new file mode 100644 index 00000000000..59eb9ff7bc8 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/namespaceImports/intermediate1.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as internal from './internal'; +export { internal }; diff --git a/build-tests/api-extractor-scenarios/src/namespaceImports/intermediate2.ts b/build-tests/api-extractor-scenarios/src/namespaceImports/intermediate2.ts new file mode 100644 index 00000000000..59eb9ff7bc8 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/namespaceImports/intermediate2.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as internal from './internal'; +export { internal }; diff --git a/build-tests/api-extractor-scenarios/src/namespaceImports/internal.ts b/build-tests/api-extractor-scenarios/src/namespaceImports/internal.ts new file mode 100644 index 00000000000..6fd90b44eda --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/namespaceImports/internal.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** @public */ +export class SomeClass {} diff --git a/build-tests/api-extractor-scenarios/src/namespaceImports2/index.ts b/build-tests/api-extractor-scenarios/src/namespaceImports2/index.ts new file mode 100644 index 00000000000..03b57b5c9ab --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/namespaceImports2/index.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +import * as internal from './internal'; +import { SomeClass } from './internal'; + +export { internal, SomeClass }; + +/** @public */ +export function someFunction(): SomeClass { + return new SomeClass(); +} diff --git a/build-tests/api-extractor-scenarios/src/namespaceImports2/internal.ts b/build-tests/api-extractor-scenarios/src/namespaceImports2/internal.ts new file mode 100644 index 00000000000..6fd90b44eda --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/namespaceImports2/internal.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. +// See LICENSE in the project root for license information. + +/** @public */ +export class SomeClass {} From 1e11c5c1ac34fffba2f6f2b72dcb37dc674752f3 Mon Sep 17 00:00:00 2001 From: Zack Elliott Date: Wed, 7 Sep 2022 17:42:31 -0700 Subject: [PATCH 07/14] Added a comment --- .../src/generators/DeclarationReferenceGenerator.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts index 8b16a787ba5..49ce1124f75 100644 --- a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts +++ b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts @@ -272,6 +272,9 @@ export class DeclarationReferenceGenerator { const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile(); + // Note that it's possible for a symbol to be exported from an entry point as well as one or more + // namespaces. In that case, it's not clear what to choose as its parent. Today's logic is neither + // perfect nor particularly stable to API items being renamed and shuffled around. const entity: CollectorEntity | undefined = this._collector.tryGetEntityForSymbol(symbol); if (entity) { if (entity.exportedFromEntryPoint) { From 844f15b942280eb8e99ebc857b9bd55d5cad10d9 Mon Sep 17 00:00:00 2001 From: Frank Wienberg Date: Thu, 8 Sep 2022 19:18:12 +0200 Subject: [PATCH 08/14] [api-extractor] Fix logic when to fall back to Navigation.Exports As suggested by @zelliott, the logic when to fall back to Navigation.Exports (`.`) must use `consumable`, not `exported`. Updated the improved build-tests/api-extractor-scenarios results. --- .../src/generators/DeclarationReferenceGenerator.ts | 8 +++++--- .../exportImportStarAs2/api-extractor-scenarios.api.json | 2 +- .../api-extractor-scenarios.api.json | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts index 49ce1124f75..2706fa9bce7 100644 --- a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts +++ b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts @@ -110,10 +110,12 @@ export class DeclarationReferenceGenerator { return Navigation.Exports; } - // Otherwise, this symbol is from the current package. If we've found an exported CollectorEntity, then use - // Exports. + // Otherwise, this symbol is from the current package. If we've found an associated consumable + // `CollectorEntity`, then use Exports. We use `consumable` here instead of `exported` because + // if the symbol is exported from a non-consumable `AstNamespaceImport`, we don't want to use + // Exports. We should use Locals instead. const entity: CollectorEntity | undefined = this._collector.tryGetEntityForSymbol(symbol); - if (entity && entity.exported) { + if (entity?.consumable) { return Navigation.Exports; } diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json index 5d20caf9169..5ac4b1403d8 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json @@ -193,7 +193,7 @@ { "kind": "Reference", "text": "forgottenNs.ForgottenClass", - "canonicalReference": "api-extractor-scenarios!ForgottenClass:class" + "canonicalReference": "api-extractor-scenarios!~ForgottenClass:class" }, { "kind": "Content", diff --git a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json index 0e0bf04f003..532d190a90f 100644 --- a/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/includeForgottenExports/api-extractor-scenarios.api.json @@ -496,7 +496,7 @@ { "kind": "Reference", "text": "internal2.ForgottenExport6", - "canonicalReference": "api-extractor-scenarios!ForgottenExport6:class" + "canonicalReference": "api-extractor-scenarios!~ForgottenExport6:class" }, { "kind": "Content", From f423294770465dac94202c9d9b97ccc9665c46e1 Mon Sep 17 00:00:00 2001 From: Frank Wienberg Date: Mon, 29 Aug 2022 09:55:10 +0200 Subject: [PATCH 09/14] Add change log entry for fix of #3593 --- ...ctor-fix-3593-reference-alias_2022-08-29-07-53.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@microsoft/api-extractor/fwienber-api-extractor-fix-3593-reference-alias_2022-08-29-07-53.json diff --git a/common/changes/@microsoft/api-extractor/fwienber-api-extractor-fix-3593-reference-alias_2022-08-29-07-53.json b/common/changes/@microsoft/api-extractor/fwienber-api-extractor-fix-3593-reference-alias_2022-08-29-07-53.json new file mode 100644 index 00000000000..e6b3c88ffa4 --- /dev/null +++ b/common/changes/@microsoft/api-extractor/fwienber-api-extractor-fix-3593-reference-alias_2022-08-29-07-53.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/api-extractor", + "comment": "Fix #3593: Incorrect canonical references in *.api.json", + "type": "patch" + } + ], + "packageName": "@microsoft/api-extractor" +} \ No newline at end of file From a2f24853cda75dd292dce55a9c2605cd350ebd80 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Mon, 12 Sep 2022 15:23:27 -0700 Subject: [PATCH 10/14] rush build --- .../api-extractor-scenarios.api.json | 10 +++------- .../namespaceImports/api-extractor-scenarios.api.json | 10 +++------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json index fc67ce054b7..5ac4b1403d8 100644 --- a/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/exportImportStarAs2/api-extractor-scenarios.api.json @@ -190,14 +190,10 @@ "kind": "Content", "text": "export declare function exportedApi(): " }, - { - "kind": "Content", - "text": "forgottenNs." - }, { "kind": "Reference", - "text": "ForgottenClass", - "canonicalReference": "api-extractor-scenarios!ForgottenClass:class" + "text": "forgottenNs.ForgottenClass", + "canonicalReference": "api-extractor-scenarios!~ForgottenClass:class" }, { "kind": "Content", @@ -206,7 +202,7 @@ ], "returnTypeTokenRange": { "startIndex": 1, - "endIndex": 3 + "endIndex": 2 }, "releaseTag": "Public", "overloadIndex": 1, diff --git a/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json index b6b1a46acac..4f050369a44 100644 --- a/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/namespaceImports/api-extractor-scenarios.api.json @@ -257,14 +257,10 @@ "kind": "Content", "text": "export declare function someFunction(): " }, - { - "kind": "Content", - "text": "i1.internal." - }, { "kind": "Reference", - "text": "SomeClass", - "canonicalReference": "api-extractor-scenarios!SomeClass:class" + "text": "i1.internal.SomeClass", + "canonicalReference": "api-extractor-scenarios!i1.internal.SomeClass:class" }, { "kind": "Content", @@ -273,7 +269,7 @@ ], "returnTypeTokenRange": { "startIndex": 1, - "endIndex": 3 + "endIndex": 2 }, "releaseTag": "Public", "overloadIndex": 1, From 4df3810bd124eb67aa9d6f86589a5ce1a0d621f8 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Mon, 12 Sep 2022 15:49:48 -0700 Subject: [PATCH 11/14] Revert the .gitignore change; we'll implement it separately in this PR: https://github.com/microsoft/rushstack/pull/3622 --- .gitignore | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.gitignore b/.gitignore index a6561a5f6a9..4dd985482a1 100644 --- a/.gitignore +++ b/.gitignore @@ -80,7 +80,3 @@ dist # Heft .heft - -# IntelliJ IDEA -.idea -*.iml From 6d975b57287524ddd70dc163078eaabfba93b4ef Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Mon, 12 Sep 2022 15:56:43 -0700 Subject: [PATCH 12/14] Convert "firstExportingConsumableParent" property to "getFirstExportingConsumableParent()" method because it does nontrivial work --- apps/api-extractor/src/collector/CollectorEntity.ts | 2 +- .../src/generators/DeclarationReferenceGenerator.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api-extractor/src/collector/CollectorEntity.ts b/apps/api-extractor/src/collector/CollectorEntity.ts index 4c2a245aa95..db52e1f4ad9 100644 --- a/apps/api-extractor/src/collector/CollectorEntity.ts +++ b/apps/api-extractor/src/collector/CollectorEntity.ts @@ -179,7 +179,7 @@ export class CollectorEntity { * Return the first consumable parent that exports this entity. If there is none, returns * `undefined`. */ - public get firstExportingConsumableParent(): CollectorEntity | undefined { + public getFirstExportingConsumableParent(): CollectorEntity | undefined { for (const [parent, localExportNames] of this._localExportNamesByParent) { if (parent.consumable && localExportNames.size > 0) { return parent; diff --git a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts index 2706fa9bce7..c5008cba592 100644 --- a/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts +++ b/apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts @@ -284,7 +284,7 @@ export class DeclarationReferenceGenerator { } const firstExportingConsumableParent: CollectorEntity | undefined = - entity.firstExportingConsumableParent; + entity.getFirstExportingConsumableParent(); if ( firstExportingConsumableParent && firstExportingConsumableParent.astEntity instanceof AstNamespaceImport From 6f1811ef4bbf1c3c58f698cedf189789d68b54a9 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Mon, 12 Sep 2022 16:05:55 -0700 Subject: [PATCH 13/14] Update change file --- ...i-extractor-fix-3593-reference-alias_2022-08-29-07-53.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/changes/@microsoft/api-extractor/fwienber-api-extractor-fix-3593-reference-alias_2022-08-29-07-53.json b/common/changes/@microsoft/api-extractor/fwienber-api-extractor-fix-3593-reference-alias_2022-08-29-07-53.json index e6b3c88ffa4..5e475cc3da5 100644 --- a/common/changes/@microsoft/api-extractor/fwienber-api-extractor-fix-3593-reference-alias_2022-08-29-07-53.json +++ b/common/changes/@microsoft/api-extractor/fwienber-api-extractor-fix-3593-reference-alias_2022-08-29-07-53.json @@ -2,8 +2,8 @@ "changes": [ { "packageName": "@microsoft/api-extractor", - "comment": "Fix #3593: Incorrect canonical references in *.api.json", - "type": "patch" + "comment": "Fix an issue where aliased classes sometimes had incorrect canonical references in *.api.json (GitHub #3593)", + "type": "minor" } ], "packageName": "@microsoft/api-extractor" From 1ba1066bde42ecfae2461e1fe544c77b73cd4b58 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Mon, 12 Sep 2022 16:07:27 -0700 Subject: [PATCH 14/14] Update install-test-workspace lockfile --- .../install-test-workspace/workspace/common/pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml b/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml index d2a0cecd89a..15f9f5dd235 100644 --- a/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml +++ b/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml @@ -460,7 +460,7 @@ packages: dev: true /concat-map/0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} dev: true /cross-spawn/7.0.3: