Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[api-extractor] add basic support for import * as module from './local-module' #1796

Merged
merged 39 commits into from
Jun 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
efdde26
added support for 'import * as module from ./local-module'
mckn Mar 25, 2020
1a94da5
added change description.
mckn Mar 25, 2020
79b397e
fixed so we return a proper error messae.
mckn Mar 25, 2020
6e8b0e2
fixed build errors.
mckn Mar 26, 2020
9c3d85a
added missing annotation.
mckn Mar 26, 2020
ec5757a
fixed feedback on pr.
mckn Mar 27, 2020
a4ea1c1
added tests.
mckn Mar 30, 2020
90032e9
merged master into branch.
mckn Apr 2, 2020
64e2c66
fixed version missmatch.
mckn Apr 2, 2020
c88df79
Convert "api-extractor-test-no-report" to be a scenario test
octogonz Apr 7, 2020
dc9bb83
Convert test functions to be stereotypical functions instead of lambd…
octogonz Apr 7, 2020
24ddf89
Merge remote-tracking branch 'remotes/origin/master' into octogonz/ex…
octogonz Apr 16, 2020
0b81dfb
Adjust changelog
octogonz Apr 16, 2020
3651886
Improve test to show how top-level name conflicts are handled
octogonz Apr 17, 2020
de80229
Show how aliases are handled
octogonz Apr 17, 2020
dd449f4
Refactor AstEntity into a proper base class
octogonz May 16, 2020
04a5563
Merge remote-tracking branch 'remotes/origin/release/master-before-pr…
octogonz Aug 28, 2020
11db642
Reapply Prettier following the steps in this comment:
octogonz Aug 28, 2020
872bccb
Merge remote-tracking branch 'remotes/origin/master' into export-star…
octogonz Aug 28, 2020
9481016
Fix lint warnings from merge
octogonz Aug 28, 2020
828baad
Some minor cleanups (no semantic changes)
octogonz Aug 28, 2020
4fbadd8
Add class documentation, rename "exportName" to "namespaceName"
octogonz Aug 28, 2020
cfc693c
Rename AstImportAsModule --> AstNamespaceImport because that's what t…
octogonz Aug 28, 2020
2e75e83
Rename AstImportAsModule --> AstNamespaceImport because that's what t…
octogonz Aug 28, 2020
b2fa123
Tune up the code for generating the .d.ts rollup and add a location f…
octogonz Aug 28, 2020
857a27a
Start work on generating an API report for namespace imports
octogonz Aug 28, 2020
cdc1678
Merge branch 'master' into export-star-as-support
mckn Oct 1, 2020
88ce9ca
fixed issue in change log.
mckn Oct 1, 2020
7ff319d
Merge remote-tracking branch 'remotes/origin/master' into export-star…
octogonz Jun 25, 2021
615a387
rush build
octogonz Jun 25, 2021
2e100e6
Update lockfile
octogonz Jun 25, 2021
8cac97b
Include /// directives in API report, since they are relevant to API …
octogonz Jun 26, 2021
66636f4
Introduce CollectorEntity.consumable distinction to ensure that AstNa…
octogonz Jun 27, 2021
ec267b3
Fix an issue where DocCommentEnhancer was skipping AstNamespaceImport…
octogonz Jun 27, 2021
0ba9748
Eliminate trailing comma
octogonz Jun 27, 2021
51acb54
Add a second test case
octogonz Jun 30, 2021
12799aa
Implement the missing ae-forgotten-export analysis for AstNamespaceIm…
octogonz Jun 30, 2021
fcc005a
Update change log
octogonz Jun 30, 2021
73a3498
Improve the error message for not yet supported "export * as ___ from…
octogonz Jun 30, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion apps/api-extractor/.vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,13 @@
"name": "scenario",
"program": "${workspaceFolder}/lib/start.js",
"cwd": "${workspaceFolder}/../../build-tests/api-extractor-scenarios",
"args": ["--debug", "run", "--local", "--config", "./temp/configs/api-extractor-typeof.json"],
"args": [
"--debug",
"run",
"--local",
"--config",
"./temp/configs/api-extractor-exportImportStarAs2.json"
],
"sourceMaps": true
}
]
Expand Down
2 changes: 1 addition & 1 deletion apps/api-extractor/src/analyzer/AstDeclaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as ts from 'typescript';
import { AstSymbol } from './AstSymbol';
import { Span } from './Span';
import { InternalError } from '@rushstack/node-core-library';
import { AstEntity } from './AstSymbolTable';
import { AstEntity } from './AstEntity';

/**
* Constructor options for AstDeclaration
Expand Down
46 changes: 46 additions & 0 deletions apps/api-extractor/src/analyzer/AstEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

/**
* `AstEntity` is the abstract base class for analyzer objects that can become a `CollectorEntity`.
*
* @remarks
*
* The subclasses are:
* ```
* - AstEntity
* - AstSymbol
* - AstSyntheticEntity
* - AstImport
* - AstNamespaceImport
* ```
*/
export abstract class AstEntity {
/**
* The original name of the symbol, as exported from the module (i.e. source file)
* containing the original TypeScript definition. Constructs such as
* `import { X as Y } from` may introduce other names that differ from the local name.
*
* @remarks
* For the most part, `localName` corresponds to `followedSymbol.name`, but there
* are some edge cases. For example, the ts.Symbol.name for `export default class X { }`
* is actually `"default"`, not `"X"`.
*/
public abstract readonly localName: string;
}

/**
* `AstSyntheticEntity` is the abstract base class for analyzer objects whose emitted declarations
* are not text transformations performed by the `Span` helper.
*
* @remarks
* Most of API Extractor's output is produced by using the using the `Span` utility to regurgitate strings from
* the input .d.ts files. If we need to rename an identifier, the `Span` visitor can pick out an interesting
* node and rewrite its string, but otherwise the transformation operates on dumb text and not compiler concepts.
* (Historically we did this because the compiler's emitter was an internal API, but it still has some advantages,
* for example preserving syntaxes generated by an older compiler to avoid incompatibilities.)
*
* This strategy does not work for cases where the output looks very different from the input. Today these
* cases are always kinds of `import` statements, but that may change in the future.
*/
export abstract class AstSyntheticEntity extends AstEntity {}
11 changes: 6 additions & 5 deletions apps/api-extractor/src/analyzer/AstImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import { AstSymbol } from './AstSymbol';
import { InternalError } from '@rushstack/node-core-library';
import { AstSyntheticEntity } from './AstEntity';

/**
* Indicates the import kind for an `AstImport`.
Expand Down Expand Up @@ -49,7 +50,7 @@ export interface IAstImportOptions {
* For a symbol that was imported from an external package, this tracks the import
* statement that was used to reach it.
*/
export class AstImport {
export class AstImport extends AstSyntheticEntity {
public readonly importKind: AstImportKind;

/**
Expand Down Expand Up @@ -110,6 +111,8 @@ export class AstImport {
public readonly key: string;

public constructor(options: IAstImportOptions) {
super();

this.importKind = options.importKind;
this.modulePath = options.modulePath;
this.exportName = options.exportName;
Expand All @@ -120,11 +123,9 @@ export class AstImport {
this.key = AstImport.getKey(options);
}

/**
* Allows `AstEntity.localName` to be used as a convenient generalization of `AstSymbol.localName` and
* `AstImport.exportName`.
*/
/** {@inheritdoc} */
public get localName(): string {
// abstract
return this.exportName;
}

Expand Down
14 changes: 7 additions & 7 deletions apps/api-extractor/src/analyzer/AstModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import * as ts from 'typescript';

import { AstSymbol } from './AstSymbol';
import { AstEntity } from './AstSymbolTable';
import { AstEntity } from './AstEntity';

/**
* Represents information collected by {@link AstSymbolTable.fetchAstModuleExportInfo}
Expand All @@ -16,6 +16,12 @@ export class AstModuleExportInfo {

/**
* Constructor parameters for AstModule
*
* @privateRemarks
* Our naming convention is to use I____Parameters for constructor options and
* I____Options for general function options. However the word "parameters" is
* confusingly similar to the terminology for function parameters modeled by API Extractor,
* so we use I____Options for both cases in this code base.
*/
export interface IAstModuleOptions {
sourceFile: ts.SourceFile;
Expand All @@ -25,12 +31,6 @@ export interface IAstModuleOptions {

/**
* An internal data structure that represents a source file that is analyzed by AstSymbolTable.
*
* @privateRemarks
* Our naming convention is to use I____Parameters for constructor options and
* I____Options for general function options. However the word "parameters" is
* confusingly similar to the terminology for function parameters modeled by API Extractor,
* so we use I____Options for both cases in this code base.
*/
export class AstModule {
/**
Expand Down
89 changes: 89 additions & 0 deletions apps/api-extractor/src/analyzer/AstNamespaceImport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import * as ts from 'typescript';

import { AstModule, AstModuleExportInfo } from './AstModule';
import { AstSyntheticEntity } from './AstEntity';
import { Collector } from '../collector/Collector';

export interface IAstNamespaceImportOptions {
readonly astModule: AstModule;
readonly namespaceName: string;
readonly declaration: ts.Declaration;
}

/**
* `AstNamespaceImport` represents a namespace that is created implicitly by a statement
* such as `import * as example from "./file";`
*
* @remarks
*
* A typical input looks like this:
* ```ts
* // Suppose that example.ts exports two functions f1() and f2().
* import * as example from "./file";
* export { example };
* ```
*
* API Extractor's .d.ts rollup will transform it into an explicit namespace, like this:
* ```ts
* declare f1(): void;
* declare f2(): void;
*
* declare namespace example {
* export {
* f1,
* f2
* }
* }
* ```
*
* The current implementation does not attempt to relocate f1()/f2() to be inside the `namespace`
* because other type signatures may reference them directly (without using the namespace qualifier).
* The `declare namespace example` is a synthetic construct represented by `AstNamespaceImport`.
*/
export class AstNamespaceImport extends AstSyntheticEntity {
/**
* Returns true if the AstSymbolTable.analyze() was called for this object.
* See that function for details.
*/
public analyzed: boolean = false;

/**
* For example, if the original statement was `import * as example from "./file";`
* then `astModule` refers to the `./file.d.ts` file.
*/
public readonly astModule: AstModule;

/**
* For example, if the original statement was `import * as example from "./file";`
* then `namespaceName` would be `example`.
*/
public readonly namespaceName: string;

/**
* The original `ts.SyntaxKind.NamespaceImport` which can be used as a location for error messages.
*/
public readonly declaration: ts.Declaration;

public constructor(options: IAstNamespaceImportOptions) {
super();
this.astModule = options.astModule;
this.namespaceName = options.namespaceName;
this.declaration = options.declaration;
}

/** {@inheritdoc} */
public get localName(): string {
// abstract
return this.namespaceName;
}

public fetchAstModuleExportInfo(collector: Collector): AstModuleExportInfo {
const astModuleExportInfo: AstModuleExportInfo = collector.astSymbolTable.fetchAstModuleExportInfo(
this.astModule
);
return astModuleExportInfo;
}
}
9 changes: 5 additions & 4 deletions apps/api-extractor/src/analyzer/AstReferenceResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import * as ts from 'typescript';
import * as tsdoc from '@microsoft/tsdoc';

import { AstSymbolTable, AstEntity } from './AstSymbolTable';
import { AstSymbolTable } from './AstSymbolTable';
import { AstEntity } from './AstEntity';
import { AstDeclaration } from './AstDeclaration';
import { WorkingPackage } from '../collector/WorkingPackage';
import { AstModule } from './AstModule';
import { AstImport } from './AstImport';
import { Collector } from '../collector/Collector';
import { DeclarationMetadata } from '../collector/DeclarationMetadata';
import { AstSymbol } from './AstSymbol';

/**
* Used by `AstReferenceResolver` to report a failed resolution.
Expand Down Expand Up @@ -90,8 +91,8 @@ export class AstReferenceResolver {
);
}

if (rootAstEntity instanceof AstImport) {
return new ResolverFailure('Reexported declarations are not supported');
if (!(rootAstEntity instanceof AstSymbol)) {
return new ResolverFailure('This type of declaration is not supported yet by the resolver');
}

let currentDeclaration: AstDeclaration | ResolverFailure = this._selectDeclaration(
Expand Down
18 changes: 6 additions & 12 deletions apps/api-extractor/src/analyzer/AstSymbol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import * as ts from 'typescript';
import { AstDeclaration } from './AstDeclaration';
import { InternalError } from '@rushstack/node-core-library';
import { AstEntity } from './AstEntity';

/**
* Constructor options for AstSymbol
Expand Down Expand Up @@ -50,18 +51,9 @@ export interface IAstSymbolOptions {
* - g
* ```
*/
export class AstSymbol {
/**
* The original name of the symbol, as exported from the module (i.e. source file)
* containing the original TypeScript definition. Constructs such as
* `import { X as Y } from` may introduce other names that differ from the local name.
*
* @remarks
* For the most part, `localName` corresponds to `followedSymbol.name`, but there
* are some edge cases. For example, the symbol name for `export default class X { }`
* is actually `"default"`, not `"X"`.
*/
public readonly localName: string;
export class AstSymbol extends AstEntity {
/** {@inheritdoc} */
public readonly localName: string; // abstract

/**
* If true, then the `followedSymbol` (i.e. original declaration) of this symbol
Expand Down Expand Up @@ -121,6 +113,8 @@ export class AstSymbol {
private _analyzed: boolean = false;

public constructor(options: IAstSymbolOptions) {
super();

this.followedSymbol = options.followedSymbol;
this.localName = options.localName;
this.isExternal = options.isExternal;
Expand Down
Loading