Skip to content

Commit 632381f

Browse files
committed
get type refs working
1 parent 09a9c5d commit 632381f

File tree

8 files changed

+2577
-99
lines changed

8 files changed

+2577
-99
lines changed

src/codegen.rs

+65-43
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,19 @@ struct CodeGenCtx {
112112

113113
/// True when we are parsing the inputs of a field with arguments
114114
parsing_inputs: bool,
115+
/// True when we are parsing the output of a field with arguments
116+
parsing_output: bool,
115117
}
116118

117119
impl CodeGenCtx {
118120
/// `manifest` is generated from the first pass in the Typescript compiler API code
119121
fn new(manifest: HashMap<String, GraphQLKind>) -> Self {
120122
let schema = Schema::new();
121-
println!("MANIFESET: {:?}", manifest);
122123
Self {
123124
schema,
124125
manifest,
125126
parsing_inputs: false,
127+
parsing_output: false,
126128
}
127129
}
128130

@@ -234,14 +236,30 @@ impl CodeGenCtx {
234236
TsType::TsKeywordType(TsKeywordType { kind, .. }) => {
235237
(Self::parse_keyword_type(kind)?, None)
236238
}
237-
TsType::TsArrayType(TsArrayType { elem_type, .. }) => (
238-
Type_::List {
239-
// TODO: There is no way to set non-nullable array elements in TS,
240-
// meaning we cant represent [Int!]!
241-
ty: Box::new(self.parse_type(field_name, elem_type, true)?.0),
242-
},
243-
None,
244-
),
239+
TsType::TsArrayType(TsArrayType { elem_type, .. }) => {
240+
match (self.parsing_output, &**elem_type) {
241+
(true, TsType::TsTypeLit(_)) => {
242+
let name = Self::compute_new_name(ComputeNameKind::Output, field_name);
243+
self.parse_type_literal(FieldKind::Object, &name, elem_type)?;
244+
(
245+
Type_::List {
246+
ty: Box::new(Type_::NamedType { name }),
247+
},
248+
None,
249+
)
250+
}
251+
_ => {
252+
(
253+
Type_::List {
254+
// TODO: There is no way to set non-nullable array elements in TS,
255+
// meaning we cant represent [Int!]!
256+
ty: Box::new(self.parse_type(field_name, elem_type, true)?.0),
257+
},
258+
None,
259+
)
260+
}
261+
}
262+
}
245263
TsType::TsTypeRef(TsTypeRef {
246264
type_name,
247265
type_params,
@@ -318,7 +336,7 @@ impl CodeGenCtx {
318336

319337
return Ok((Type_::NamedType { name }, None));
320338
}
321-
_ => self.parse_type("", non_null, true),
339+
_ => self.parse_type(field_name, non_null, true),
322340
};
323341
}
324342
TsType::TsTypeLit(_) => {
@@ -383,17 +401,20 @@ impl CodeGenCtx {
383401
.collect::<Result<Vec<InputValue>>>()?;
384402
self.parsing_inputs = false;
385403

404+
self.parsing_output = true;
386405
// Last param can be anything here, since we don't know if the return type is
387406
// optional until we parse it. `self.parse_type()` will make sure to return
388407
// the correct type if we are parsing return type
389408
let (ret_ty, _) = self.parse_type(field_name, &type_ann.type_ann, true)?;
409+
self.parsing_output = false;
390410

391411
return Ok((ret_ty, Some(args)));
392412
}
393413
TsType::TsUnionOrIntersectionType(TsUnionOrIntersectionType::TsUnionType(uni)) => {
394414
let typ = Self::unwrap_union(uni)?;
395415
return self.parse_type(field_name, typ, true);
396416
}
417+
// TODO: Move TsTypeLit in here
397418
r => {
398419
println!("{:?}", r);
399420
todo!();
@@ -1168,39 +1189,40 @@ mod tests {
11681189
],
11691190
);
11701191

1171-
// let src = "
1172-
// type User = { id: string; name: string; karma: number; }
1173-
// type GetUserInput = { id?: string; name?: string; karma?: number; }
1174-
// type Foo = { id: string; name: string; karma: number; }
1175-
// type Query = { getUser: (input: { user?: GetUserInput; karma?: number;}) => Promise<{ id: string; name: string; karma: number;}[] | null>; }
1176-
// type Mutation = {
1177-
// createUser: (input: { name?: string; karma?: number;}) => Promise<{ name: string;}>;
1178-
// updateUser: (input: { user: { id?: string; name?: string; };}) => Promise<{ id: string; name: string; karma: number;} | null>;
1179-
// }
1180-
// ";
1181-
// test(
1182-
// src,
1183-
// indoc! { r#"
1184-
// type User {
1185-
// id: String!
1186-
// name: String!
1187-
// karma: Int!
1188-
// }
1189-
// type FindUserOutput {
1190-
// user: User!
1191-
// success: Boolean!
1192-
// }
1193-
// type Query {
1194-
// findUser(id: String!): FindUserOutput
1195-
// }
1196-
// "# },
1197-
// vec![
1198-
// ("User", GraphQLKind::Object),
1199-
// ("GetUserInput", GraphQLKind::Input),
1200-
// ("Query", GraphQLKind::Object),
1201-
// ("Mutation", GraphQLKind::Object),
1202-
// ],
1203-
// );
1192+
let src = "
1193+
type User = { id: string; name: string; karma: number; }
1194+
type Mutation = {
1195+
updateUser: (input: { user: { id?: string; name?: string; };}) => Promise<{ id: string; name: string; karma: number;}[] | null>;
1196+
}
1197+
";
1198+
test(
1199+
src,
1200+
indoc! { r#"
1201+
type User {
1202+
id: String!
1203+
name: String!
1204+
karma: Int!
1205+
}
1206+
input UpdateUserInput {
1207+
id: String
1208+
name: String
1209+
}
1210+
type UpdateUserOutput {
1211+
id: String!
1212+
name: String!
1213+
karma: Int!
1214+
}
1215+
type Mutation {
1216+
updateUser(user: UpdateUserInput!): [UpdateUserOutput]
1217+
}
1218+
"# },
1219+
vec![
1220+
("User", GraphQLKind::Object),
1221+
("GetUserInput", GraphQLKind::Input),
1222+
("Query", GraphQLKind::Object),
1223+
("Mutation", GraphQLKind::Object),
1224+
],
1225+
);
12041226
}
12051227
}
12061228
}

type-reduce/esbuild.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
require('esbuild').buildSync({
2-
entryPoints: [ './src/index.js' ],
2+
entryPoints: ['./src/index.js'],
33
bundle: true,
44
outfile: 'dist/index.js',
55
platform: 'node'

type-reduce/jest.config.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module.exports = {
2+
roots: ['<rootDir>/src'],
3+
testMatch: [
4+
'**/__tests__/**/*.+(ts|tsx|js)',
5+
'**/?(*.)+(spec|test).+(ts|tsx|js)',
6+
],
7+
transform: {
8+
'^.+\\.(t|j)sx?$': ['@swc/jest'],
9+
},
10+
};

type-reduce/package.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
"node": ">=10"
1212
},
1313
"scripts": {
14+
"test": "jest",
1415
"start": "nodemon -e ts --ignore 'cleaned.ts' --ignore 'dist/' --exec \"yarn build && yarn out\"",
15-
"build": "tsc && node ./esbuild.js && mkdir -p ../dist && cp dist/* ../dist/",
16+
"build": "rm -rf ./dist && rm -rf ../dist && tsc && node ./esbuild.js && mkdir -p ../dist && mv dist/* ../dist/",
1617
"tsc": "tsc",
1718
"out": "node ./dist/index.js"
1819
},
@@ -26,8 +27,13 @@
2627
"name": "type-reduce",
2728
"author": "Zack Radisic",
2829
"devDependencies": {
30+
"@swc/core": "^1.2.102",
31+
"@swc/jest": "^0.2.5",
32+
"@types/jest": "^27.0.2",
2933
"@types/node": "^16.11.1",
3034
"husky": "^7.0.2",
35+
"jest": "^27.3.1",
36+
"ts-jest": "^27.0.7",
3137
"typescript": "^4.4.4"
3238
},
3339
"dependencies": {

type-reduce/src/index.ts

+9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ import {
33
} from 'ts-morph';
44
import { TypeReducer } from './lib';
55

6+
const prelude = `
7+
type ___ExpandRecursively<T> = T extends object ? T extends infer O ? {
8+
[K in keyof O]: ___ExpandRecursively<O[K]>;
9+
} : never : T;
10+
type ___Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
11+
`;
12+
613
export const reduceTypes = ({ tsconfigPath, path } = {tsconfigPath: './tsconfig.json', path: './src/schema.ts' }) => {
714
const project = new Project({ tsConfigFilePath: tsconfigPath })
815

@@ -14,6 +21,8 @@ export const reduceTypes = ({ tsconfigPath, path } = {tsconfigPath: './tsconfig.
1421
if (!sourceFile) {
1522
throw new Error('Schema file not found')
1623
}
24+
25+
sourceFile.insertText(0, prelude)
1726

1827
const diagnostics = project.getPreEmitDiagnostics()
1928
if (diagnostics.length) {

0 commit comments

Comments
 (0)