From eb245109c05e98e10fdd75c48040d33c432a6a98 Mon Sep 17 00:00:00 2001 From: Timothee Guerin Date: Fri, 26 Jul 2024 10:13:45 -0700 Subject: [PATCH] Add deprecation notice in ref docs (#4025) fix [#2326](https://github.com/microsoft/typespec/issues/2326) --- docs/standard-library/built-in-data-types.md | 3 ++ docs/standard-library/built-in-decorators.md | 9 ++++ .../tspd/src/ref-doc/emitters/docusaurus.ts | 5 +++ .../tspd/src/ref-doc/emitters/markdown.ts | 27 ++++++++---- packages/tspd/src/ref-doc/extractor.ts | 42 +++++++++++-------- packages/tspd/src/ref-doc/types.ts | 5 +++ .../ref-doc/emitters/markdown/model.test.ts | 32 ++++++++++++++ 7 files changed, 98 insertions(+), 25 deletions(-) diff --git a/docs/standard-library/built-in-data-types.md b/docs/standard-library/built-in-data-types.md index 20d2412c46..f330b84b24 100644 --- a/docs/standard-library/built-in-data-types.md +++ b/docs/standard-library/built-in-data-types.md @@ -54,6 +54,9 @@ model ExampleOptions | description? | [`string`](#string) | Description of the example | ### `object` {#object} +:::warning +**Deprecated**: object is deprecated. Please use {} for an empty model, `Record` for a record with unknown property types, `unknown[]` for an array. +::: Represent a model ```typespec diff --git a/docs/standard-library/built-in-decorators.md b/docs/standard-library/built-in-decorators.md index d7413c6039..7317eae744 100644 --- a/docs/standard-library/built-in-decorators.md +++ b/docs/standard-library/built-in-decorators.md @@ -6,6 +6,9 @@ toc_max_heading_level: 3 # Built-in Decorators ## TypeSpec ### `@deprecated` {#@deprecated} +:::warning +**Deprecated**: @deprecated decorator is deprecated. Use the `#deprecated` directive instead. +::: Mark this type as deprecated. @@ -358,6 +361,9 @@ model Pet { ### `@knownValues` {#@knownValues} +:::warning +**Deprecated**: This decorator has been deprecated. Use a named union of string literals with a string variant to achieve the same result without a decorator. +::: Provide a set of known values to a string type. ```typespec @@ -701,6 +707,9 @@ scalar LowerAlpha extends string; ### `@projectedName` {#@projectedName} +:::warning +**Deprecated**: Use `@encodedName` instead for changing the name over the wire. +::: DEPRECATED: Use `@encodedName` instead. diff --git a/packages/tspd/src/ref-doc/emitters/docusaurus.ts b/packages/tspd/src/ref-doc/emitters/docusaurus.ts index 585ffb0823..a9011004f5 100644 --- a/packages/tspd/src/ref-doc/emitters/docusaurus.ts +++ b/packages/tspd/src/ref-doc/emitters/docusaurus.ts @@ -1,4 +1,5 @@ import { + DeprecationNotice, NamedTypeRefDoc, RefDocEntity, TypeSpecLibraryRefDoc, @@ -302,4 +303,8 @@ export class DocusaurusRenderer extends MarkdownRenderer { return url; } } + + deprecationNotice(notice: DeprecationNotice): MarkdownDoc { + return [":::warning", `**Deprecated**: ${notice.message}`, ":::"]; + } } diff --git a/packages/tspd/src/ref-doc/emitters/markdown.ts b/packages/tspd/src/ref-doc/emitters/markdown.ts index 8a4c0c7dff..518190dee3 100644 --- a/packages/tspd/src/ref-doc/emitters/markdown.ts +++ b/packages/tspd/src/ref-doc/emitters/markdown.ts @@ -9,6 +9,7 @@ import { readFile } from "fs/promises"; import { stringify } from "yaml"; import { DecoratorRefDoc, + DeprecationNotice, EmitterOptionRefDoc, EnumRefDoc, ExampleRefDoc, @@ -105,6 +106,15 @@ export class MarkdownRenderer { return `${item.name.toLowerCase().replace(/ /g, "-")}`; } + deprecationNotice(notice: DeprecationNotice): MarkdownDoc { + return `_Deprecated: ${notice.message}_`; + } + + typeSection(type: NamedTypeRefDoc, content: MarkdownDoc) { + const deprecated = type.deprecated ? this.deprecationNotice(type.deprecated) : []; + return section(this.headingTitle(type), [deprecated, content]); + } + //#region TypeSpec types operation(op: OperationRefDoc) { const content: MarkdownDoc = ["", op.doc, codeblock(op.signature, "typespec"), ""]; @@ -115,7 +125,7 @@ export class MarkdownRenderer { content.push(this.examples(op.examples)); - return section(this.headingTitle(op), content); + return this.typeSection(op, content); } interface(iface: InterfaceRefDoc) { @@ -133,7 +143,7 @@ export class MarkdownRenderer { content.push(this.examples(iface.examples)); - return section(this.headingTitle(iface), content); + return this.typeSection(iface, content); } model(model: ModelRefDoc) { @@ -145,7 +155,7 @@ export class MarkdownRenderer { content.push(this.examples(model.examples)); content.push(this.modelProperties(model)); - return section(this.headingTitle(model), content); + return this.typeSection(model, content); } modelProperties(model: ModelRefDoc) { @@ -175,8 +185,9 @@ export class MarkdownRenderer { } modelPropertyRows(prop: ModelPropertyRefDoc): { name: string; type: string; doc: string }[] { + const name = `${prop.name}${prop.type.optional ? "?" : ""}`; const base = { - name: `${prop.name}${prop.type.optional ? "?" : ""}`, + name: prop.deprecated ? `~~${name}~~ _DEPRECATED_` : name, type: this.ref(prop.type.type), doc: prop.doc, }; @@ -224,7 +235,7 @@ export class MarkdownRenderer { this.examples(e.examples), ]; - return section(this.headingTitle(e), content); + return this.typeSection(e, content); } enumMembers(e: EnumRefDoc): MarkdownDoc { @@ -251,7 +262,7 @@ export class MarkdownRenderer { content.push(this.examples(union.examples)); - return section(this.headingTitle(union), content); + return this.typeSection(union, content); } scalar(scalar: ScalarRefDoc): MarkdownDoc { @@ -263,7 +274,7 @@ export class MarkdownRenderer { content.push(this.examples(scalar.examples)); - return section(this.headingTitle(scalar), content); + return this.typeSection(scalar, content); } templateParameters(templateParameters: readonly TemplateParameterRefDoc[]): MarkdownDoc { @@ -292,7 +303,7 @@ export class MarkdownRenderer { content.push(this.examples(dec.examples)); - return section(this.headingTitle(dec), content); + return this.typeSection(dec, content); } MixedParameterConstraint(constraint: MixedParameterConstraint): string { diff --git a/packages/tspd/src/ref-doc/extractor.ts b/packages/tspd/src/ref-doc/extractor.ts index b104bc7901..9dd7067cde 100644 --- a/packages/tspd/src/ref-doc/extractor.ts +++ b/packages/tspd/src/ref-doc/extractor.ts @@ -8,6 +8,7 @@ import { DocUnknownTagNode, Enum, EnumMember, + getDeprecated, getDoc, getLocationContext, getSourceLocation, @@ -44,6 +45,7 @@ import { pathToFileURL } from "url"; import { reportDiagnostic } from "./lib.js"; import { DecoratorRefDoc, + DeprecationNotice, EmitterOptionRefDoc, EnumMemberRefDoc, EnumRefDoc, @@ -58,6 +60,7 @@ import { NamespaceRefDoc, OperationRefDoc, RefDocEntity, + ReferencableElement, ScalarRefDoc, TypeSpecLibraryRefDoc, TypeSpecRefDocBase, @@ -305,8 +308,7 @@ function extractInterfaceRefDocs(program: Program, iface: Interface): InterfaceR } return { kind: "interface", - id: getNamedTypeId(iface), - name: iface.name, + ...extractBase(program, iface), signature: getTypeSignature(iface), type: iface, templateParameters: extractTemplateParameterDocs(program, iface), @@ -318,6 +320,19 @@ function extractInterfaceRefDocs(program: Program, iface: Interface): InterfaceR }; } +function extractBase( + program: Program, + type: Type & { name: string } +): ReferencableElement & { readonly deprecated?: DeprecationNotice } { + const deprecated = getDeprecated(program, type); + + return { + id: getNamedTypeId(type), + name: type.name, + deprecated: deprecated ? { message: deprecated } : undefined, + }; +} + function extractOperationRefDoc( program: Program, operation: Operation, @@ -343,7 +358,7 @@ function extractOperationRefDoc( } return { kind: "operation", - id: getNamedTypeId(operation), + ...extractBase(program, operation), name: interfaceName ? `${interfaceName}.${operation.name}` : operation.name, signature: getTypeSignature(operation), type: operation, @@ -387,8 +402,7 @@ function extractDecoratorRefDoc(program: Program, decorator: Decorator): Decorat } return { kind: "decorator", - id: getNamedTypeId(decorator), - name: decorator.name, + ...extractBase(program, decorator), type: decorator, signature: getTypeSignature(decorator), doc: mainDoc, @@ -417,8 +431,7 @@ function extractModelRefDocs(program: Program, type: Model): ModelRefDoc { } return { kind: "model", - id: getNamedTypeId(type), - name: type.name, + ...extractBase(program, type), signature: getTypeSignature(type), type, templateParameters: extractTemplateParameterDocs(program, type), @@ -433,8 +446,7 @@ function extractModelRefDocs(program: Program, type: Model): ModelRefDoc { function extractModelPropertyRefDocs(program: Program, type: ModelProperty): ModelPropertyRefDoc { const doc = extractMainDoc(program, type); return { - id: getNamedTypeId(type), - name: type.name, + ...extractBase(program, type), signature: getTypeSignature(type), type, doc: doc, @@ -454,8 +466,7 @@ function extractEnumRefDoc(program: Program, type: Enum): EnumRefDoc { } return { kind: "enum", - id: getNamedTypeId(type), - name: type.name, + ...extractBase(program, type), signature: getTypeSignature(type), type, doc: doc, @@ -469,8 +480,7 @@ function extractEnumRefDoc(program: Program, type: Enum): EnumRefDoc { function extractEnumMemberRefDocs(program: Program, type: EnumMember): EnumMemberRefDoc { const doc = extractMainDoc(program, type); return { - id: getNamedTypeId(type), - name: type.name, + ...extractBase(program, type), signature: getTypeSignature(type), type, doc: doc, @@ -490,8 +500,7 @@ function extractUnionRefDocs(program: Program, type: Union & { name: string }): } return { kind: "union", - id: getNamedTypeId(type), - name: type.name, + ...extractBase(program, type), signature: getTypeSignature(type), type, templateParameters: extractTemplateParameterDocs(program, type), @@ -512,8 +521,7 @@ function extractScalarRefDocs(program: Program, type: Scalar): ScalarRefDoc { } return { kind: "scalar", - id: getNamedTypeId(type), - name: type.name, + ...extractBase(program, type), signature: getTypeSignature(type), type, doc: doc, diff --git a/packages/tspd/src/ref-doc/types.ts b/packages/tspd/src/ref-doc/types.ts index 755c700f4a..62ec0bf035 100644 --- a/packages/tspd/src/ref-doc/types.ts +++ b/packages/tspd/src/ref-doc/types.ts @@ -109,6 +109,11 @@ export type NamedTypeRefDoc = ReferencableElement & { readonly signature: string; readonly doc: string; readonly examples: readonly ExampleRefDoc[]; + readonly deprecated?: DeprecationNotice; +}; + +export type DeprecationNotice = { + readonly message: string; }; export type DecoratorRefDoc = NamedTypeRefDoc & { diff --git a/packages/tspd/test/ref-doc/emitters/markdown/model.test.ts b/packages/tspd/test/ref-doc/emitters/markdown/model.test.ts index fd16992ae0..2ed58f76cc 100644 --- a/packages/tspd/test/ref-doc/emitters/markdown/model.test.ts +++ b/packages/tspd/test/ref-doc/emitters/markdown/model.test.ts @@ -28,6 +28,27 @@ it("render simple model", async () => { ); }); +it("render deprecation notice", async () => { + const result = await renderModel(` + #deprecated "Use something else" + model Test {}`); + expect(result).toEqual( + [ + "# `Test`", + "_Deprecated: Use something else_", + "", + "", + "```typespec", + "model Lib.Test", + "```", + "", + "", + "## Properties", + "None", + ].join("\n") + ); +}); + it("render model with template parameter", async () => { const result = await renderModel(`model Test {}`); expect(result).toEqual( @@ -153,6 +174,17 @@ describe("properties table", () => { }); }); + it("render deprecated properties", async () => { + await expectTable({ + code: `model Test { + #deprecated "Use other" + name: string, + other: int32 + }`, + rows: ["| ~~name~~ _DEPRECATED_ | `string` | |", "| other | `int32` | |"], + }); + }); + it("render enum properties", async () => { await expectTable({ code: `