diff --git a/apps/api-extractor/src/generators/ApiModelGenerator.ts b/apps/api-extractor/src/generators/ApiModelGenerator.ts index 5bb4d510608..cb0d52e17d3 100644 --- a/apps/api-extractor/src/generators/ApiModelGenerator.ts +++ b/apps/api-extractor/src/generators/ApiModelGenerator.ts @@ -202,6 +202,10 @@ export class ApiModelGenerator { this._processApiProperty(astDeclaration, exportedName, parentApiItem); break; + case ts.SyntaxKind.SetAccessor: + this._processApiProperty(astDeclaration, exportedName, parentApiItem); + break; + case ts.SyntaxKind.IndexSignature: this._processApiIndexSignature(astDeclaration, exportedName, parentApiItem); break; @@ -817,13 +821,17 @@ export class ApiModelGenerator { let apiProperty: ApiProperty | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiProperty; if (apiProperty === undefined) { - const propertyDeclaration: ts.PropertyDeclaration = - astDeclaration.declaration as ts.PropertyDeclaration; - const nodesToCapture: IExcerptBuilderNodeToCapture[] = []; const propertyTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); - nodesToCapture.push({ node: propertyDeclaration.type, tokenRange: propertyTypeTokenRange }); + + // If the property declaration's type is `undefined`, then we're processing a setter with no corresponding + // getter. Use the parameter type instead (note that TypeScript always reports an error if a setter + // does not have exactly one parameter). + const propertyTypeNode: ts.TypeNode | undefined = + (astDeclaration.declaration as ts.PropertyDeclaration | ts.GetAccessorDeclaration).type || + (astDeclaration.declaration as ts.SetAccessorDeclaration).parameters[0].type; + nodesToCapture.push({ node: propertyTypeNode, tokenRange: propertyTypeTokenRange }); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); diff --git a/build-tests/api-documenter-test/config/api-extractor.json b/build-tests/api-documenter-test/config/api-extractor.json index 5a21503f8df..17b82fa03cc 100644 --- a/build-tests/api-documenter-test/config/api-extractor.json +++ b/build-tests/api-documenter-test/config/api-extractor.json @@ -18,5 +18,14 @@ "enabled": false }, - "testMode": true + "testMode": true, + + "messages": { + "extractorMessageReporting": { + // Purposefully disabled for the `writeonlyProperty` test case. + "ae-missing-getter": { + "logLevel": "none" + } + } + } } diff --git a/build-tests/api-documenter-test/etc/api-documenter-test.api.json b/build-tests/api-documenter-test/etc/api-documenter-test.api.json index 1474a9e2fe9..8ec69740667 100644 --- a/build-tests/api-documenter-test/etc/api-documenter-test.api.json +++ b/build-tests/api-documenter-test/etc/api-documenter-test.api.json @@ -905,6 +905,33 @@ "endIndex": 2 }, "isStatic": false + }, + { + "kind": "Property", + "canonicalReference": "api-documenter-test!DocClass1#writeonlyProperty:member", + "docComment": "/**\n * API Extractor will surface an `ae-missing-getter` finding for this property.\n */\n", + "excerptTokens": [ + { + "kind": "Content", + "text": "set writeonlyProperty(value: " + }, + { + "kind": "Content", + "text": "string" + }, + { + "kind": "Content", + "text": ");" + } + ], + "isOptional": false, + "releaseTag": "Public", + "name": "writeonlyProperty", + "propertyTypeTokenRange": { + "startIndex": 1, + "endIndex": 2 + }, + "isStatic": false } ], "extendsTokenRange": { diff --git a/build-tests/api-documenter-test/etc/api-documenter-test.api.md b/build-tests/api-documenter-test/etc/api-documenter-test.api.md index ab432523a49..828c30e2583 100644 --- a/build-tests/api-documenter-test/etc/api-documenter-test.api.md +++ b/build-tests/api-documenter-test/etc/api-documenter-test.api.md @@ -49,6 +49,7 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter // (undocumented) get writeableProperty(): string; set writeableProperty(value: string); + set writeonlyProperty(value: string); } // @public diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.md index 12107be9e13..0c0a9e42f51 100644 --- a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.md +++ b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.md @@ -38,6 +38,7 @@ The constructor for this class is marked as internal. Third-party code should no | [readonlyProperty](./api-documenter-test.docclass1.readonlyproperty.md) | | string | | | [regularProperty](./api-documenter-test.docclass1.regularproperty.md) | | [SystemEvent](./api-documenter-test.systemevent.md) | This is a regular property that happens to use the SystemEvent type. | | [writeableProperty](./api-documenter-test.docclass1.writeableproperty.md) | | string | | +| [writeonlyProperty](./api-documenter-test.docclass1.writeonlyproperty.md) | | string | API Extractor will surface an ae-missing-getter finding for this property. | ## Methods diff --git a/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeonlyproperty.md b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeonlyproperty.md new file mode 100644 index 00000000000..c67cc8b01dd --- /dev/null +++ b/build-tests/api-documenter-test/etc/markdown/api-documenter-test.docclass1.writeonlyproperty.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [api-documenter-test](./api-documenter-test.md) > [DocClass1](./api-documenter-test.docclass1.md) > [writeonlyProperty](./api-documenter-test.docclass1.writeonlyproperty.md) + +## DocClass1.writeonlyProperty property + +API Extractor will surface an `ae-missing-getter` finding for this property. + +Signature: + +```typescript +set writeonlyProperty(value: string); +``` diff --git a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclass1.yml b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclass1.yml index cbe85393815..b02c6cb4ff3 100644 --- a/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclass1.yml +++ b/build-tests/api-documenter-test/etc/yaml/api-documenter-test/docclass1.yml @@ -61,6 +61,19 @@ properties: set writeableProperty(value: string); return: type: string + - name: writeonlyProperty + uid: 'api-documenter-test!DocClass1#writeonlyProperty:member' + package: api-documenter-test! + fullName: writeonlyProperty + summary: API Extractor will surface an `ae-missing-getter` finding for this property. + remarks: '' + example: [] + isPreview: false + isDeprecated: false + syntax: + content: 'set writeonlyProperty(value: string);' + return: + type: string methods: - name: deprecatedExample() uid: 'api-documenter-test!DocClass1#deprecatedExample:member(1)' diff --git a/build-tests/api-documenter-test/src/DocClass1.ts b/build-tests/api-documenter-test/src/DocClass1.ts index 2de7efe031f..313a0f45a95 100644 --- a/build-tests/api-documenter-test/src/DocClass1.ts +++ b/build-tests/api-documenter-test/src/DocClass1.ts @@ -206,6 +206,11 @@ export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInter } public set writeableProperty(value: string) {} + /** + * API Extractor will surface an `ae-missing-getter` finding for this property. + */ + public set writeonlyProperty(value: string) {} + /** * This event is fired whenever the object is modified. * @eventProperty 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 3fafc4e2348..da643175830 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.4.tgz - '@rushstack/heft': file:rushstack-heft-0.44.13.tgz + '@rushstack/eslint-config': file:rushstack-eslint-config-2.6.0.tgz + '@rushstack/heft': file:rushstack-heft-0.45.0.tgz eslint: ~8.7.0 tslint: ~5.20.1 typescript: ~4.6.3 devDependencies: - '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.5.4.tgz_eslint@8.7.0+typescript@4.6.3 - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.13.tgz + '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.6.0.tgz_eslint@8.7.0+typescript@4.6.3 + '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.45.0.tgz eslint: 8.7.0 tslint: 5.20.1_typescript@4.6.3 typescript: 4.6.3 typescript-v3-test: specifiers: - '@rushstack/eslint-config': file:rushstack-eslint-config-2.5.4.tgz - '@rushstack/heft': file:rushstack-heft-0.44.13.tgz + '@rushstack/eslint-config': file:rushstack-eslint-config-2.6.0.tgz + '@rushstack/heft': file:rushstack-heft-0.45.0.tgz eslint: ~8.7.0 tslint: ~5.20.1 typescript: ~4.6.3 devDependencies: - '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.5.4.tgz_eslint@8.7.0+typescript@4.6.3 - '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.44.13.tgz + '@rushstack/eslint-config': file:../temp/tarballs/rushstack-eslint-config-2.6.0.tgz_eslint@8.7.0+typescript@4.6.3 + '@rushstack/heft': file:../temp/tarballs/rushstack-heft-0.45.0.tgz eslint: 8.7.0 tslint: 5.20.1_typescript@4.6.3 typescript: 4.6.3 @@ -1673,19 +1673,19 @@ packages: commander: 2.20.3 dev: true - file:../temp/tarballs/rushstack-eslint-config-2.5.4.tgz_eslint@8.7.0+typescript@4.6.3: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-config-2.5.4.tgz} - id: file:../temp/tarballs/rushstack-eslint-config-2.5.4.tgz + file:../temp/tarballs/rushstack-eslint-config-2.6.0.tgz_eslint@8.7.0+typescript@4.6.3: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-config-2.6.0.tgz} + id: file:../temp/tarballs/rushstack-eslint-config-2.6.0.tgz name: '@rushstack/eslint-config' - version: 2.5.4 + version: 2.6.0 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.3.tgz - '@rushstack/eslint-plugin': file:../temp/tarballs/rushstack-eslint-plugin-0.8.6.tgz_eslint@8.7.0+typescript@4.6.3 - '@rushstack/eslint-plugin-packlets': file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.6.tgz_eslint@8.7.0+typescript@4.6.3 - '@rushstack/eslint-plugin-security': file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.6.tgz_eslint@8.7.0+typescript@4.6.3 + '@rushstack/eslint-plugin': file:../temp/tarballs/rushstack-eslint-plugin-0.9.0.tgz_eslint@8.7.0+typescript@4.6.3 + '@rushstack/eslint-plugin-packlets': file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.4.0.tgz_eslint@8.7.0+typescript@4.6.3 + '@rushstack/eslint-plugin-security': file:../temp/tarballs/rushstack-eslint-plugin-security-0.3.0.tgz_eslint@8.7.0+typescript@4.6.3 '@typescript-eslint/eslint-plugin': 5.20.0_e20e806d7f50be84027c83f3a408385a '@typescript-eslint/experimental-utils': 5.20.0_eslint@8.7.0+typescript@4.6.3 '@typescript-eslint/parser': 5.20.0_eslint@8.7.0+typescript@4.6.3 @@ -1705,11 +1705,11 @@ packages: version: 1.1.3 dev: true - file:../temp/tarballs/rushstack-eslint-plugin-0.8.6.tgz_eslint@8.7.0+typescript@4.6.3: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-0.8.6.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-0.8.6.tgz + file:../temp/tarballs/rushstack-eslint-plugin-0.9.0.tgz_eslint@8.7.0+typescript@4.6.3: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-0.9.0.tgz} + id: file:../temp/tarballs/rushstack-eslint-plugin-0.9.0.tgz name: '@rushstack/eslint-plugin' - version: 0.8.6 + version: 0.9.0 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: @@ -1721,11 +1721,11 @@ packages: - typescript dev: true - file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.6.tgz_eslint@8.7.0+typescript@4.6.3: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.6.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.3.6.tgz + file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.4.0.tgz_eslint@8.7.0+typescript@4.6.3: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.4.0.tgz} + id: file:../temp/tarballs/rushstack-eslint-plugin-packlets-0.4.0.tgz name: '@rushstack/eslint-plugin-packlets' - version: 0.3.6 + version: 0.4.0 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: @@ -1737,11 +1737,11 @@ packages: - typescript dev: true - file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.6.tgz_eslint@8.7.0+typescript@4.6.3: - resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.6.tgz} - id: file:../temp/tarballs/rushstack-eslint-plugin-security-0.2.6.tgz + file:../temp/tarballs/rushstack-eslint-plugin-security-0.3.0.tgz_eslint@8.7.0+typescript@4.6.3: + resolution: {tarball: file:../temp/tarballs/rushstack-eslint-plugin-security-0.3.0.tgz} + id: file:../temp/tarballs/rushstack-eslint-plugin-security-0.3.0.tgz name: '@rushstack/eslint-plugin-security' - version: 0.2.6 + version: 0.3.0 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 dependencies: @@ -1753,17 +1753,17 @@ packages: - typescript dev: true - file:../temp/tarballs/rushstack-heft-0.44.13.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-0.44.13.tgz} + file:../temp/tarballs/rushstack-heft-0.45.0.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-heft-0.45.0.tgz} name: '@rushstack/heft' - version: 0.44.13 + version: 0.45.0 engines: {node: '>=10.13.0'} hasBin: true dependencies: - '@rushstack/heft-config-file': file:../temp/tarballs/rushstack-heft-config-file-0.8.2.tgz - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.45.3.tgz - '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.10.tgz - '@rushstack/ts-command-line': file:../temp/tarballs/rushstack-ts-command-line-4.10.9.tgz + '@rushstack/heft-config-file': file:../temp/tarballs/rushstack-heft-config-file-0.8.3.tgz + '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.45.4.tgz + '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.11.tgz + '@rushstack/ts-command-line': file:../temp/tarballs/rushstack-ts-command-line-4.10.10.tgz '@types/tapable': 1.0.6 argparse: 1.0.10 chokidar: 3.4.3 @@ -1776,21 +1776,21 @@ packages: true-case-path: 2.2.1 dev: true - file:../temp/tarballs/rushstack-heft-config-file-0.8.2.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-heft-config-file-0.8.2.tgz} + file:../temp/tarballs/rushstack-heft-config-file-0.8.3.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-heft-config-file-0.8.3.tgz} name: '@rushstack/heft-config-file' - version: 0.8.2 + version: 0.8.3 engines: {node: '>=10.13.0'} dependencies: - '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.45.3.tgz - '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.10.tgz + '@rushstack/node-core-library': file:../temp/tarballs/rushstack-node-core-library-3.45.4.tgz + '@rushstack/rig-package': file:../temp/tarballs/rushstack-rig-package-0.3.11.tgz jsonpath-plus: 4.0.0 dev: true - file:../temp/tarballs/rushstack-node-core-library-3.45.3.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-node-core-library-3.45.3.tgz} + file:../temp/tarballs/rushstack-node-core-library-3.45.4.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-node-core-library-3.45.4.tgz} name: '@rushstack/node-core-library' - version: 3.45.3 + version: 3.45.4 dependencies: '@types/node': 12.20.24 colors: 1.2.5 @@ -1803,10 +1803,10 @@ packages: z-schema: 5.0.2 dev: true - file:../temp/tarballs/rushstack-rig-package-0.3.10.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-rig-package-0.3.10.tgz} + file:../temp/tarballs/rushstack-rig-package-0.3.11.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-rig-package-0.3.11.tgz} name: '@rushstack/rig-package' - version: 0.3.10 + version: 0.3.11 dependencies: resolve: 1.17.0 strip-json-comments: 3.1.1 @@ -1818,10 +1818,10 @@ packages: version: 0.2.3 dev: true - file:../temp/tarballs/rushstack-ts-command-line-4.10.9.tgz: - resolution: {tarball: file:../temp/tarballs/rushstack-ts-command-line-4.10.9.tgz} + file:../temp/tarballs/rushstack-ts-command-line-4.10.10.tgz: + resolution: {tarball: file:../temp/tarballs/rushstack-ts-command-line-4.10.10.tgz} name: '@rushstack/ts-command-line' - version: 4.10.9 + version: 4.10.10 dependencies: '@types/argparse': 1.0.38 argparse: 1.0.10 diff --git a/common/changes/@microsoft/api-extractor/handle-setters_2022-05-05-17-41.json b/common/changes/@microsoft/api-extractor/handle-setters_2022-05-05-17-41.json new file mode 100644 index 00000000000..4c4bd34687d --- /dev/null +++ b/common/changes/@microsoft/api-extractor/handle-setters_2022-05-05-17-41.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@microsoft/api-extractor", + "comment": "Generate API doc model nodes for setters without getters", + "type": "minor" + } + ], + "packageName": "@microsoft/api-extractor" +} \ No newline at end of file