-
Notifications
You must be signed in to change notification settings - Fork 4
/
inline-queries.ts
144 lines (129 loc) · 4.26 KB
/
inline-queries.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
135
136
137
138
139
140
141
142
143
144
import { stripIndent } from 'common-tags';
import { $, adapter } from 'edgedb';
import { $ as $$ } from 'execa';
import { Node, SyntaxKind, VariableDeclarationKind } from 'ts-morph';
import { injectHydrators } from './inject-hydrators';
import { analyzeQuery } from './query-files';
import { customScalars } from './scalars';
import { addCustomScalarImports, GeneratorParams } from './util';
export async function generateInlineQueries({
client,
root,
hydrators,
}: GeneratorParams) {
console.log('Generating queries for edgeql() calls...');
const grepForShortList = await $$({
reject: false,
cwd: root.getPath(),
})`grep -lR edgeql src --exclude-dir=src/core/edgedb`;
const shortList = grepForShortList.stdout
? root.addSourceFilesAtPaths(grepForShortList.stdout.split('\n'))
: [];
const queries =
shortList.flatMap((file) =>
file.getDescendantsOfKind(SyntaxKind.CallExpression).flatMap((call) => {
if (call.getExpression().getText() !== 'edgeql') {
return [];
}
const args = call.getArguments();
// // 1000x slower to confirm edgeql import
// const defs = call
// .getExpressionIfKindOrThrow(SyntaxKind.Identifier)
// .getDefinitionNodes();
// if (
// !defs[0].getSourceFile().getFilePath().endsWith('edgedb/edgeql.ts')
// ) {
// return [];
// }
if (
args.length > 1 ||
(!Node.isStringLiteral(args[0]) &&
!Node.isNoSubstitutionTemplateLiteral(args[0]))
) {
return [];
}
const query = args[0].getText().slice(1, -1);
return { query, call };
}),
) ?? [];
const inlineQueriesFile = root.createSourceFile(
'src/core/edgedb/generated-client/inline-queries.ts',
`import type { TypedEdgeQL } from '../edgeql';`,
{ overwrite: true },
);
const queryMapType = inlineQueriesFile.addInterface({
name: 'InlineQueryMap',
isExported: true,
});
const imports = new Set<string>();
const seen = new Set<string>();
const queryMap = new Map<
string,
{ query: string; cardinality: $.Cardinality }
>();
for (const { query, call } of queries) {
// Prevent duplicate keys in QueryMap in the off chance that two queries are identical
if (seen.has(query)) {
continue;
}
seen.add(query);
const path = adapter.path.posix.relative(
root.getPath(),
call.getSourceFile().getFilePath(),
);
const lineNumber = call.getStartLineNumber();
const source = `./${path}:${lineNumber}`;
let types;
let error;
try {
const injectedQuery = injectHydrators(query, hydrators);
types = await analyzeQuery(client, injectedQuery);
console.log(` ${source}`);
} catch (err) {
error = err as Error;
console.log(`Error in query '${source}': ${String(err)}`);
}
if (types) {
// Save cardinality & hydrated query for use at runtime.
queryMap.set(stripIndent(query), {
query: injectHydrators(query, hydrators),
cardinality: types.cardinality,
});
// Add imports to the used imports list
[...types.imports].forEach((i) => imports.add(i));
}
queryMapType.addProperty({
name: `[\`${query}\`]`,
type: types
? `TypedEdgeQL<${types.args}, ${types.result}>`
: error
? `{ ${error.name}: \`${error.message.trim()}\` }`
: 'unknown',
leadingTrivia:
(queryMapType.getProperties().length > 0 ? '\n' : '') +
`/** {@link import('${path}')} L${lineNumber} */\n`,
});
}
addCustomScalarImports(
inlineQueriesFile,
[...imports].flatMap((i) => customScalars.get(i) ?? []),
0,
);
const builtIn = ['$', ...[...imports].filter((i) => !customScalars.has(i))];
inlineQueriesFile.insertImportDeclaration(0, {
isTypeOnly: true,
namedImports: builtIn,
moduleSpecifier: 'edgedb',
});
const queryMapAsStr = JSON.stringify([...queryMap], null, 2);
inlineQueriesFile.addVariableStatement({
isExported: true,
declarationKind: VariableDeclarationKind.Const,
declarations: [
{
name: 'InlineQueryRuntimeMap',
initializer: `new Map<string, { query: string, cardinality: \`\${$.Cardinality}\` }>(${queryMapAsStr})`,
},
],
});
}