From ca3f370377a62a3549aec9df0431bfaac79833f7 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Tue, 22 Mar 2022 18:27:53 -0700 Subject: [PATCH 01/14] Trying to get path mappings picked up in tsconfig. --- .../src/pathMappings/index.ts | 8 ++ .../api-extractor-scenarios/tsconfig.json | 8 +- .../workspace/common/pnpm-lock.yaml | 104 +++++++++--------- 3 files changed, 67 insertions(+), 53 deletions(-) create mode 100644 build-tests/api-extractor-scenarios/src/pathMappings/index.ts diff --git a/build-tests/api-extractor-scenarios/src/pathMappings/index.ts b/build-tests/api-extractor-scenarios/src/pathMappings/index.ts new file mode 100644 index 00000000000..044b0181f81 --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/pathMappings/index.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. + +// Does not work! +// export { AbstractClass } from 'apiItemKinds'; + +// Works! +// export { AbstractClass } from 'lib/apiItemKinds'; \ No newline at end of file diff --git a/build-tests/api-extractor-scenarios/tsconfig.json b/build-tests/api-extractor-scenarios/tsconfig.json index 1799652cc42..369be6856a2 100644 --- a/build-tests/api-extractor-scenarios/tsconfig.json +++ b/build-tests/api-extractor-scenarios/tsconfig.json @@ -10,7 +10,13 @@ "strictNullChecks": true, "types": ["node", "jest"], "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], - "outDir": "lib" + "outDir": "lib", + + // These properties are used for the pathMappings/ test scenario. + "baseUrl": ".", + "paths": { + "apiItemKinds/*": ["lib/apiItemKinds/*"], + } }, "include": ["src/**/*.ts", "typings/tsd.d.ts"] } 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 2517d776184..206c5deef24 100644 --- a/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml +++ b/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml @@ -4,28 +4,28 @@ importers: typescript-newest-test: specifiers: - '@rushstack/eslint-config': file:rushstack-eslint-config-2.5.1.tgz - '@rushstack/heft': file:rushstack-heft-0.44.2.tgz + '@rushstack/eslint-config': file:rushstack-eslint-config-2.5.2.tgz + '@rushstack/heft': file:rushstack-heft-0.44.3.tgz eslint: ~8.7.0 tslint: ~5.20.1 typescript: ~4.5.2 devDependencies: - '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.5.1.tgz_eslint@8.7.0+typescript@4.5.2 - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.2.tgz + '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.5.2.tgz_eslint@8.7.0+typescript@4.5.2 + '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.3.tgz eslint: 8.7.0 tslint: 5.20.1_typescript@4.5.2 typescript: 4.5.2 typescript-v3-test: specifiers: - '@rushstack/eslint-config': file:rushstack-eslint-config-2.5.1.tgz - '@rushstack/heft': file:rushstack-heft-0.44.2.tgz + '@rushstack/eslint-config': file:rushstack-eslint-config-2.5.2.tgz + '@rushstack/heft': file:rushstack-heft-0.44.3.tgz eslint: ~8.7.0 tslint: ~5.20.1 typescript: ~4.5.2 devDependencies: - '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.5.1.tgz_eslint@8.7.0+typescript@4.5.2 - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.2.tgz + '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.5.2.tgz_eslint@8.7.0+typescript@4.5.2 + '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.3.tgz eslint: 8.7.0 tslint: 5.20.1_typescript@4.5.2 typescript: 4.5.2 @@ -1640,19 +1640,19 @@ packages: commander: 2.20.3 dev: true - file:../temp/tarballs/rushstack-eslint-config-2.5.1.tgz_eslint@8.7.0+typescript@4.5.2: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-config-2.5.1.tgz} - id: file:../temp/tarballs/rushstack-eslint-config-2.5.1.tgz + file:../temp/tarballs/rushstack-eslint-config-2.5.2.tgz_eslint@8.7.0+typescript@4.5.2: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-config-2.5.2.tgz} + id: file:../temp/tarballs/rushstack-eslint-config-2.5.2.tgz name: '@rushstack/eslint-config' - version: 2.5.1 + version: 2.5.2 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 typescript: '>=3.0.0' dependencies: - '@rushstack/eslint-patch': file:../temp/tarballs/rushstack-eslint-patch-1.1.0.tgz - '@rushstack/eslint-plugin': file:../temp/tarballs/rushstack-eslint-plugin-0.8.4.tgz_eslint@8.7.0+typescript@4.5.2 - '@rushstack/eslint-plugin-packlets': file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.4.tgz_eslint@8.7.0+typescript@4.5.2 - '@rushstack/eslint-plugin-security': file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.4.tgz_eslint@8.7.0+typescript@4.5.2 + '@rushstack/eslint-patch': file:../temp/tarballs/rushstack-eslint-patch-1.1.1.tgz + '@rushstack/eslint-plugin': file:../temp/tarballs/rushstack-eslint-plugin-0.8.5.tgz_eslint@8.7.0+typescript@4.5.2 + '@rushstack/eslint-plugin-packlets': file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.5.tgz_eslint@8.7.0+typescript@4.5.2 + '@rushstack/eslint-plugin-security': file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.5.tgz_eslint@8.7.0+typescript@4.5.2 '@typescript-eslint/eslint-plugin': 5.6.0_d466ed60638eced99d543a223feeb14a '@typescript-eslint/experimental-utils': 5.6.0_eslint@8.7.0+typescript@4.5.2 '@typescript-eslint/parser': 5.6.0_eslint@8.7.0+typescript@4.5.2 @@ -1666,17 +1666,17 @@ packages: - supports-color dev: true - file:../temp/tarballs/rushstack-eslint-patch-1.1.0.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-patch-1.1.0.tgz} + file:../temp/tarballs/rushstack-eslint-patch-1.1.1.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-patch-1.1.1.tgz} name: '@rushstack/eslint-patch' - version: 1.1.0 + version: 1.1.1 dev: true - file:../temp/tarballs/rushstack-eslint-plugin-0.8.4.tgz_eslint@8.7.0+typescript@4.5.2: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-0.8.4.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-0.8.4.tgz + file:../temp/tarballs/rushstack-eslint-plugin-0.8.5.tgz_eslint@8.7.0+typescript@4.5.2: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-0.8.5.tgz} + id: file:../temp/tarballs/rushstack-eslint-plugin-0.8.5.tgz name: '@rushstack/eslint-plugin' - version: 0.8.4 + version: 0.8.5 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: @@ -1688,11 +1688,11 @@ packages: - typescript dev: true - file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.4.tgz_eslint@8.7.0+typescript@4.5.2: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.4.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.4.tgz + file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.5.tgz_eslint@8.7.0+typescript@4.5.2: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.5.tgz} + id: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.5.tgz name: '@rushstack/eslint-plugin-packlets' - version: 0.3.4 + version: 0.3.5 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: @@ -1704,11 +1704,11 @@ packages: - typescript dev: true - file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.4.tgz_eslint@8.7.0+typescript@4.5.2: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.4.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.4.tgz + file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.5.tgz_eslint@8.7.0+typescript@4.5.2: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.5.tgz} + id: file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.5.tgz name: '@rushstack/eslint-plugin-security' - version: 0.2.4 + version: 0.2.5 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: @@ -1720,17 +1720,17 @@ packages: - typescript dev: true - file:../temp/tarballs/rushstack-heft-0.44.2.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-0.44.2.tgz} + file:../temp/tarballs/rushstack-heft-0.44.3.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-heft-0.44.3.tgz} name: '@rushstack/heft' - version: 0.44.2 + version: 0.44.3 engines: {node: '>=10.13.0'} hasBin: true dependencies: - '@rushstack/heft-config-file': file:../temp/tarballs/rushstack-heft-config-file-0.7.11.tgz - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.45.0.tgz - '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.7.tgz - '@rushstack/ts-command-line': file:../temp/tarballs/rushstack-ts-command-line-4.10.6.tgz + '@rushstack/heft-config-file': file:../temp/tarballs/rushstack-heft-config-file-0.7.12.tgz + '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.45.1.tgz + '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.8.tgz + '@rushstack/ts-command-line': file:../temp/tarballs/rushstack-ts-command-line-4.10.7.tgz '@types/tapable': 1.0.6 argparse: 1.0.10 chokidar: 3.4.3 @@ -1743,21 +1743,21 @@ packages: true-case-path: 2.2.1 dev: true - file:../temp/tarballs/rushstack-heft-config-file-0.7.11.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-config-file-0.7.11.tgz} + file:../temp/tarballs/rushstack-heft-config-file-0.7.12.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-heft-config-file-0.7.12.tgz} name: '@rushstack/heft-config-file' - version: 0.7.11 + version: 0.7.12 engines: {node: '>=10.13.0'} dependencies: - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.45.0.tgz - '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.7.tgz + '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.45.1.tgz + '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.8.tgz jsonpath-plus: 4.0.0 dev: true - file:../temp/tarballs/rushstack-node-core-library-3.45.0.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-node-core-library-3.45.0.tgz} + file:../temp/tarballs/rushstack-node-core-library-3.45.1.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-node-core-library-3.45.1.tgz} name: '@rushstack/node-core-library' - version: 3.45.0 + version: 3.45.1 dependencies: '@types/node': 12.20.24 colors: 1.2.5 @@ -1770,10 +1770,10 @@ packages: z-schema: 5.0.2 dev: true - file:../temp/tarballs/rushstack-rig-package-0.3.7.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-rig-package-0.3.7.tgz} + file:../temp/tarballs/rushstack-rig-package-0.3.8.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-rig-package-0.3.8.tgz} name: '@rushstack/rig-package' - version: 0.3.7 + version: 0.3.8 dependencies: resolve: 1.17.0 strip-json-comments: 3.1.1 @@ -1785,10 +1785,10 @@ packages: version: 0.2.2 dev: true - file:../temp/tarballs/rushstack-ts-command-line-4.10.6.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-ts-command-line-4.10.6.tgz} + file:../temp/tarballs/rushstack-ts-command-line-4.10.7.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-ts-command-line-4.10.7.tgz} name: '@rushstack/ts-command-line' - version: 4.10.6 + version: 4.10.7 dependencies: '@types/argparse': 1.0.38 argparse: 1.0.10 From 424204f017e8f5c7145fd76b4967896bb1c277c7 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Tue, 5 Apr 2022 14:11:25 -0700 Subject: [PATCH 02/14] isExternalModulePath now takes path mappings into consideration --- .../src/analyzer/ExportAnalyzer.ts | 31 ++ .../api-extractor-scenarios.api.json | 323 ++++++++++++++++++ .../api-extractor-scenarios.api.md | 33 ++ .../etc/test-outputs/pathMappings/rollup.d.ts | 20 ++ .../src/ambientModule/bar.ts | 1 + .../src/pathMappings/index.ts | 7 +- .../api-extractor-scenarios/tsconfig.json | 5 +- 7 files changed, 413 insertions(+), 7 deletions(-) create mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json create mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md create mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts create mode 100644 build-tests/api-extractor-scenarios/src/ambientModule/bar.ts diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index 12d828a87cf..349631f809b 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -279,6 +279,10 @@ export class ExportAnalyzer { return false; } + if (this.hasPathMappingMatch(moduleSpecifier)) { + return false; + } + const match: RegExpExecArray | null = ExportAnalyzer._modulePathRegExp.exec(moduleSpecifier); if (match) { // Extract "@my-scope/my-package" from "@my-scope/my-package/path/module" @@ -291,6 +295,33 @@ export class ExportAnalyzer { return true; } + /** + * Returns true if the module specifier matches a path mapping entry in the tsconfig `paths` map. + * + * @remarks + * Handles two types of path mappings: + * + * - Simple path-like strings: `some/path/to/import` + * - Paths with wildcards at the end: `some/path/to/*` + */ + private hasPathMappingMatch(moduleSpecifier: string): boolean { + const pathKeys: string[] = Object.keys(this._program.getCompilerOptions().paths || {}); + for (const pathKey of pathKeys) { + if (pathKey.endsWith('*')) { + const pathPrefix: string = pathKey.slice(0, -1); + if (moduleSpecifier.startsWith(pathPrefix)) { + return true; + } + } else { + if (pathKey === moduleSpecifier) { + return true; + } + } + } + + return false; + } + /** * Returns true if when we analyzed sourceFile, we found that it contains an "export=" statement that allows * it to behave /either/ as an ambient module /or/ as a regular importable module. In this case, diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..32734482027 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json @@ -0,0 +1,323 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1004, + "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 + } + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "members": [ + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!AbstractClass:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export abstract class AbstractClass " + } + ], + "releaseTag": "Public", + "name": "AbstractClass", + "members": [ + { + "kind": "Method", + "canonicalReference": "api-extractor-scenarios!AbstractClass#member:member(1)", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public abstract member(): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isOptional": false, + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "member" + } + ], + "implementsTokenRanges": [] + }, + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!SimpleClass:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export class SimpleClass " + } + ], + "releaseTag": "Public", + "name": "SimpleClass", + "members": [ + { + "kind": "Method", + "canonicalReference": "api-extractor-scenarios!SimpleClass#member:member(1)", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public member(): " + }, + { + "kind": "Content", + "text": "void " + }, + { + "kind": "Content", + "text": "{}" + } + ], + "isOptional": false, + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "member" + }, + { + "kind": "Property", + "canonicalReference": "api-extractor-scenarios!SimpleClass#readonlyProperty:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public get readonlyProperty(): " + }, + { + "kind": "Content", + "text": "string " + }, + { + "kind": "Content", + "text": "{\n return 'hello';\n }" + } + ], + "isOptional": false, + "releaseTag": "Public", + "name": "readonlyProperty", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isStatic": false + }, + { + "kind": "Property", + "canonicalReference": "api-extractor-scenarios!SimpleClass#writeableProperty:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public get writeableProperty(): " + }, + { + "kind": "Content", + "text": "string " + }, + { + "kind": "Content", + "text": "{\n return 'hello';\n }" + }, + { + "kind": "Content", + "text": "\n\npublic set writeableProperty(value: string) {}" + } + ], + "isOptional": false, + "releaseTag": "Public", + "name": "writeableProperty", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isStatic": false + } + ], + "implementsTokenRanges": [] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..5e911a4639d --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md @@ -0,0 +1,33 @@ +## 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 abstract class AbstractClass { + // (undocumented) + public abstract member(): void; +} + +// @public (undocumented) +export class SimpleClass { + // (undocumented) + public member(): void {} + + // (undocumented) + public get readonlyProperty(): string { + return 'hello'; + } + + // (undocumented) + public get writeableProperty(): string { + return 'hello'; + } + + public set writeableProperty(value: string) {} +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts new file mode 100644 index 00000000000..e48d6cf15a7 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts @@ -0,0 +1,20 @@ +/** @public */ +export declare abstract class AbstractClass { + public abstract member(): void; +} + +/** @public */ +export declare class SimpleClass { + public member(): void {} + + public get readonlyProperty(): string { + return 'hello'; + } + + public get writeableProperty(): string { + return 'hello'; + } + public set writeableProperty(value: string) {} +} + +export { } diff --git a/build-tests/api-extractor-scenarios/src/ambientModule/bar.ts b/build-tests/api-extractor-scenarios/src/ambientModule/bar.ts new file mode 100644 index 00000000000..096fb45dc8b --- /dev/null +++ b/build-tests/api-extractor-scenarios/src/ambientModule/bar.ts @@ -0,0 +1 @@ +export class Bar {} diff --git a/build-tests/api-extractor-scenarios/src/pathMappings/index.ts b/build-tests/api-extractor-scenarios/src/pathMappings/index.ts index 044b0181f81..fadbfa6c3e0 100644 --- a/build-tests/api-extractor-scenarios/src/pathMappings/index.ts +++ b/build-tests/api-extractor-scenarios/src/pathMappings/index.ts @@ -1,8 +1,5 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. -// Does not work! -// export { AbstractClass } from 'apiItemKinds'; - -// Works! -// export { AbstractClass } from 'lib/apiItemKinds'; \ No newline at end of file +export { AbstractClass } from 'path-mapping'; +export { SimpleClass } from 'wildcard-path-mapping/apiItemKinds'; diff --git a/build-tests/api-extractor-scenarios/tsconfig.json b/build-tests/api-extractor-scenarios/tsconfig.json index 369be6856a2..ced6570b1b2 100644 --- a/build-tests/api-extractor-scenarios/tsconfig.json +++ b/build-tests/api-extractor-scenarios/tsconfig.json @@ -12,10 +12,11 @@ "lib": ["es5", "scripthost", "es2015.collection", "es2015.promise", "es2015.iterable", "dom"], "outDir": "lib", - // These properties are used for the pathMappings/ test scenario. + // These properties are only used by the pathMappings/ test scenario. "baseUrl": ".", "paths": { - "apiItemKinds/*": ["lib/apiItemKinds/*"], + "path-mapping": ["src/apiItemKinds"], + "wildcard-path-mapping/*": ["src/*"] } }, "include": ["src/**/*.ts", "typings/tsd.d.ts"] From 451383bb663d92c2d8a8f9991810e146834db5ae Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Tue, 5 Apr 2022 14:36:10 -0700 Subject: [PATCH 03/14] Nit: added _ prefix to private method --- apps/api-extractor/src/analyzer/ExportAnalyzer.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index 349631f809b..053d64104f5 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -273,13 +273,15 @@ export class ExportAnalyzer { * - NO: `./file1` * - YES: `library1/path/path` * - YES: `@my-scope/my-package` + * - NO: `@my-scope/my-package` (if present in tsconfig `paths` mapping). */ private _isExternalModulePath(moduleSpecifier: string): boolean { if (ts.isExternalModuleNameRelative(moduleSpecifier)) { return false; } - if (this.hasPathMappingMatch(moduleSpecifier)) { + // Any module specifiers that match a path mapping entry are considered part of the current package. + if (this._hasPathMappingMatch(moduleSpecifier)) { return false; } @@ -304,7 +306,7 @@ export class ExportAnalyzer { * - Simple path-like strings: `some/path/to/import` * - Paths with wildcards at the end: `some/path/to/*` */ - private hasPathMappingMatch(moduleSpecifier: string): boolean { + private _hasPathMappingMatch(moduleSpecifier: string): boolean { const pathKeys: string[] = Object.keys(this._program.getCompilerOptions().paths || {}); for (const pathKey of pathKeys) { if (pathKey.endsWith('*')) { From dd65747cd0bde8da85727c27236f73547bed4b45 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Tue, 5 Apr 2022 19:29:50 -0700 Subject: [PATCH 04/14] Ran rush rebuild --- .../api-extractor-scenarios.api.json | 323 ------------------ .../api-extractor-scenarios.api.md | 33 -- .../etc/test-outputs/pathMappings/rollup.d.ts | 20 -- .../src/ambientModule/bar.ts | 1 - .../workspace/common/pnpm-lock.yaml | 14 +- 5 files changed, 7 insertions(+), 384 deletions(-) delete mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json delete mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md delete mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts delete mode 100644 build-tests/api-extractor-scenarios/src/ambientModule/bar.ts diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json deleted file mode 100644 index 32734482027..00000000000 --- a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json +++ /dev/null @@ -1,323 +0,0 @@ -{ - "metadata": { - "toolPackage": "@microsoft/api-extractor", - "toolVersion": "[test mode]", - "schemaVersion": 1004, - "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 - } - } - }, - "kind": "Package", - "canonicalReference": "api-extractor-scenarios!", - "docComment": "", - "name": "api-extractor-scenarios", - "members": [ - { - "kind": "EntryPoint", - "canonicalReference": "api-extractor-scenarios!", - "name": "", - "members": [ - { - "kind": "Class", - "canonicalReference": "api-extractor-scenarios!AbstractClass:class", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export abstract class AbstractClass " - } - ], - "releaseTag": "Public", - "name": "AbstractClass", - "members": [ - { - "kind": "Method", - "canonicalReference": "api-extractor-scenarios!AbstractClass#member:member(1)", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "public abstract member(): " - }, - { - "kind": "Content", - "text": "void" - }, - { - "kind": "Content", - "text": ";" - } - ], - "isOptional": false, - "isStatic": false, - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "member" - } - ], - "implementsTokenRanges": [] - }, - { - "kind": "Class", - "canonicalReference": "api-extractor-scenarios!SimpleClass:class", - "docComment": "/**\n * @public\n */\n", - "excerptTokens": [ - { - "kind": "Content", - "text": "export class SimpleClass " - } - ], - "releaseTag": "Public", - "name": "SimpleClass", - "members": [ - { - "kind": "Method", - "canonicalReference": "api-extractor-scenarios!SimpleClass#member:member(1)", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "public member(): " - }, - { - "kind": "Content", - "text": "void " - }, - { - "kind": "Content", - "text": "{}" - } - ], - "isOptional": false, - "isStatic": false, - "returnTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "releaseTag": "Public", - "overloadIndex": 1, - "parameters": [], - "name": "member" - }, - { - "kind": "Property", - "canonicalReference": "api-extractor-scenarios!SimpleClass#readonlyProperty:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "public get readonlyProperty(): " - }, - { - "kind": "Content", - "text": "string " - }, - { - "kind": "Content", - "text": "{\n return 'hello';\n }" - } - ], - "isOptional": false, - "releaseTag": "Public", - "name": "readonlyProperty", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isStatic": false - }, - { - "kind": "Property", - "canonicalReference": "api-extractor-scenarios!SimpleClass#writeableProperty:member", - "docComment": "", - "excerptTokens": [ - { - "kind": "Content", - "text": "public get writeableProperty(): " - }, - { - "kind": "Content", - "text": "string " - }, - { - "kind": "Content", - "text": "{\n return 'hello';\n }" - }, - { - "kind": "Content", - "text": "\n\npublic set writeableProperty(value: string) {}" - } - ], - "isOptional": false, - "releaseTag": "Public", - "name": "writeableProperty", - "propertyTypeTokenRange": { - "startIndex": 1, - "endIndex": 2 - }, - "isStatic": false - } - ], - "implementsTokenRanges": [] - } - ] - } - ] -} diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md deleted file mode 100644 index 5e911a4639d..00000000000 --- a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md +++ /dev/null @@ -1,33 +0,0 @@ -## 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 abstract class AbstractClass { - // (undocumented) - public abstract member(): void; -} - -// @public (undocumented) -export class SimpleClass { - // (undocumented) - public member(): void {} - - // (undocumented) - public get readonlyProperty(): string { - return 'hello'; - } - - // (undocumented) - public get writeableProperty(): string { - return 'hello'; - } - - public set writeableProperty(value: string) {} -} - -// (No @packageDocumentation comment for this package) - -``` diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts deleted file mode 100644 index e48d6cf15a7..00000000000 --- a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** @public */ -export declare abstract class AbstractClass { - public abstract member(): void; -} - -/** @public */ -export declare class SimpleClass { - public member(): void {} - - public get readonlyProperty(): string { - return 'hello'; - } - - public get writeableProperty(): string { - return 'hello'; - } - public set writeableProperty(value: string) {} -} - -export { } diff --git a/build-tests/api-extractor-scenarios/src/ambientModule/bar.ts b/build-tests/api-extractor-scenarios/src/ambientModule/bar.ts deleted file mode 100644 index 096fb45dc8b..00000000000 --- a/build-tests/api-extractor-scenarios/src/ambientModule/bar.ts +++ /dev/null @@ -1 +0,0 @@ -export class Bar {} 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 cb68e3e336f..24daa6581aa 100644 --- a/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml +++ b/build-tests/install-test-workspace/workspace/common/pnpm-lock.yaml @@ -5,13 +5,13 @@ importers: typescript-newest-test: specifiers: '@rushstack/eslint-config': file:rushstack-eslint-config-2.5.2.tgz - '@rushstack/heft': file:rushstack-heft-0.44.4.tgz + '@rushstack/heft': file:rushstack-heft-0.44.5.tgz eslint: ~8.7.0 tslint: ~5.20.1 typescript: ~4.5.2 devDependencies: '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.5.2.tgz_eslint@8.7.0+typescript@4.5.2 - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.4.tgz + '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.5.tgz eslint: 8.7.0 tslint: 5.20.1_typescript@4.5.2 typescript: 4.5.2 @@ -19,13 +19,13 @@ importers: typescript-v3-test: specifiers: '@rushstack/eslint-config': file:rushstack-eslint-config-2.5.2.tgz - '@rushstack/heft': file:rushstack-heft-0.44.4.tgz + '@rushstack/heft': file:rushstack-heft-0.44.5.tgz eslint: ~8.7.0 tslint: ~5.20.1 typescript: ~4.5.2 devDependencies: '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.5.2.tgz_eslint@8.7.0+typescript@4.5.2 - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.4.tgz + '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.5.tgz eslint: 8.7.0 tslint: 5.20.1_typescript@4.5.2 typescript: 4.5.2 @@ -1720,10 +1720,10 @@ packages: - typescript dev: true - file:../temp/tarballs/rushstack-heft-0.44.4.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-0.44.4.tgz} + file:../temp/tarballs/rushstack-heft-0.44.5.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-heft-0.44.5.tgz} name: '@rushstack/heft' - version: 0.44.4 + version: 0.44.5 engines: {node: '>=10.13.0'} hasBin: true dependencies: From 2958132894bf6e4f073a51c743f2d2c0b2832bc6 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Tue, 5 Apr 2022 19:38:57 -0700 Subject: [PATCH 05/14] Added rush change file --- .../api-extractor/path-mapping_2022-04-06-02-38.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json diff --git a/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json b/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json new file mode 100644 index 00000000000..7a82c233e1b --- /dev/null +++ b/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/api-extractor", + "comment": "Bug fix to API Extractor to properly process projects with path mappings in their tsconfig.", + "type": "patch" + } + ], + "packageName": "@microsoft/api-extractor" +} \ No newline at end of file From 5f4c6d146d9c2aa0babbced101a44f980cbde651 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Wed, 6 Apr 2022 07:24:31 -0700 Subject: [PATCH 06/14] Added test output for pathMappings test scenario --- .../config/build-config.json | 1 + .../api-extractor-scenarios.api.json | 369 ++++++++++++++++++ .../api-extractor-scenarios.api.md | 36 ++ .../etc/test-outputs/pathMappings/rollup.d.ts | 22 ++ 4 files changed, 428 insertions(+) create mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json create mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md create mode 100644 build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts diff --git a/build-tests/api-extractor-scenarios/config/build-config.json b/build-tests/api-extractor-scenarios/config/build-config.json index dc194fe5192..a6c0a5e7e64 100644 --- a/build-tests/api-extractor-scenarios/config/build-config.json +++ b/build-tests/api-extractor-scenarios/config/build-config.json @@ -34,6 +34,7 @@ "inconsistentReleaseTags", "internationalCharacters", "namedDefaultImport", + "pathMappings", "preapproved", "spanSorting", "typeOf", diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json new file mode 100644 index 00000000000..52a5a9ada72 --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json @@ -0,0 +1,369 @@ +{ + "metadata": { + "toolPackage": "@microsoft/api-extractor", + "toolVersion": "[test mode]", + "schemaVersion": 1005, + "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 + } + } + }, + "kind": "Package", + "canonicalReference": "api-extractor-scenarios!", + "docComment": "", + "name": "api-extractor-scenarios", + "members": [ + { + "kind": "EntryPoint", + "canonicalReference": "api-extractor-scenarios!", + "name": "", + "members": [ + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!AbstractClass:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export abstract class AbstractClass " + } + ], + "releaseTag": "Public", + "name": "AbstractClass", + "members": [ + { + "kind": "Method", + "canonicalReference": "api-extractor-scenarios!AbstractClass#member:member(1)", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public abstract member(): " + }, + { + "kind": "Content", + "text": "void" + }, + { + "kind": "Content", + "text": ";" + } + ], + "isOptional": false, + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "member" + } + ], + "implementsTokenRanges": [] + }, + { + "kind": "Class", + "canonicalReference": "api-extractor-scenarios!SimpleClass:class", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export class SimpleClass " + } + ], + "releaseTag": "Public", + "name": "SimpleClass", + "members": [ + { + "kind": "Method", + "canonicalReference": "api-extractor-scenarios!SimpleClass#member:member(1)", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public member(): " + }, + { + "kind": "Content", + "text": "void " + }, + { + "kind": "Content", + "text": "{}" + } + ], + "isOptional": false, + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [], + "name": "member" + }, + { + "kind": "Method", + "canonicalReference": "api-extractor-scenarios!SimpleClass#optionalParamMethod:member(1)", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public optionalParamMethod(x?: " + }, + { + "kind": "Content", + "text": "number" + }, + { + "kind": "Content", + "text": "): " + }, + { + "kind": "Content", + "text": "void " + }, + { + "kind": "Content", + "text": "{}" + } + ], + "isOptional": false, + "isStatic": false, + "returnTypeTokenRange": { + "startIndex": 3, + "endIndex": 4 + }, + "releaseTag": "Public", + "overloadIndex": 1, + "parameters": [ + { + "parameterName": "x", + "parameterTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isOptional": true + } + ], + "name": "optionalParamMethod" + }, + { + "kind": "Property", + "canonicalReference": "api-extractor-scenarios!SimpleClass#readonlyProperty:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public get readonlyProperty(): " + }, + { + "kind": "Content", + "text": "string " + }, + { + "kind": "Content", + "text": "{\n return 'hello';\n }" + } + ], + "isOptional": false, + "releaseTag": "Public", + "name": "readonlyProperty", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isStatic": false + }, + { + "kind": "Property", + "canonicalReference": "api-extractor-scenarios!SimpleClass#writeableProperty:member", + "docComment": "", + "excerptTokens": [ + { + "kind": "Content", + "text": "public get writeableProperty(): " + }, + { + "kind": "Content", + "text": "string " + }, + { + "kind": "Content", + "text": "{\n return 'hello';\n }" + }, + { + "kind": "Content", + "text": "\n\npublic set writeableProperty(value: string) {}" + } + ], + "isOptional": false, + "releaseTag": "Public", + "name": "writeableProperty", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isStatic": false + } + ], + "implementsTokenRanges": [] + } + ] + } + ] +} diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md new file mode 100644 index 00000000000..76b024aaf9b --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/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 + +// @public (undocumented) +export abstract class AbstractClass { + // (undocumented) + public abstract member(): void; +} + +// @public (undocumented) +export class SimpleClass { + // (undocumented) + public member(): void {} + + // (undocumented) + public optionalParamMethod(x?: number): void {} + + // (undocumented) + public get readonlyProperty(): string { + return 'hello'; + } + + // (undocumented) + public get writeableProperty(): string { + return 'hello'; + } + + public set writeableProperty(value: string) {} +} + +// (No @packageDocumentation comment for this package) + +``` diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts new file mode 100644 index 00000000000..6b95ca7356a --- /dev/null +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts @@ -0,0 +1,22 @@ +/** @public */ +export declare abstract class AbstractClass { + public abstract member(): void; +} + +/** @public */ +export declare class SimpleClass { + public member(): void {} + + public optionalParamMethod(x?: number): void {} + + public get readonlyProperty(): string { + return 'hello'; + } + + public get writeableProperty(): string { + return 'hello'; + } + public set writeableProperty(value: string) {} +} + +export { } From 93aea59bda9080333f46452c1bda73a1dbbb1034 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Thu, 7 Apr 2022 07:48:19 -0700 Subject: [PATCH 07/14] Optimization to hasPathMappingMatch --- .../src/analyzer/ExportAnalyzer.ts | 31 +++++++++++++------ 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index 053d64104f5..ce1187bf9b2 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -77,6 +77,10 @@ export class ExportAnalyzer { private readonly _astImportsByKey: Map = new Map(); private readonly _astNamespaceImportByModule: Map = new Map(); + // Used and populated by `hasPathMappingMatch`. + private readonly _exactPathMappings: Set = new Set(); + private readonly _prefixPathMappings: string[] = []; + public constructor( program: ts.Program, typeChecker: ts.TypeChecker, @@ -307,21 +311,28 @@ export class ExportAnalyzer { * - Paths with wildcards at the end: `some/path/to/*` */ private _hasPathMappingMatch(moduleSpecifier: string): boolean { + // If there are no path mappings, there cannot be a match. const pathKeys: string[] = Object.keys(this._program.getCompilerOptions().paths || {}); - for (const pathKey of pathKeys) { - if (pathKey.endsWith('*')) { - const pathPrefix: string = pathKey.slice(0, -1); - if (moduleSpecifier.startsWith(pathPrefix)) { - return true; - } - } else { - if (pathKey === moduleSpecifier) { - return true; + if (pathKeys.length === 0) { + return false; + } + + // Otherwise, if we haven't already, initialize some data structures to optimize how we evaluate path + // mapping matches. This will only be run once for all calls of `_hasPathMappingMatch`. + if (this._exactPathMappings.size === 0 && this._prefixPathMappings.length === 0) { + for (const pathKey of pathKeys) { + if (pathKey.endsWith('*')) { + this._prefixPathMappings.push(pathKey.slice(0, -1)); + } else { + this._exactPathMappings.add(pathKey); } } } - return false; + return ( + this._exactPathMappings.has(moduleSpecifier) || + this._prefixPathMappings.some((prefix) => moduleSpecifier.startsWith(prefix)) + ); } /** From 81dd926261f66852c329403cb9c70957b69416d3 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Thu, 7 Apr 2022 07:50:48 -0700 Subject: [PATCH 08/14] Nit comment change --- apps/api-extractor/src/analyzer/ExportAnalyzer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index ce1187bf9b2..f129aa0566e 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -77,7 +77,7 @@ export class ExportAnalyzer { private readonly _astImportsByKey: Map = new Map(); private readonly _astNamespaceImportByModule: Map = new Map(); - // Used and populated by `hasPathMappingMatch`. + // Used and populated by `_hasPathMappingMatch`. private readonly _exactPathMappings: Set = new Set(); private readonly _prefixPathMappings: string[] = []; From 90a4885416936e09a0bd89543caa4a624d698dd3 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Fri, 8 Apr 2022 10:17:25 -0700 Subject: [PATCH 09/14] Updated isExternalModulePath to use getResolvedModule --- .../src/analyzer/AstSymbolTable.ts | 2 +- .../src/analyzer/ExportAnalyzer.ts | 182 +++++++----------- .../etc/api-extractor-lib3-test.api.md | 1 - .../api-extractor-scenarios.api.json | 79 ++++++++ .../api-extractor-scenarios.api.md | 9 + .../etc/test-outputs/pathMappings/rollup.d.ts | 18 ++ .../src/pathMappings/index.ts | 4 + 7 files changed, 179 insertions(+), 116 deletions(-) diff --git a/apps/api-extractor/src/analyzer/AstSymbolTable.ts b/apps/api-extractor/src/analyzer/AstSymbolTable.ts index 41361d81217..cee070753dc 100644 --- a/apps/api-extractor/src/analyzer/AstSymbolTable.ts +++ b/apps/api-extractor/src/analyzer/AstSymbolTable.ts @@ -118,7 +118,7 @@ export class AstSymbolTable { * Used to analyze an entry point that belongs to the working package. */ public fetchAstModuleFromWorkingPackage(sourceFile: ts.SourceFile): AstModule { - return this._exportAnalyzer.fetchAstModuleFromSourceFile(sourceFile, undefined); + return this._exportAnalyzer.fetchAstModuleFromSourceFile(sourceFile, undefined, false); } /** diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index f129aa0566e..20e32517e18 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -98,10 +98,12 @@ export class ExportAnalyzer { * * @param moduleReference - contextual information about the import statement that took us to this source file. * or `undefined` if this source file is the initial entry point + * @param isExternal - whether the given `moduleReference` is external. */ public fetchAstModuleFromSourceFile( sourceFile: ts.SourceFile, - moduleReference: IAstModuleReference | undefined + moduleReference: IAstModuleReference | undefined, + isExternal: boolean ): AstModule { const moduleSymbol: ts.Symbol = this._getModuleSymbolFromSourceFile(sourceFile, moduleReference); @@ -111,14 +113,8 @@ export class ExportAnalyzer { let astModule: AstModule | undefined = this._astModulesByModuleSymbol.get(moduleSymbol); if (!astModule) { // (If moduleReference === undefined, then this is the entry point of the local project being analyzed.) - let externalModulePath: string | undefined = undefined; - if (moduleReference !== undefined) { - // Match: "@microsoft/sp-lodash-subset" or "lodash/has" - // but ignore: "../folder/LocalFile" - if (this._isExternalModulePath(moduleReference.moduleSpecifier)) { - externalModulePath = moduleReference.moduleSpecifier; - } - } + const externalModulePath: string | undefined = + moduleReference !== undefined && isExternal ? moduleReference.moduleSpecifier : undefined; astModule = new AstModule({ sourceFile, moduleSymbol, externalModulePath }); @@ -270,69 +266,23 @@ export class ExportAnalyzer { /** * Returns true if the module specifier refers to an external package. Ignores packages listed in the * "bundledPackages" setting from the api-extractor.json config file. - * - * @remarks - * Examples: - * - * - NO: `./file1` - * - YES: `library1/path/path` - * - YES: `@my-scope/my-package` - * - NO: `@my-scope/my-package` (if present in tsconfig `paths` mapping). */ - private _isExternalModulePath(moduleSpecifier: string): boolean { - if (ts.isExternalModuleNameRelative(moduleSpecifier)) { - return false; - } - - // Any module specifiers that match a path mapping entry are considered part of the current package. - if (this._hasPathMappingMatch(moduleSpecifier)) { - return false; - } - - const match: RegExpExecArray | null = ExportAnalyzer._modulePathRegExp.exec(moduleSpecifier); - if (match) { - // Extract "@my-scope/my-package" from "@my-scope/my-package/path/module" - const packageName: string = match[1]; - if (this._bundledPackageNames.has(packageName)) { - return false; - } - } - - return true; - } + private _isExternalModulePath( + importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration | ts.ImportTypeNode, + moduleSpecifier: string + ): boolean { + const resolvedModule: ts.ResolvedModuleFull = this._getResolvedModule( + importOrExportDeclaration, + moduleSpecifier + ); - /** - * Returns true if the module specifier matches a path mapping entry in the tsconfig `paths` map. - * - * @remarks - * Handles two types of path mappings: - * - * - Simple path-like strings: `some/path/to/import` - * - Paths with wildcards at the end: `some/path/to/*` - */ - private _hasPathMappingMatch(moduleSpecifier: string): boolean { - // If there are no path mappings, there cannot be a match. - const pathKeys: string[] = Object.keys(this._program.getCompilerOptions().paths || {}); - if (pathKeys.length === 0) { + // Either something like `jquery` or `@microsoft/api-extractor`. + const packageName: string | undefined = resolvedModule.packageId?.name; + if (packageName !== undefined && this._bundledPackageNames.has(packageName)) { return false; } - // Otherwise, if we haven't already, initialize some data structures to optimize how we evaluate path - // mapping matches. This will only be run once for all calls of `_hasPathMappingMatch`. - if (this._exactPathMappings.size === 0 && this._prefixPathMappings.length === 0) { - for (const pathKey of pathKeys) { - if (pathKey.endsWith('*')) { - this._prefixPathMappings.push(pathKey.slice(0, -1)); - } else { - this._exactPathMappings.add(pathKey); - } - } - } - - return ( - this._exactPathMappings.has(moduleSpecifier) || - this._prefixPathMappings.some((prefix) => moduleSpecifier.startsWith(prefix)) - ); + return !!resolvedModule.isExternalLibraryImport; } /** @@ -612,10 +562,7 @@ export class ExportAnalyzer { // Ignore "export { A }" without a module specifier if (exportDeclaration.moduleSpecifier) { - const externalModulePath: string | undefined = this._tryGetExternalModulePath( - exportDeclaration, - declarationSymbol - ); + const externalModulePath: string | undefined = this._tryGetExternalModulePath(exportDeclaration); if (externalModulePath !== undefined) { return this._fetchAstImport(declarationSymbol, { @@ -641,10 +588,7 @@ export class ExportAnalyzer { TypeScriptHelpers.findFirstParent(declaration, ts.SyntaxKind.ImportDeclaration); if (importDeclaration) { - const externalModulePath: string | undefined = this._tryGetExternalModulePath( - importDeclaration, - declarationSymbol - ); + const externalModulePath: string | undefined = this._tryGetExternalModulePath(importDeclaration); if (declaration.kind === ts.SyntaxKind.NamespaceImport) { // EXAMPLE: @@ -896,22 +840,10 @@ export class ExportAnalyzer { } private _tryGetExternalModulePath( - importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration | ts.ImportTypeNode, - exportSymbol?: ts.Symbol + importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration | ts.ImportTypeNode ): string | undefined { - // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point' - const moduleSpecifier: string | undefined = - TypeScriptHelpers.getModuleSpecifier(importOrExportDeclaration); - if (!moduleSpecifier) { - throw new InternalError( - 'Unable to parse module specifier\n' + - SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration) - ); - } - - // Match: "@microsoft/sp-lodash-subset" or "lodash/has" - // but ignore: "../folder/LocalFile" - if (this._isExternalModulePath(moduleSpecifier)) { + const moduleSpecifier: string = this._getModuleSpecifier(importOrExportDeclaration); + if (this._isExternalModulePath(importOrExportDeclaration, moduleSpecifier)) { return moduleSpecifier; } @@ -926,32 +858,12 @@ export class ExportAnalyzer { importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration, exportSymbol: ts.Symbol ): AstModule { - // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point' - const moduleSpecifier: string | undefined = - TypeScriptHelpers.getModuleSpecifier(importOrExportDeclaration); - if (!moduleSpecifier) { - throw new InternalError( - 'Unable to parse module specifier\n' + - SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration) - ); - } - - const resolvedModule: ts.ResolvedModuleFull | undefined = TypeScriptInternals.getResolvedModule( - importOrExportDeclaration.getSourceFile(), + const moduleSpecifier: string = this._getModuleSpecifier(importOrExportDeclaration); + const resolvedModule: ts.ResolvedModuleFull = this._getResolvedModule( + importOrExportDeclaration, moduleSpecifier ); - if (resolvedModule === undefined) { - // This should not happen, since getResolvedModule() specifically looks up names that the compiler - // found in export declarations for this source file - // - // Encountered in https://github.com/microsoft/rushstack/issues/1914 - throw new InternalError( - `getResolvedModule() could not resolve module name ${JSON.stringify(moduleSpecifier)}\n` + - SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration) - ); - } - // Map the filename back to the corresponding SourceFile. This circuitous approach is needed because // we have no way to access the compiler's internal resolveExternalModuleName() function const moduleSourceFile: ts.SourceFile | undefined = this._program.getSourceFile( @@ -966,13 +878,15 @@ export class ExportAnalyzer { ); } + const isExternal: boolean = this._isExternalModulePath(importOrExportDeclaration, moduleSpecifier); const moduleReference: IAstModuleReference = { moduleSpecifier: moduleSpecifier, moduleSpecifierSymbol: exportSymbol }; const specifierAstModule: AstModule = this.fetchAstModuleFromSourceFile( moduleSourceFile, - moduleReference + moduleReference, + isExternal ); return specifierAstModule; @@ -1007,4 +921,44 @@ export class ExportAnalyzer { return astImport; } + + private _getResolvedModule( + importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration | ts.ImportTypeNode, + moduleSpecifier: string + ): ts.ResolvedModuleFull { + const resolvedModule: ts.ResolvedModuleFull | undefined = TypeScriptInternals.getResolvedModule( + importOrExportDeclaration.getSourceFile(), + moduleSpecifier + ); + + if (resolvedModule === undefined) { + // This should not happen, since getResolvedModule() specifically looks up names that the compiler + // found in export declarations for this source file + // + // Encountered in https://github.com/microsoft/rushstack/issues/1914 + throw new InternalError( + `getResolvedModule() could not resolve module name ${JSON.stringify(moduleSpecifier)}\n` + + SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration) + ); + } + + return resolvedModule; + } + + private _getModuleSpecifier( + importOrExportDeclaration: ts.ImportDeclaration | ts.ExportDeclaration | ts.ImportTypeNode + ): string { + // The name of the module, which could be like "./SomeLocalFile' or like 'external-package/entry/point' + const moduleSpecifier: string | undefined = + TypeScriptHelpers.getModuleSpecifier(importOrExportDeclaration); + + if (!moduleSpecifier) { + throw new InternalError( + 'Unable to parse module specifier\n' + + SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration) + ); + } + + return moduleSpecifier; + } } diff --git a/build-tests/api-extractor-lib3-test/etc/api-extractor-lib3-test.api.md b/build-tests/api-extractor-lib3-test/etc/api-extractor-lib3-test.api.md index b08047ac72e..08b8ba504ea 100644 --- a/build-tests/api-extractor-lib3-test/etc/api-extractor-lib3-test.api.md +++ b/build-tests/api-extractor-lib3-test/etc/api-extractor-lib3-test.api.md @@ -8,5 +8,4 @@ import { Lib1Class } from 'api-extractor-lib1-test'; export { Lib1Class } - ``` diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json index 52a5a9ada72..fddf491490f 100644 --- a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.json @@ -214,6 +214,85 @@ ], "implementsTokenRanges": [] }, + { + "kind": "Enum", + "canonicalReference": "api-extractor-scenarios!RegularEnum:enum", + "docComment": "/**\n * @public\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "export enum RegularEnum " + } + ], + "releaseTag": "Public", + "name": "RegularEnum", + "members": [ + { + "kind": "EnumMember", + "canonicalReference": "api-extractor-scenarios!RegularEnum.One:member", + "docComment": "/**\n * These are some docs for One\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "One = " + }, + { + "kind": "Content", + "text": "1" + } + ], + "releaseTag": "Public", + "name": "One", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 2 + } + }, + { + "kind": "EnumMember", + "canonicalReference": "api-extractor-scenarios!RegularEnum.Two:member", + "docComment": "/**\n * These are some docs for Two\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "Two = " + }, + { + "kind": "Reference", + "text": "RegularEnum.One", + "canonicalReference": "api-extractor-scenarios!RegularEnum.One:member" + }, + { + "kind": "Content", + "text": " + 1" + } + ], + "releaseTag": "Public", + "name": "Two", + "initializerTokenRange": { + "startIndex": 1, + "endIndex": 3 + } + }, + { + "kind": "EnumMember", + "canonicalReference": "api-extractor-scenarios!RegularEnum.Zero:member", + "docComment": "/**\n * These are some docs for Zero\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "Zero" + } + ], + "releaseTag": "Public", + "name": "Zero", + "initializerTokenRange": { + "startIndex": 0, + "endIndex": 0 + } + } + ] + }, { "kind": "Class", "canonicalReference": "api-extractor-scenarios!SimpleClass:class", diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md index 76b024aaf9b..a932d81a1a0 100644 --- a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/api-extractor-scenarios.api.md @@ -10,6 +10,15 @@ export abstract class AbstractClass { public abstract member(): void; } +// @public (undocumented) +export enum RegularEnum { + One = 1, + + Two = RegularEnum.One + 1, + + Zero +} + // @public (undocumented) export class SimpleClass { // (undocumented) diff --git a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts index 6b95ca7356a..d7f81f3baab 100644 --- a/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts +++ b/build-tests/api-extractor-scenarios/etc/test-outputs/pathMappings/rollup.d.ts @@ -3,6 +3,24 @@ export declare abstract class AbstractClass { public abstract member(): void; } +/** @public */ +export declare enum RegularEnum { + /** + * These are some docs for Zero + */ + Zero, + + /** + * These are some docs for One + */ + One = 1, + + /** + * These are some docs for Two + */ + Two = RegularEnum.One + 1 +} + /** @public */ export declare class SimpleClass { public member(): void {} diff --git a/build-tests/api-extractor-scenarios/src/pathMappings/index.ts b/build-tests/api-extractor-scenarios/src/pathMappings/index.ts index fadbfa6c3e0..90683d28bd7 100644 --- a/build-tests/api-extractor-scenarios/src/pathMappings/index.ts +++ b/build-tests/api-extractor-scenarios/src/pathMappings/index.ts @@ -1,5 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +// Relies upon tsconfig `baseUrl` & `paths`. export { AbstractClass } from 'path-mapping'; export { SimpleClass } from 'wildcard-path-mapping/apiItemKinds'; + +// Relies upon only tsconfig `baseUrl`. +export { RegularEnum } from 'src/apiItemKinds'; From ce9ca4b46440873e3035e53316587d164c69f025 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Fri, 8 Apr 2022 10:20:08 -0700 Subject: [PATCH 10/14] Remove some data structures from previous approach --- apps/api-extractor/src/analyzer/ExportAnalyzer.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index 20e32517e18..5eaaa852e23 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -77,10 +77,6 @@ export class ExportAnalyzer { private readonly _astImportsByKey: Map = new Map(); private readonly _astNamespaceImportByModule: Map = new Map(); - // Used and populated by `_hasPathMappingMatch`. - private readonly _exactPathMappings: Set = new Set(); - private readonly _prefixPathMappings: string[] = []; - public constructor( program: ts.Program, typeChecker: ts.TypeChecker, From 34ecc63a8db274fb82d9aca79940e08c18eaa32f Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Fri, 8 Apr 2022 12:26:27 -0700 Subject: [PATCH 11/14] Respond to PR feedback --- .../src/analyzer/ExportAnalyzer.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index 5eaaa852e23..cbe9b911ce9 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -56,14 +56,6 @@ interface IAstModuleReference { * generating .d.ts rollups. */ export class ExportAnalyzer { - // Captures "@a/b" or "d" from these examples: - // @a/b - // @a/b/c - // d - // d/ - // d/e - private static _modulePathRegExp: RegExp = /^((?:@[^@\/\s]+\/)?[^@\/\s]+)(?:.*)$/; - private readonly _program: ts.Program; private readonly _typeChecker: ts.TypeChecker; private readonly _bundledPackageNames: ReadonlySet; @@ -278,7 +270,16 @@ export class ExportAnalyzer { return false; } - return !!resolvedModule.isExternalLibraryImport; + if (resolvedModule.isExternalLibraryImport === undefined) { + // This presumably means the compiler couldn't figure out whether the module was external, but we're not + // sure how this can happen. + throw new InternalError( + `Cannot determine whether the module is external ${JSON.stringify(moduleSpecifier)}\n` + + SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration) + ); + } + + return resolvedModule.isExternalLibraryImport; } /** From dc0eedd67b19e72bc374795571bc320235f48ca6 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Fri, 8 Apr 2022 12:29:17 -0700 Subject: [PATCH 12/14] Fix error message --- apps/api-extractor/src/analyzer/ExportAnalyzer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts index cbe9b911ce9..09d9c70acc9 100644 --- a/apps/api-extractor/src/analyzer/ExportAnalyzer.ts +++ b/apps/api-extractor/src/analyzer/ExportAnalyzer.ts @@ -274,7 +274,7 @@ export class ExportAnalyzer { // This presumably means the compiler couldn't figure out whether the module was external, but we're not // sure how this can happen. throw new InternalError( - `Cannot determine whether the module is external ${JSON.stringify(moduleSpecifier)}\n` + + `Cannot determine whether the module ${JSON.stringify(moduleSpecifier)} is external\n` + SourceFileLocationFormatter.formatDeclaration(importOrExportDeclaration) ); } From 89e1377505e1bbaa6c861b7121199a3f47b39537 Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Fri, 8 Apr 2022 12:31:00 -0700 Subject: [PATCH 13/14] Change from patch to minor --- .../@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json b/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json index 7a82c233e1b..a66af96dcb0 100644 --- a/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json +++ b/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json @@ -3,7 +3,7 @@ { "packageName": "@microsoft/api-extractor", "comment": "Bug fix to API Extractor to properly process projects with path mappings in their tsconfig.", - "type": "patch" + "type": "minor" } ], "packageName": "@microsoft/api-extractor" From e2760785ced516c449c1b465f5b814bc6476e25b Mon Sep 17 00:00:00 2001 From: Zack Elliott <4220717+zelliott@users.noreply.github.com> Date: Fri, 8 Apr 2022 12:31:42 -0700 Subject: [PATCH 14/14] Update common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json Co-authored-by: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> --- .../@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json b/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json index a66af96dcb0..eaf9904a6af 100644 --- a/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json +++ b/common/changes/@microsoft/api-extractor/path-mapping_2022-04-06-02-38.json @@ -2,7 +2,7 @@ "changes": [ { "packageName": "@microsoft/api-extractor", - "comment": "Bug fix to API Extractor to properly process projects with path mappings in their tsconfig.", + "comment": "Add support for projects that use tsconfig.json \"baseUrl\" and \"paths\" settings to remap imports of local files (GitHub #3291)", "type": "minor" } ],