Skip to content
This repository was archived by the owner on Sep 27, 2023. It is now read-only.

Commit a53b116

Browse files
committed
fix opaque types using string literals
1 parent 9c97404 commit a53b116

File tree

3 files changed

+224
-495
lines changed

3 files changed

+224
-495
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,5 +104,6 @@
104104
},
105105
"publishConfig": {
106106
"registry": "https://registry.npmjs.org/"
107-
}
107+
},
108+
"prettier": {}
108109
}

src/TypeScriptGenerator.ts

Lines changed: 36 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import {
22
Condition,
3-
Directive,
43
Fragment,
54
IRVisitor,
65
LinkedField,
7-
Metadata,
86
Root,
97
ScalarField,
108
SchemaUtils,
@@ -32,6 +30,7 @@ const { isAbstractType } = SchemaUtils;
3230

3331
const REF_TYPE = " $refType";
3432
const FRAGMENT_REFS = " $fragmentRefs";
33+
const FRAGMENT_REFS_TYPE_NAME = "FragmentRefs";
3534
const MODULE_IMPORT_FIELD = "MODULE_IMPORT_FIELD";
3635
const DIRECTIVE_NAME = "raw_response_type";
3736

@@ -205,9 +204,8 @@ function selectionsToAST(
205204
props.push(
206205
readOnlyObjectTypeProperty(
207206
REF_TYPE,
208-
ts.createTypeReferenceNode(
209-
ts.createIdentifier(fragmentTypeName),
210-
undefined
207+
ts.createLiteralTypeNode(
208+
ts.createStringLiteral(`${fragmentTypeName}$ref`)
211209
)
212210
)
213211
);
@@ -303,16 +301,6 @@ function exportType(name: string, type: ts.TypeNode) {
303301
);
304302
}
305303

306-
function exportTypes(types: string[]) {
307-
return ts.createExportDeclaration(
308-
undefined,
309-
undefined,
310-
ts.createNamedExports(
311-
types.map(type => ts.createExportSpecifier(undefined, type))
312-
)
313-
);
314-
}
315-
316304
function importTypes(names: string[], fromModule: string): ts.Statement {
317305
return ts.createImportDeclaration(
318306
undefined,
@@ -381,14 +369,8 @@ function createVisitor(options: TypeGeneratorOptions): IRVisitor.NodeVisitor {
381369
createRawResponseTypeVisitor(state)
382370
);
383371
}
384-
const refetchableFragmentName = getRefetchableQueryParentFragmentName(
385-
state,
386-
node.metadata
387-
);
388372
const nodes = [
389-
...(refetchableFragmentName
390-
? generateFragmentRefsForRefetchable(refetchableFragmentName)
391-
: getFragmentImports(state)),
373+
...getFragmentDeclarations(state),
392374
...getEnumDefinitions(state),
393375
...inputObjectTypes,
394376
inputVariablesType,
@@ -444,30 +426,20 @@ function createVisitor(options: TypeGeneratorOptions): IRVisitor.NodeVisitor {
444426
return [selection];
445427
});
446428
state.generatedFragments.add(node.name);
447-
const fragmentTypes = getFragmentTypes(
448-
node.name,
449-
getRefetchableQueryPath(state, node.directives),
450-
state
451-
);
452429
const unmasked = node.metadata != null && node.metadata.mask === false;
453430
const baseType = selectionsToAST(
454431
selections,
455432
state,
456433
unmasked,
457-
unmasked ? undefined : getOldFragmentTypeName(node.name)
434+
unmasked ? undefined : node.name
458435
);
459436
const type = isPlural(node)
460437
? ts.createTypeReferenceNode(ts.createIdentifier("ReadonlyArray"), [
461438
baseType
462439
])
463440
: baseType;
464441

465-
return [
466-
...getFragmentImports(state),
467-
...getEnumDefinitions(state),
468-
...fragmentTypes,
469-
exportType(node.name, type)
470-
];
442+
return [...getEnumDefinitions(state), exportType(node.name, type)];
471443
},
472444
InlineFragment(node) {
473445
const typeCondition = node.typeCondition;
@@ -831,61 +803,6 @@ function generateInputVariablesType(node: Root, state: State) {
831803
);
832804
}
833805

834-
// If it's a @refetchable fragment, we generate the $fragmentRef in generated
835-
// query, and import it in the fragment to avoid circular dependencies
836-
function getRefetchableQueryParentFragmentName(
837-
state: State,
838-
metadata: Metadata
839-
): string | null {
840-
if (
841-
!(metadata && metadata.isRefetchableQuery) ||
842-
(!state.useHaste && !state.useSingleArtifactDirectory)
843-
) {
844-
return null;
845-
}
846-
const derivedFrom = metadata && metadata.derivedFrom;
847-
if (derivedFrom != null && typeof derivedFrom === "string") {
848-
return derivedFrom;
849-
}
850-
return null;
851-
}
852-
853-
function getRefetchableQueryPath(
854-
state: State,
855-
directives: ReadonlyArray<Directive>
856-
): string | undefined {
857-
let refetchableQuery: string | undefined;
858-
if (!state.useHaste && !state.useSingleArtifactDirectory) {
859-
return;
860-
}
861-
const directive = directives.find(d => d.name === "refetchable");
862-
const refetchableArgs = directive && directive.args;
863-
if (!refetchableArgs) {
864-
return;
865-
}
866-
const argument = refetchableArgs.find(
867-
arg => arg.kind === "Argument" && arg.name === "queryName"
868-
);
869-
if (
870-
argument &&
871-
argument.value &&
872-
argument.value.kind === "Literal" &&
873-
typeof argument.value.value === "string"
874-
) {
875-
refetchableQuery = argument.value.value;
876-
if (!state.useHaste) {
877-
refetchableQuery = "./" + refetchableQuery;
878-
}
879-
refetchableQuery += ".graphql";
880-
}
881-
return refetchableQuery;
882-
}
883-
884-
function generateFragmentRefsForRefetchable(name: string) {
885-
const oldFragmentTypeName = getOldFragmentTypeName(name);
886-
return declareExportOpaqueType(oldFragmentTypeName);
887-
}
888-
889806
function groupRefs(props: Selection[]): Selection[] {
890807
const result: Selection[] = [];
891808
const refs: string[] = [];
@@ -897,53 +814,23 @@ function groupRefs(props: Selection[]): Selection[] {
897814
}
898815
});
899816
if (refs.length > 0) {
900-
const value = ts.createIntersectionTypeNode(
901-
refs.map(ref =>
902-
ts.createTypeReferenceNode(
903-
ts.createIdentifier(getOldFragmentTypeName(ref)),
904-
undefined
905-
)
906-
)
817+
const refTypes = ts.createUnionTypeNode(
818+
refs.map(ref => ts.createLiteralTypeNode(ts.createStringLiteral(ref)))
907819
);
908820
result.push({
909821
key: FRAGMENT_REFS,
910822
conditional: false,
911-
value
823+
value: ts.createTypeReferenceNode(FRAGMENT_REFS_TYPE_NAME, [refTypes])
912824
});
913825
}
914826
return result;
915827
}
916828

917-
function createAnyTypeAlias(name: string): ts.TypeAliasDeclaration {
918-
return ts.createTypeAliasDeclaration(
919-
undefined,
920-
undefined,
921-
ts.createIdentifier(name),
922-
undefined,
923-
ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
924-
);
925-
}
926-
927-
function getFragmentImports(state: State) {
928-
const imports: ts.Statement[] = [];
829+
function getFragmentDeclarations(state: State): ts.Statement[] {
929830
if (state.usedFragments.size > 0) {
930-
const usedFragments = Array.from(state.usedFragments).sort();
931-
for (const usedFragment of usedFragments) {
932-
const fragmentTypeName = getOldFragmentTypeName(usedFragment);
933-
if (
934-
!state.generatedFragments.has(usedFragment) &&
935-
state.useSingleArtifactDirectory &&
936-
state.existingFragmentNames.has(usedFragment)
937-
) {
938-
imports.push(
939-
importTypes([fragmentTypeName], `./${usedFragment}.graphql`)
940-
);
941-
} else {
942-
imports.push(createAnyTypeAlias(fragmentTypeName));
943-
}
944-
}
831+
return [fragmentRefsType];
945832
}
946-
return imports;
833+
return [];
947834
}
948835

949836
function getEnumDefinitions({
@@ -982,62 +869,32 @@ function stringLiteralTypeAnnotation(name: string): ts.TypeNode {
982869
return ts.createLiteralTypeNode(ts.createLiteral(name));
983870
}
984871

985-
function getFragmentTypes(
986-
name: string,
987-
refetchableQueryPath: undefined | string,
988-
state: State
989-
) {
990-
const oldFragmentTypeName = getOldFragmentTypeName(name);
991-
992-
if (!state.useSingleArtifactDirectory && !state.useHaste) {
993-
return [
994-
exportType(
995-
oldFragmentTypeName,
996-
ts.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword)
997-
)
998-
];
999-
}
1000-
1001-
if (refetchableQueryPath) {
1002-
return [
1003-
importTypes([oldFragmentTypeName], refetchableQueryPath),
1004-
exportTypes([oldFragmentTypeName])
1005-
];
1006-
}
1007-
1008-
return declareExportOpaqueType(oldFragmentTypeName);
1009-
}
1010-
1011-
function declareExportOpaqueType(oldFragmentTypeName: string) {
1012-
const _refTypeName = `_${oldFragmentTypeName}`;
1013-
const _refType = ts.createVariableStatement(
1014-
[ts.createToken(ts.SyntaxKind.DeclareKeyword)],
1015-
ts.createVariableDeclarationList(
1016-
[
1017-
ts.createVariableDeclaration(
1018-
_refTypeName,
1019-
ts.createTypeOperatorNode(
1020-
ts.SyntaxKind.UniqueKeyword,
1021-
ts.createKeywordTypeNode(ts.SyntaxKind.SymbolKeyword)
1022-
)
1023-
)
1024-
],
1025-
ts.NodeFlags.Const
872+
// type Fragments<Refs extends string> = null | {[ref in Refs]: true}
873+
const fragmentRefsType = ts.createTypeAliasDeclaration(
874+
undefined,
875+
undefined,
876+
FRAGMENT_REFS_TYPE_NAME,
877+
[
878+
ts.createTypeParameterDeclaration(
879+
"Refs",
880+
ts.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
881+
undefined
1026882
)
1027-
);
1028-
1029-
return [
1030-
_refType,
1031-
exportType(
1032-
oldFragmentTypeName,
1033-
ts.createTypeQueryNode(ts.createIdentifier(_refTypeName))
883+
],
884+
ts.createUnionTypeNode([
885+
ts.createNull(),
886+
ts.createMappedTypeNode(
887+
undefined,
888+
ts.createTypeParameterDeclaration(
889+
"ref",
890+
ts.createTypeReferenceNode("Refs", undefined),
891+
undefined
892+
),
893+
undefined,
894+
ts.createLiteralTypeNode(ts.createTrue())
1034895
)
1035-
];
1036-
}
1037-
1038-
function getOldFragmentTypeName(name: string) {
1039-
return `${name}$ref`;
1040-
}
896+
])
897+
);
1041898

1042899
// Should match FLOW_TRANSFORMS array
1043900
// https://github.com/facebook/relay/blob/v6.0.0/packages/relay-compiler/language/javascript/RelayFlowGenerator.js#L621-L627

0 commit comments

Comments
 (0)