-
-
Notifications
You must be signed in to change notification settings - Fork 43
/
thread.ts
134 lines (115 loc) · 4.46 KB
/
thread.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import ts from 'typescript';
import type { GraphQLSPConfig } from '@gql.tada/internal';
import { getSchemaNamesFromConfig } from '@gql.tada/internal';
import { findAllCallExpressions } from '@0no-co/graphqlsp/api';
import { programFactory } from '../../ts';
import { expose } from '../../threads';
import type { TurboSignal, TurboWarning, TurboDocument } from './types';
export interface TurboParams {
rootPath: string;
configPath: string;
pluginConfig: GraphQLSPConfig;
}
async function* _runTurbo(params: TurboParams): AsyncIterableIterator<TurboSignal> {
const schemaNames = getSchemaNamesFromConfig(params.pluginConfig);
const factory = programFactory(params);
// NOTE: We add our override declaration here before loading all files
// This sets `__cacheDisabled` on the turbo cache, which disables the cache temporarily
// If we don't disable the cache then we couldn't regenerate it from inferred types
factory.addSourceFile({
fileId: '__gql-tada-override__.d.ts',
sourceText: DECLARATION_OVERRIDE,
scriptKind: ts.ScriptKind.TS,
});
const externalFiles = factory.createExternalFiles();
if (externalFiles.length) {
yield { kind: 'EXTERNAL_WARNING' };
await factory.addVirtualFiles(externalFiles);
}
const container = factory.build();
const pluginInfo = container.buildPluginInfo(params.pluginConfig);
const sourceFiles = container.getSourceFiles();
yield {
kind: 'FILE_COUNT',
fileCount: sourceFiles.length,
};
const checker = container.program.getTypeChecker();
for (const sourceFile of sourceFiles) {
let filePath = sourceFile.fileName;
const documents: TurboDocument[] = [];
const warnings: TurboWarning[] = [];
const calls = findAllCallExpressions(sourceFile, pluginInfo, false).nodes;
for (const call of calls) {
const callExpression = call.node.parent;
if (!ts.isCallExpression(callExpression)) {
continue;
}
const position = container.getSourcePosition(sourceFile, callExpression.getStart());
filePath = position.fileName;
if (!schemaNames.has(call.schema)) {
warnings.push({
message: call.schema
? `The '${call.schema}' schema is not in the configuration but was referenced by document.`
: schemaNames.size > 1
? 'The document is not for a known schema. Have you re-generated the output file?'
: 'Multiple schemas are configured, but the document is not for a specific schema.',
file: position.fileName,
line: position.line,
col: position.col,
});
continue;
}
const returnType = checker.getTypeAtLocation(callExpression);
const argumentType = checker.getTypeAtLocation(call.node);
// NOTE: `returnType.symbol` is incorrectly typed and is in fact
// optional and not always present
if (!returnType.symbol || returnType.symbol.getEscapedName() !== 'TadaDocumentNode') {
warnings.push({
message:
`The discovered document is not of type "TadaDocumentNode".\n` +
'If this is unexpected, please file an issue describing your case.',
file: position.fileName,
line: position.line,
col: position.col,
});
continue;
}
const argumentKey: string =
'value' in argumentType &&
typeof argumentType.value === 'string' &&
(argumentType.flags & ts.TypeFlags.StringLiteral) === 0
? JSON.stringify(argumentType.value)
: checker.typeToString(argumentType, callExpression, BUILDER_FLAGS);
const documentType = checker.typeToString(returnType, callExpression, BUILDER_FLAGS);
documents.push({
schemaName: call.schema,
argumentKey,
documentType,
});
}
yield {
kind: 'FILE_TURBO',
filePath,
documents,
warnings,
};
}
}
export const runTurbo = expose(_runTurbo);
const BUILDER_FLAGS: ts.TypeFormatFlags =
ts.TypeFormatFlags.NoTruncation |
ts.TypeFormatFlags.NoTypeReduction |
ts.TypeFormatFlags.InTypeAlias |
ts.TypeFormatFlags.UseFullyQualifiedType |
ts.TypeFormatFlags.GenerateNamesForShadowedTypeParams |
ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope |
ts.TypeFormatFlags.AllowUniqueESSymbolType |
ts.TypeFormatFlags.WriteTypeArgumentsOfSignature;
const DECLARATION_OVERRIDE = `
import * as _gqlTada from 'gql.tada';
declare module 'gql.tada' {
interface setupCache {
readonly __cacheDisabled: true;
}
}
`.trim();