From 9cc7529fb668f557a351d4e679ad22c793bc7d43 Mon Sep 17 00:00:00 2001 From: Eric Kim-Butler Date: Thu, 24 Nov 2022 16:29:05 +0100 Subject: [PATCH] Add Support for `@deprecated` Markers --- src/graphql/ir.rs | 2 ++ src/typescript.rs | 38 ++++++++++++-------- tests/typescript/field.rs | 76 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 14 deletions(-) diff --git a/src/graphql/ir.rs b/src/graphql/ir.rs index b8d2b16..430b2b1 100644 --- a/src/graphql/ir.rs +++ b/src/graphql/ir.rs @@ -274,6 +274,7 @@ impl<'a> TryFrom> for Vec { Ok(Field { prop_name: alias.to_string(), documentation: field.documentation.clone(), + deprecated: field.deprecated, last_type_modifier: field.type_description.type_modifiers().1.clone(), type_ir: get_type_ir_for_field(field, concrete, sub_traversal)?, }) @@ -460,6 +461,7 @@ impl<'a> FieldTraversal<'a> { pub struct Field { pub prop_name: String, pub documentation: schema::Documentation, + pub deprecated: bool, pub last_type_modifier: schema_field::FieldTypeModifier, pub type_ir: FieldType, } diff --git a/src/typescript.rs b/src/typescript.rs index b77c8d7..65d90d7 100644 --- a/src/typescript.rs +++ b/src/typescript.rs @@ -97,7 +97,7 @@ fn input_def_from_type( let mut sorted = input_type.fields.iter().collect::>(); sorted.sort_unstable_by_key(|item| item.0); for (name, field) in sorted.into_iter() { - let doc = compile_documentation(&field.documentation, 2); + let doc = compile_documentation(&field.documentation, field.deprecated, 2); let field_type = from_input_def_field_def(config, name, field)?; let (_, last_type_mod) = field.type_description.type_modifiers(); let ts_field = match last_type_mod { @@ -132,7 +132,7 @@ fn enum_def_from_type( documentation: &schema::Documentation, enum_type: &schema::EnumType, ) -> String { - let doc_comment = compile_documentation(documentation, 0); + let doc_comment = compile_documentation(documentation, false, 0); let values = enum_type .possible_values .iter() @@ -234,17 +234,27 @@ pub fn compile_globals( }) } -fn compile_documentation(documentation: &schema::Documentation, tab_width: usize) -> Typescript { - documentation - .as_ref() - .map(|docs| { - let tab = " ".repeat(tab_width); - let processed_desc = docs - .replace('\n', &format!("\n {tab}* ")) - .replace("*/", EMPTY); - format!("/**\n {tab}* {processed_desc}\n {tab}*/\n{tab}") - }) - .unwrap_or_else(|| String::from(EMPTY)) +fn compile_documentation( + documentation: &schema::Documentation, + deprecated: bool, + tab_width: usize, +) -> Typescript { + let tab = " ".repeat(tab_width); + + let processed_documentation = documentation.as_deref().map(|docs| { + docs.replace('\n', &format!("\n {tab}* ")) + .replace("/*", EMPTY) + .replace("*/", EMPTY) + }); + + let wrap = |content: &str| format!("/**\n {tab}* {content}\n {tab}*/\n{tab}"); + + match (processed_documentation.as_deref(), deprecated) { + (Some(docs), true) => wrap(&format!("{docs}\n {tab}* @deprecated")), + (Some(docs), false) => wrap(docs), + (None, true) => wrap("@deprecated"), + (None, false) => EMPTY.to_string(), + } } fn compile_custom_scalar_name( @@ -414,7 +424,7 @@ fn type_definitions_from_complex_ir<'a>( ir::FieldType::TypeName => format!("\"{}\"", complex_ir.name), }; let prop_def_type = prop_type_def(&field_ir.last_type_modifier, flat_type_name); - let doc_comment = compile_documentation(&field_ir.documentation, 2); + let doc_comment = compile_documentation(&field_ir.documentation, field_ir.deprecated, 2); prop_defs.push(format!( " {doc_comment}{}: {prop_def_type};", field_ir.prop_name, diff --git a/tests/typescript/field.rs b/tests/typescript/field.rs index 9c99da6..df77e17 100644 --- a/tests/typescript/field.rs +++ b/tests/typescript/field.rs @@ -136,3 +136,79 @@ export type TestQuery = { ", ); } + +#[test] +fn compile_deprecated_field() { + let (mut cmd, temp_dir) = qlc_command_with_fake_dir_and_schema(); + temp_dir + .child("file.graphql") + .write_str( + " +query Deprecated($orgId: ID!) { + viewer { + user { + tier + } + } + org: node(id: $orgId) { + ... on Organization { + features + } + } +} + ", + ) + .unwrap(); + cmd.assert().success(); + assert_generated( + &temp_dir, + "Deprecated.ts", + r#" +import type { OrganizationAvailableFeatures, Tier } from "__generated__/globalTypes"; + +export type Deprecated_org_Organization = { + /** + * Features available for the organization to use (from LaunchDarkly) + * @deprecated + */ + features: (OrganizationAvailableFeatures | null)[] | null; +}; + +export type Deprecated_org_$$other = { + +}; + +export type Deprecated_org = Deprecated_org_Organization | Deprecated_org_$$other; + +export type Deprecated_viewer_user = { + /** + * @deprecated + */ + tier: Tier; +}; + +export type Deprecated_viewer = { + /** + * The user associated with the current viewer. Use this field to get info + * about current viewer and access any records associated w/ their account. + */ + user: Deprecated_viewer_user | null; +}; + +export type Deprecated = { + /** + * Fetches an object given its ID. + */ + org: Deprecated_org | null; + /** + * Access to fields relevant to a consumer of the application + */ + viewer: Deprecated_viewer | null; +}; + +export type DeprecatedVariables = { + orgId: string; +}; + "#, + ); +}