Skip to content

Commit

Permalink
feat: implement infrastructure for separation of scalar types (#40)
Browse files Browse the repository at this point in the history
* chore: define ScalarTypeConfig

* chore: pass ScalarTypeConfig

* refactor: introduce TypeTarget

* test: fix ScalarTypeConfig related test

* refactor: remove impl TypePrinter for TypeSystemDocument

* refactor: introduce TypeTarget in SchemaTypePrinterContext

* feat: output four namespaces

* feat: do not emit unneeded types

* feat: use OperationOutput / OperationInput from operation_type_printer

* feat: implement server-side separation
  • Loading branch information
uhyo authored Dec 31, 2023
1 parent 0467702 commit 4bce853
Show file tree
Hide file tree
Showing 56 changed files with 1,419 additions and 545 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions crates/config-file/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{collections::HashMap, path::PathBuf, str::FromStr};

use serde::Deserialize;

use crate::parsing_utils::deserialize_fromstr;
use crate::{parsing_utils::deserialize_fromstr, scalar_type::ScalarTypeConfig};

#[derive(Debug, Default)]
pub struct Config {
Expand Down Expand Up @@ -73,7 +73,7 @@ impl FromStr for GenerateMode {
#[serde(default, rename_all = "camelCase")]
pub struct GenerateTypeConfig {
/// Type of scalars.
pub scalar_types: HashMap<String, String>,
pub scalar_types: HashMap<String, ScalarTypeConfig>,
/// Whether to allow undefined as input value
/// for nullable input fields.
pub allow_undefined_as_optional_input: bool,
Expand Down
4 changes: 4 additions & 0 deletions crates/config-file/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ mod load_config;
mod node;
mod parse_config;
mod parsing_utils;
mod scalar_type;
#[cfg(test)]
mod tests;
mod type_target;

pub use config::{Config, GenerateConfig, GenerateMode};
#[cfg(feature = "execute_js")]
Expand All @@ -19,3 +21,5 @@ pub use load_config::load_config;
#[cfg(feature = "execute_js")]
pub use node::{load_default_from_js_file, run_node};
pub use parse_config::parse_config;
pub use scalar_type::ScalarTypeConfig;
pub use type_target::TypeTarget;
60 changes: 60 additions & 0 deletions crates/config-file/src/scalar_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use serde::Deserialize;

use crate::TypeTarget;

/// Representation of a scalar type's TypeScript type
/// as defined in the config file.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ScalarTypeConfig {
/// Single specification for use in all situations.
Single(String),
}

impl ScalarTypeConfig {
/// Get the TypeScript type for given target.
pub fn get_type(&self, _target: TypeTarget) -> &str {
match self {
ScalarTypeConfig::Single(type_name) => type_name,
}
}
/// Get the TypeScript type as a resolver output type.
pub fn as_resolver_output_type(&self) -> &str {
match self {
ScalarTypeConfig::Single(type_name) => type_name,
}
}
/// Get the TypeScript type as a resolver input type.
pub fn as_resolver_input_type(&self) -> &str {
match self {
ScalarTypeConfig::Single(type_name) => type_name,
}
}
/// Get the TypeScript type as an operation output type.
pub fn as_operation_output_type(&self) -> &str {
match self {
ScalarTypeConfig::Single(type_name) => type_name,
}
}
/// Get the TypeScript type as an operation input type.
pub fn as_operation_input_type(&self) -> &str {
match self {
ScalarTypeConfig::Single(type_name) => type_name,
}
}
/// Returns an Iterator over all type names used in this config.
pub fn type_names(&self) -> impl Iterator<Item = &str> {
match self {
ScalarTypeConfig::Single(type_name) => std::iter::once(type_name.as_str()),
}
}
}

impl<'de> Deserialize<'de> for ScalarTypeConfig {
fn deserialize<D>(deserializer: D) -> Result<ScalarTypeConfig, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Ok(ScalarTypeConfig::Single(s))
}
}
32 changes: 25 additions & 7 deletions crates/config-file/src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::PathBuf;

use crate::{parse_config, GenerateMode};
use crate::{parse_config, GenerateMode, ScalarTypeConfig};

mod export;
mod name;
Expand Down Expand Up @@ -81,12 +81,30 @@ extensions:
assert_eq!(
config.generate.r#type.scalar_types,
vec![
("Date".to_owned(), "Date".to_owned()),
("BigInt".to_owned(), "bigint".to_owned()),
("Int".to_owned(), "number".to_owned()),
("Float".to_owned(), "number".to_owned()),
("ID".to_owned(), "string".to_owned()),
("String".to_owned(), "string".to_owned()),
(
"Date".to_owned(),
ScalarTypeConfig::Single("Date".to_owned())
),
(
"BigInt".to_owned(),
ScalarTypeConfig::Single("bigint".to_owned())
),
(
"Int".to_owned(),
ScalarTypeConfig::Single("number".to_owned())
),
(
"Float".to_owned(),
ScalarTypeConfig::Single("number".to_owned())
),
(
"ID".to_owned(),
ScalarTypeConfig::Single("string".to_owned())
),
(
"String".to_owned(),
ScalarTypeConfig::Single("string".to_owned())
),
]
.into_iter()
.collect()
Expand Down
12 changes: 9 additions & 3 deletions crates/config-file/src/tests/type.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;

use crate::parse_config;
use crate::{parse_config, ScalarTypeConfig};

#[test]
fn default_config() {
Expand Down Expand Up @@ -47,8 +47,14 @@ extensions:
let config = parse_config(config).unwrap();
let ty = config.generate.r#type;
let mut expected = HashMap::new();
expected.insert("DateTime".to_string(), "Date".to_string());
expected.insert("JSON".to_string(), "any".to_string());
expected.insert(
"DateTime".to_string(),
ScalarTypeConfig::Single("Date".to_string()),
);
expected.insert(
"JSON".to_string(),
ScalarTypeConfig::Single("any".to_string()),
);
assert_eq!(ty.scalar_types, expected);
assert!(!ty.allow_undefined_as_optional_input);
}
40 changes: 40 additions & 0 deletions crates/config-file/src/type_target.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use std::fmt::Display;

#[derive(Copy, Clone)]
pub enum TypeTarget {
OperationInput,
OperationOutput,
ResolverInput,
ResolverOutput,
}

impl TypeTarget {
/// Returns the string representation of self.
/// String representation is prefixed with `__` for use in type definitions.
pub fn as_str(&self) -> &'static str {
match self {
TypeTarget::OperationInput => "__OperationInput",
TypeTarget::OperationOutput => "__OperationOutput",
TypeTarget::ResolverInput => "__ResolverInput",
TypeTarget::ResolverOutput => "__ResolverOutput",
}
}

/// Returns whether self is an output target.
pub fn is_output(&self) -> bool {
matches!(
self,
TypeTarget::OperationOutput | TypeTarget::ResolverOutput
)
}
/// Returns whether self is an input target.
pub fn is_input(&self) -> bool {
!self.is_output()
}
}

impl Display for TypeTarget {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
1 change: 1 addition & 0 deletions crates/plugin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ edition = "2021"
nitrogql-ast = { path = "../ast" }
graphql-builtins = { path = "../builtins" }
nitrogql-checker = { path = "../checker" }
nitrogql-config-file = { path = "../config-file" }
nitrogql-error = { path = "../error" }
nitrogql-parser = { path = "../parser" }
nitrogql-printer = { path = "../printer" }
Expand Down
4 changes: 3 additions & 1 deletion crates/plugin/src/model_plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use nitrogql_ast::{
value::Value,
TypeSystemDocument,
};
use nitrogql_config_file::TypeTarget;
use nitrogql_printer::{
ts_types::{ts_types_util::ts_union, TSType},
ResolverTypePrinterOptions,
Expand Down Expand Up @@ -167,8 +168,9 @@ directive @model(
let obj_type = TSType::TypeFunc(
Box::new(TSType::TypeVariable("Pick".into())),
vec![
TSType::NamespaceMember(
TSType::NamespaceMember3(
options.schema_root_namespace.clone(),
TypeTarget::ResolverOutput.to_string(),
def.name.name.into(),
),
ts_union(model_field_names.map(|n| TSType::StringLiteral(n.into()))),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import type { GraphQLResolveInfo } from "graphql";
import type * as Schema from "";
type __Resolver<Parent, Args, Context, Result> = (parent: Parent, args: Args, context: Context, info: GraphQLResolveInfo) => Result | Promise<Result>;
type __TypeResolver<Obj, Context, Result> = (object: Obj, context: Context, info: GraphQLResolveInfo) => Result | Promise<Result>;
type Int = Schema.Int;
type Float = Schema.Float;
type String = Schema.String;
type Boolean = Schema.Boolean;
type ID = Schema.ID;
type User = Pick<Schema.User, "id" | "name">;
type Post = Pick<Schema.Post, "id" | "title">;
type Int = Schema.__ResolverOutput.Int;
type Float = Schema.__ResolverOutput.Float;
type String = Schema.__ResolverOutput.String;
type Boolean = Schema.__ResolverOutput.Boolean;
type ID = Schema.__ResolverOutput.ID;
type User = Pick<Schema.__ResolverOutput.User, "id" | "name">;
type Post = Pick<Schema.__ResolverOutput.Post, "id" | "title">;
export type Resolvers<Context> = {
User: {
age: __Resolver<User, {}, Context, Int>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ expression: printed
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
import type * as Schema from "";

type Result = Schema.__SelectionSet<Schema.Query, {
me: Schema.__SelectionSet<Schema.User, {
id: Schema.ID;
name: Schema.String;
type: Schema.UserType;
age: Schema.Int | null;
type Result = Schema.__SelectionSet<Schema.__OperationOutput.Query, {
me: Schema.__SelectionSet<Schema.__OperationOutput.User, {
id: Schema.__OperationOutput.ID;
name: Schema.__OperationOutput.String;
type: Schema.__OperationOutput.UserType;
age: Schema.__OperationOutput.Int | null;
}, {}>;
}, {}>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ expression: printed
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
import type * as Schema from "";

export type SampleQueryResult = Schema.__SelectionSet<Schema.Query, {
me: Schema.__SelectionSet<Schema.User, {
id: Schema.ID;
name: Schema.String;
type: Schema.UserType;
age: Schema.Int | null;
export type SampleQueryResult = Schema.__SelectionSet<Schema.__OperationOutput.Query, {
me: Schema.__SelectionSet<Schema.__OperationOutput.User, {
id: Schema.__OperationOutput.ID;
name: Schema.__OperationOutput.String;
type: Schema.__OperationOutput.UserType;
age: Schema.__OperationOutput.Int | null;
}, {}>;
}, {}>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,18 @@ expression: printed
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
import type * as Schema from "";

type Result = Schema.__SelectionSet<Schema.Query, {
me: Schema.__SelectionSet<Schema.User, {
id: Schema.ID;
name: Schema.String;
type: Schema.UserType;
age: Schema.Int | null;
posts: (Schema.__SelectionSet<Schema.User, {
id: Schema.ID;
}, {}> | Schema.__SelectionSet<Schema.Bot, {
id: Schema.ID;
}, {}> | Schema.__SelectionSet<Schema.Post, {
id: Schema.ID;
type Result = Schema.__SelectionSet<Schema.__OperationOutput.Query, {
me: Schema.__SelectionSet<Schema.__OperationOutput.User, {
id: Schema.__OperationOutput.ID;
name: Schema.__OperationOutput.String;
type: Schema.__OperationOutput.UserType;
age: Schema.__OperationOutput.Int | null;
posts: (Schema.__SelectionSet<Schema.__OperationOutput.User, {
id: Schema.__OperationOutput.ID;
}, {}> | Schema.__SelectionSet<Schema.__OperationOutput.Bot, {
id: Schema.__OperationOutput.ID;
}, {}> | Schema.__SelectionSet<Schema.__OperationOutput.Post, {
id: Schema.__OperationOutput.ID;
}, {}>)[];
}, {}>;
}, {}>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ expression: printed
import type { TypedDocumentNode } from "@graphql-typed-document-node/core";
import type * as Schema from "";

type TestResult = Schema.__SelectionSet<Schema.Query, {
me: Schema.__SelectionSet<Schema.User, {
id: Schema.ID;
name: Schema.String;
type: Schema.UserType;
age: Schema.Int | null;
posts: (Schema.__SelectionSet<Schema.User, {
id: Schema.ID;
id: Schema.ID;
}, {}> | Schema.__SelectionSet<Schema.Bot, {
id: Schema.ID;
id: Schema.ID;
}, {}> | Schema.__SelectionSet<Schema.Post, {
id: Schema.ID;
id: Schema.ID;
type TestResult = Schema.__SelectionSet<Schema.__OperationOutput.Query, {
me: Schema.__SelectionSet<Schema.__OperationOutput.User, {
id: Schema.__OperationOutput.ID;
name: Schema.__OperationOutput.String;
type: Schema.__OperationOutput.UserType;
age: Schema.__OperationOutput.Int | null;
posts: (Schema.__SelectionSet<Schema.__OperationOutput.User, {
id: Schema.__OperationOutput.ID;
id: Schema.__OperationOutput.ID;
}, {}> | Schema.__SelectionSet<Schema.__OperationOutput.Bot, {
id: Schema.__OperationOutput.ID;
id: Schema.__OperationOutput.ID;
}, {}> | Schema.__SelectionSet<Schema.__OperationOutput.Post, {
id: Schema.__OperationOutput.ID;
id: Schema.__OperationOutput.ID;
}, {}>)[];
}, {}>;
}, {}>;
Expand All @@ -30,12 +30,12 @@ declare const TestQuery: TypedDocumentNode<TestResult, TestVariables>;

export { TestQuery as default };

export type F = Schema.__SelectionSet<Schema.User, {
id: Schema.ID;
}, {}> | Schema.__SelectionSet<Schema.Bot, {
id: Schema.ID;
}, {}> | Schema.__SelectionSet<Schema.Post, {
id: Schema.ID;
export type F = Schema.__SelectionSet<Schema.__OperationOutput.User, {
id: Schema.__OperationOutput.ID;
}, {}> | Schema.__SelectionSet<Schema.__OperationOutput.Bot, {
id: Schema.__OperationOutput.ID;
}, {}> | Schema.__SelectionSet<Schema.__OperationOutput.Post, {
id: Schema.__OperationOutput.ID;
}, {}>;

export const F: TypedDocumentNode<F, never>;
Expand Down
Loading

0 comments on commit 4bce853

Please sign in to comment.