diff --git a/changelog/@unreleased/deprecated-jsdoc.v2.yml b/changelog/@unreleased/deprecated-jsdoc.v2.yml new file mode 100644 index 00000000..5599c55d --- /dev/null +++ b/changelog/@unreleased/deprecated-jsdoc.v2.yml @@ -0,0 +1,5 @@ +type: improvement +improvement: + description: jsdoc has @deprecated added reason provided in definition + links: + - https://github.com/palantir/conjure-typescript/pull/149 diff --git a/package.json b/package.json index dd74b972..adb4e9b9 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ }, "dependencies": { "commander": "^2.19.0", - "conjure-api": "^4.3.0", + "conjure-api": "^4.13.0", "conjure-client": "^2.4.1", "fs-extra": "^5.0.0", "lodash": "^4.17.11", diff --git a/src/commands/generate/__tests__/generatorTest.ts b/src/commands/generate/__tests__/generatorTest.ts index 05f18577..f53be880 100644 --- a/src/commands/generate/__tests__/generatorTest.ts +++ b/src/commands/generate/__tests__/generatorTest.ts @@ -55,6 +55,7 @@ describe("generator", () => { services: [], types: [enumDefinition1, enumDefinition2], version: 1, + extensions: {}, }, outDir, ); @@ -79,6 +80,7 @@ describe("generator", () => { services: [], types: [enumDefinition1, enumDefinition2], version: 1, + extensions: {}, }, outDir, ); diff --git a/src/commands/generate/__tests__/resources/definitions/example-types.yml b/src/commands/generate/__tests__/resources/definitions/example-types.yml index 4fd170d7..536335da 100644 --- a/src/commands/generate/__tests__/resources/definitions/example-types.yml +++ b/src/commands/generate/__tests__/resources/definitions/example-types.yml @@ -210,3 +210,47 @@ types: alias: ExternalLong ExternalLongAliasTwo: alias: ExternalLongAliasOne + DeprecatedEnumExample: + values: + - ONE + - value: OLD_ONE + deprecated: use ONE + - value: OLD_DEPRECATED_ONE + docs: | + You should no longer use this + deprecated: use ONE + - value: OLD_DOCUMENTED_ONE + docs: | + You should no longer use this + + @deprecated should use ONE + deprecated: use ONE + DeprecatedFieldExample: + fields: + one: + type: string + deprecatedOne: + type: string + deprecated: use ONE + documentedDeprecatedOne: + type: string + docs: | + You should no longer use this + deprecated: use ONE + deprecatedWithinDocumentOne: + type: string + docs: | + You should no longer use this + + @deprecated should use ONE + deprecated: use ONE + DeprecatedUnion: + union: + good: string + noGood: + type: string + deprecated: use good + noGoodDoc: + type: string + deprecated: use good + docs: this is no good diff --git a/src/commands/generate/__tests__/resources/test-cases/example-service/another/testService.ts b/src/commands/generate/__tests__/resources/test-cases/example-service/another/testService.ts index 28d43fa2..41e6523f 100644 --- a/src/commands/generate/__tests__/resources/test-cases/example-service/another/testService.ts +++ b/src/commands/generate/__tests__/resources/test-cases/example-service/another/testService.ts @@ -30,6 +30,7 @@ export interface ITestService { /** * Gets all branches of this dataset. * + * @deprecated use getBranches instead */ getBranchesDeprecated(datasetRid: string): Promise>; resolveBranch(datasetRid: string, branch: string): Promise; diff --git a/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedEnumExample.ts b/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedEnumExample.ts new file mode 100644 index 00000000..76d28915 --- /dev/null +++ b/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedEnumExample.ts @@ -0,0 +1,20 @@ +export enum DeprecatedEnumExample { + ONE = "ONE", + /** + * @deprecated use ONE + */ + OLD_ONE = "OLD_ONE", + /** + * You should no longer use this + * + * @deprecated use ONE + */ + OLD_DEPRECATED_ONE = "OLD_DEPRECATED_ONE", + /** + * You should no longer use this + * + * @deprecated should use ONE + * + */ + OLD_DOCUMENTED_ONE = "OLD_DOCUMENTED_ONE" +} diff --git a/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedFieldExample.ts b/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedFieldExample.ts new file mode 100644 index 00000000..78c7c774 --- /dev/null +++ b/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedFieldExample.ts @@ -0,0 +1,20 @@ +export interface IDeprecatedFieldExample { + 'one': string; + /** + * @deprecated use ONE + */ + 'deprecatedOne': string; + /** + * You should no longer use this + * + * @deprecated use ONE + */ + 'documentedDeprecatedOne': string; + /** + * You should no longer use this + * + * @deprecated should use ONE + * + */ + 'deprecatedWithinDocumentOne': string; +} diff --git a/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedUnion.ts b/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedUnion.ts new file mode 100644 index 00000000..77151310 --- /dev/null +++ b/src/commands/generate/__tests__/resources/test-cases/example-types/product/deprecatedUnion.ts @@ -0,0 +1,92 @@ +export interface IDeprecatedUnion_Good { + 'good': string; + 'type': "good"; +} + +/** + * @deprecated use good + */ +export interface IDeprecatedUnion_NoGood { + 'noGood': string; + 'type': "noGood"; +} + +/** + * this is no good + * @deprecated use good + */ +export interface IDeprecatedUnion_NoGoodDoc { + 'noGoodDoc': string; + 'type': "noGoodDoc"; +} + +function isGood(obj: IDeprecatedUnion): obj is IDeprecatedUnion_Good { + return (obj.type === "good"); +} + +function good(obj: string): IDeprecatedUnion_Good { + return { + good: obj, + type: "good", + }; +} + +function isNoGood(obj: IDeprecatedUnion): obj is IDeprecatedUnion_NoGood { + return (obj.type === "noGood"); +} + +/** + * @deprecated use good + */ +function noGood(obj: string): IDeprecatedUnion_NoGood { + return { + noGood: obj, + type: "noGood", + }; +} + +function isNoGoodDoc(obj: IDeprecatedUnion): obj is IDeprecatedUnion_NoGoodDoc { + return (obj.type === "noGoodDoc"); +} + +/** + * @deprecated use good + */ +function noGoodDoc(obj: string): IDeprecatedUnion_NoGoodDoc { + return { + noGoodDoc: obj, + type: "noGoodDoc", + }; +} + +export type IDeprecatedUnion = IDeprecatedUnion_Good | IDeprecatedUnion_NoGood | IDeprecatedUnion_NoGoodDoc; + +export interface IDeprecatedUnionVisitor { + 'good': (obj: string) => T; + 'noGood': (obj: string) => T; + 'noGoodDoc': (obj: string) => T; + 'unknown': (obj: IDeprecatedUnion) => T; +} + +function visit(obj: IDeprecatedUnion, visitor: IDeprecatedUnionVisitor): T { + if (isGood(obj)) { + return visitor.good(obj.good); + } + if (isNoGood(obj)) { + return visitor.noGood(obj.noGood); + } + if (isNoGoodDoc(obj)) { + return visitor.noGoodDoc(obj.noGoodDoc); + } + return visitor.unknown(obj); +} + +export const IDeprecatedUnion = { + isGood: isGood, + good: good, + isNoGood: isNoGood, + noGood: noGood, + isNoGoodDoc: isNoGoodDoc, + noGoodDoc: noGoodDoc, + visit: visit +}; diff --git a/src/commands/generate/serviceGenerator.ts b/src/commands/generate/serviceGenerator.ts index e58c97ca..3c229e62 100644 --- a/src/commands/generate/serviceGenerator.ts +++ b/src/commands/generate/serviceGenerator.ts @@ -41,7 +41,7 @@ import { SimpleAst } from "./simpleAst"; import { StringConversionTypeVisitor } from "./stringConversionTypeVisitor"; import { TsArgumentTypeVisitor } from "./tsArgumentTypeVisitor"; import { TsReturnTypeVisitor } from "./tsReturnTypeVisitor"; -import { CONJURE_CLIENT } from "./utils"; +import { addDeprecatedToDocs, CONJURE_CLIENT } from "./utils"; /** Type used in the generation of the service class. Expected to be provided by conjure-client */ const HTTP_API_BRIDGE_TYPE = "IHttpApiBridge"; @@ -102,8 +102,9 @@ export function generateService( parameters, returnType: `Promise<${returnTsType}>`, }; - if (endpointDefinition.docs != null) { - signature.docs = [{ description: endpointDefinition.docs }]; + const docs = addDeprecatedToDocs(endpointDefinition); + if (docs != null) { + signature.docs = docs; } endpointSignatures.push(signature); diff --git a/src/commands/generate/typeGenerator.ts b/src/commands/generate/typeGenerator.ts index abbaeb40..af5339dc 100644 --- a/src/commands/generate/typeGenerator.ts +++ b/src/commands/generate/typeGenerator.ts @@ -14,7 +14,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { IEnumDefinition, IObjectDefinition, IType, ITypeDefinition, IUnionDefinition } from "conjure-api"; import { FunctionDeclarationStructure, @@ -27,7 +26,7 @@ import { import { ImportsVisitor, sortImports } from "./imports"; import { SimpleAst } from "./simpleAst"; import { TsReturnTypeVisitor } from "./tsReturnTypeVisitor"; -import { doubleQuote, isValidFunctionName, singleQuote } from "./utils"; +import { addDeprecatedToDocs, doubleQuote, isValidFunctionName, singleQuote } from "./utils"; export function generateType( definition: ITypeDefinition, @@ -61,10 +60,10 @@ export async function generateEnum(definition: IEnumDefinition, simpleAst: Simpl sourceFile.addEnum({ docs: definition.docs != null ? [{ description: definition.docs }] : undefined, isExported: true, - members: definition.values.map(({ docs, value }) => ({ - docs: docs != null ? [{ description: docs }] : undefined, - name: value, - value, + members: definition.values.map(enumValue => ({ + docs: addDeprecatedToDocs(enumValue), + name: enumValue.value, + value: enumValue.value, })), name: definition.typeName.name, }); @@ -99,10 +98,9 @@ export async function generateObject( hasQuestionToken: IType.isOptional(fieldDefinition.type), name: singleQuote(fieldDefinition.fieldName), type: fieldType, + docs: addDeprecatedToDocs(fieldDefinition), }; - if (fieldDefinition.docs !== undefined && fieldDefinition.docs !== null) { - property.docs = [{ description: fieldDefinition.docs }]; - } + properties.push(property); imports.push(...IType.visit(fieldDefinition.type, importsVisitor)); @@ -218,8 +216,9 @@ function processUnionMembers( imports.push(...IType.visit(fieldDefinition.type, importsVisitor)); const interfaceName = `${unionTsType}_${uppercase(memberName)}`; + memberInterfaces.push({ - docs: fieldDefinition.docs != null ? [{ description: fieldDefinition.docs }] : undefined, + docs: addDeprecatedToDocs(fieldDefinition), isExported: true, name: interfaceName, properties: [ @@ -263,6 +262,11 @@ function processUnionMembers( }, ], returnType: interfaceName, + // deprecate creation of deprecated types + docs: + fieldDefinition.deprecated !== null && fieldDefinition.deprecated !== undefined + ? [`@deprecated ${fieldDefinition.deprecated}`] + : undefined, }); visitorProperties.push({ diff --git a/src/commands/generate/utils.ts b/src/commands/generate/utils.ts index 37d85a3e..948b57d8 100644 --- a/src/commands/generate/utils.ts +++ b/src/commands/generate/utils.ts @@ -14,8 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { ITypeName } from "conjure-api"; +import { IEndpointDefinition, IEnumValueDefinition, IFieldDefinition, ITypeName } from "conjure-api"; export const CONJURE_CLIENT = "conjure-client"; @@ -91,3 +90,18 @@ const strictModeReservedKeywords = new Set([ "static", "yield", ]); + +type DeprecatableDefinitions = IFieldDefinition | IEnumValueDefinition | IEndpointDefinition; +export function addDeprecatedToDocs(typeDefintion: T): string[] | undefined { + if (typeDefintion.deprecated !== undefined && typeDefintion.deprecated !== null) { + if (typeDefintion.docs !== undefined && typeDefintion.docs !== null) { + // Do not add deprecated JSDoc if already exists + if (typeDefintion.docs.indexOf("@deprecated") === -1) { + return [typeDefintion.docs + `\n@deprecated ${typeDefintion.deprecated}`]; + } + } else { + return [`@deprecated ${typeDefintion.deprecated}`]; + } + } + return typeDefintion.docs != null ? [typeDefintion.docs] : undefined; +} diff --git a/versions.props b/versions.props index 674438ef..9250ce37 100644 --- a/versions.props +++ b/versions.props @@ -1,3 +1,3 @@ # User versions.props so that excavator can automatically keep our version up to date com.palantir.conjure.verification:* = 0.18.5 -com.palantir.conjure:conjure = 4.6.1 +com.palantir.conjure:conjure = 4.13.0 diff --git a/yarn.lock b/yarn.lock index 2da65e65..57759b92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1516,10 +1516,10 @@ concat-stream@^1.5.0: readable-stream "^2.2.2" typedarray "^0.0.6" -conjure-api@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/conjure-api/-/conjure-api-4.3.0.tgz#c778736f369e7c749a86118e14dab83deb698ace" - integrity sha512-AnZe6qkiCRSsfuQMgA556dFs+/DbXpCK2uuhq9bHn36dSbKi+6HUDntpc9jkVPzWOecLXGqy+JAD6IKV8c9Sqw== +conjure-api@^4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/conjure-api/-/conjure-api-4.13.0.tgz#3afb10e2ba3fefb582ce7cb20b259d096aab6895" + integrity sha512-ATtXX42n9WwEOrMdQpMYJreauyiBDf+5XmXATjE5Ky2nozlgu2DNXXajrXKzPhBM1XEjeTH4JS5zwlE4/JhnmA== conjure-client@^2.4.1: version "2.4.1"