@soda-gql/tsc-transformer 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +598 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +53 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +53 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +570 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +32 -11
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
13
|
+
__defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
23
|
+
value: mod,
|
|
24
|
+
enumerable: true
|
|
25
|
+
}) : target, mod));
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
let __soda_gql_builder = require("@soda-gql/builder");
|
|
29
|
+
let __soda_gql_plugin_common = require("@soda-gql/plugin-common");
|
|
30
|
+
let typescript = require("typescript");
|
|
31
|
+
typescript = __toESM(typescript);
|
|
32
|
+
let __soda_gql_common = require("@soda-gql/common");
|
|
33
|
+
let neverthrow = require("neverthrow");
|
|
34
|
+
|
|
35
|
+
//#region packages/tsc-transformer/src/ast/imports.ts
|
|
36
|
+
const RUNTIME_MODULE = "@soda-gql/runtime";
|
|
37
|
+
/**
|
|
38
|
+
* Ensure that the gqlRuntime require exists in the source file for CJS output.
|
|
39
|
+
* Injects: const __soda_gql_runtime = require("@soda-gql/runtime");
|
|
40
|
+
* Returns an updated source file with the require added if needed.
|
|
41
|
+
*/
|
|
42
|
+
const ensureGqlRuntimeRequire = ({ sourceFile, factory }) => {
|
|
43
|
+
if (sourceFile.statements.find((statement) => typescript.default.isVariableStatement(statement) && statement.declarationList.declarations.some((decl) => {
|
|
44
|
+
if (!typescript.default.isIdentifier(decl.name) || decl.name.text !== "__soda_gql_runtime") return false;
|
|
45
|
+
if (!decl.initializer || !typescript.default.isCallExpression(decl.initializer)) return false;
|
|
46
|
+
const callExpr = decl.initializer;
|
|
47
|
+
if (!typescript.default.isIdentifier(callExpr.expression) || callExpr.expression.text !== "require") return false;
|
|
48
|
+
const arg = callExpr.arguments[0];
|
|
49
|
+
return arg && typescript.default.isStringLiteral(arg) && arg.text === RUNTIME_MODULE;
|
|
50
|
+
}))) return sourceFile;
|
|
51
|
+
const requireCall = factory.createCallExpression(factory.createIdentifier("require"), void 0, [factory.createStringLiteral(RUNTIME_MODULE)]);
|
|
52
|
+
const variableDeclaration = factory.createVariableDeclaration(factory.createIdentifier("__soda_gql_runtime"), void 0, void 0, requireCall);
|
|
53
|
+
const newStatements = [factory.createVariableStatement(void 0, factory.createVariableDeclarationList([variableDeclaration], typescript.default.NodeFlags.Const)), ...sourceFile.statements];
|
|
54
|
+
return factory.updateSourceFile(sourceFile, newStatements);
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Ensure that the gqlRuntime import exists in the source file.
|
|
58
|
+
* gqlRuntime is always imported from @soda-gql/runtime.
|
|
59
|
+
* Returns an updated source file with the import added or merged.
|
|
60
|
+
*/
|
|
61
|
+
const ensureGqlRuntimeImport = ({ sourceFile, factory }) => {
|
|
62
|
+
const existing = sourceFile.statements.find((statement) => typescript.default.isImportDeclaration(statement) && typescript.default.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === RUNTIME_MODULE);
|
|
63
|
+
if (existing?.importClause?.namedBindings && typescript.default.isNamedImports(existing.importClause.namedBindings)) {
|
|
64
|
+
if (existing.importClause.namedBindings.elements.some((element) => typescript.default.isIdentifier(element.name) && element.name.text === "gqlRuntime")) return sourceFile;
|
|
65
|
+
const newElements = [...existing.importClause.namedBindings.elements, factory.createImportSpecifier(false, void 0, factory.createIdentifier("gqlRuntime"))];
|
|
66
|
+
const newNamedBindings = factory.createNamedImports(newElements);
|
|
67
|
+
const newImportClause = factory.createImportClause(false, void 0, newNamedBindings);
|
|
68
|
+
const newImportDeclaration = factory.createImportDeclaration(void 0, newImportClause, factory.createStringLiteral(RUNTIME_MODULE), void 0);
|
|
69
|
+
const newStatements$1 = sourceFile.statements.map((stmt) => stmt === existing ? newImportDeclaration : stmt);
|
|
70
|
+
return factory.updateSourceFile(sourceFile, newStatements$1);
|
|
71
|
+
}
|
|
72
|
+
const newStatements = [factory.createImportDeclaration(void 0, factory.createImportClause(false, void 0, factory.createNamedImports([factory.createImportSpecifier(false, void 0, factory.createIdentifier("gqlRuntime"))])), factory.createStringLiteral(RUNTIME_MODULE), void 0), ...sourceFile.statements];
|
|
73
|
+
return factory.updateSourceFile(sourceFile, newStatements);
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* Remove the graphql-system import (runtimeModule) and gql-related exports from the source file.
|
|
77
|
+
* After transformation, gqlRuntime is imported from @soda-gql/runtime instead,
|
|
78
|
+
* so the original graphql-system import should be completely removed.
|
|
79
|
+
*
|
|
80
|
+
* This handles both ESM imports and CommonJS require() statements (including interop helpers).
|
|
81
|
+
*/
|
|
82
|
+
const removeGraphqlSystemImports = ({ sourceFile, factory, graphqlSystemIdentifyHelper }) => {
|
|
83
|
+
const updatedStatements = Array.from(sourceFile.statements).filter((statement) => {
|
|
84
|
+
if (typescript.default.isImportDeclaration(statement) && typescript.default.isStringLiteral(statement.moduleSpecifier)) return !graphqlSystemIdentifyHelper.isGraphqlSystemImportSpecifier({
|
|
85
|
+
filePath: sourceFile.fileName,
|
|
86
|
+
specifier: statement.moduleSpecifier.text
|
|
87
|
+
});
|
|
88
|
+
if (typescript.default.isVariableStatement(statement)) return !statement.declarationList.declarations.every((decl) => {
|
|
89
|
+
const specifier = extractRequireTargetSpecifier(decl.initializer);
|
|
90
|
+
if (!specifier) return false;
|
|
91
|
+
return graphqlSystemIdentifyHelper.isGraphqlSystemImportSpecifier({
|
|
92
|
+
filePath: sourceFile.fileName,
|
|
93
|
+
specifier
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
return true;
|
|
97
|
+
});
|
|
98
|
+
if (updatedStatements.length === sourceFile.statements.length) return sourceFile;
|
|
99
|
+
return factory.updateSourceFile(sourceFile, updatedStatements);
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Create an "after" transformer that stubs out require() calls for the runtimeModule.
|
|
103
|
+
* This runs after TypeScript's own transformers (including CommonJS down-leveling),
|
|
104
|
+
* so we can replace `const X = require("@/graphql-system")` with a lightweight stub.
|
|
105
|
+
*
|
|
106
|
+
* This prevents the heavy graphql-system module from being loaded at runtime.
|
|
107
|
+
*/
|
|
108
|
+
const createAfterStubTransformer = ({ sourceFile, graphqlSystemIdentifyHelper }) => {
|
|
109
|
+
return (context) => {
|
|
110
|
+
const factory = context.factory;
|
|
111
|
+
const visitor = (node) => {
|
|
112
|
+
if (typescript.default.isVariableStatement(node)) {
|
|
113
|
+
let transformed = false;
|
|
114
|
+
const newDeclarations = node.declarationList.declarations.map((decl) => {
|
|
115
|
+
const specifier = extractRequireTargetSpecifier(decl.initializer);
|
|
116
|
+
if (!specifier) return decl;
|
|
117
|
+
if (!graphqlSystemIdentifyHelper.isGraphqlSystemImportSpecifier({
|
|
118
|
+
filePath: sourceFile.fileName,
|
|
119
|
+
specifier
|
|
120
|
+
})) return decl;
|
|
121
|
+
const stub = factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("Object"), "create"), void 0, [factory.createNull()]);
|
|
122
|
+
typescript.default.addSyntheticLeadingComment(stub, typescript.default.SyntaxKind.MultiLineCommentTrivia, "#__PURE__", false);
|
|
123
|
+
transformed = true;
|
|
124
|
+
return factory.updateVariableDeclaration(decl, decl.name, decl.exclamationToken, decl.type, stub);
|
|
125
|
+
});
|
|
126
|
+
if (transformed) return factory.updateVariableStatement(node, node.modifiers, factory.updateVariableDeclarationList(node.declarationList, newDeclarations));
|
|
127
|
+
}
|
|
128
|
+
return typescript.default.visitEachChild(node, visitor, context);
|
|
129
|
+
};
|
|
130
|
+
return (sourceFile$1) => typescript.default.visitNode(sourceFile$1, visitor);
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Check if an expression is a require() call for the runtimeModule.
|
|
135
|
+
* Handles multiple patterns:
|
|
136
|
+
* - require("@/graphql-system")
|
|
137
|
+
* - __importDefault(require("@/graphql-system"))
|
|
138
|
+
* - __importStar(require("@/graphql-system"))
|
|
139
|
+
*/
|
|
140
|
+
const extractRequireTargetSpecifier = (expr) => {
|
|
141
|
+
if (!expr) return;
|
|
142
|
+
if (typescript.default.isCallExpression(expr)) {
|
|
143
|
+
if (typescript.default.isIdentifier(expr.expression) && expr.expression.text === "require") {
|
|
144
|
+
const arg = expr.arguments[0];
|
|
145
|
+
if (arg && typescript.default.isStringLiteral(arg)) return arg.text;
|
|
146
|
+
}
|
|
147
|
+
if (typescript.default.isIdentifier(expr.expression)) {
|
|
148
|
+
const helperName = expr.expression.text;
|
|
149
|
+
if (helperName === "__importDefault" || helperName === "__importStar") {
|
|
150
|
+
const arg = expr.arguments[0];
|
|
151
|
+
if (arg && typescript.default.isCallExpression(arg)) {
|
|
152
|
+
if (typescript.default.isIdentifier(arg.expression) && arg.expression.text === "require") {
|
|
153
|
+
const requireArg = arg.arguments[0];
|
|
154
|
+
if (requireArg && typescript.default.isStringLiteral(requireArg)) return requireArg.text;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
//#endregion
|
|
163
|
+
//#region packages/tsc-transformer/src/ast/metadata.ts
|
|
164
|
+
const collectGqlDefinitionMetadata = ({ sourceFile, filename, createTracker }) => {
|
|
165
|
+
const exportBindings = collectExportBindings(sourceFile);
|
|
166
|
+
const tracker = (createTracker ?? __soda_gql_common.createCanonicalTracker)({
|
|
167
|
+
filePath: filename,
|
|
168
|
+
getExportName: (localName) => exportBindings.get(localName)
|
|
169
|
+
});
|
|
170
|
+
const getAnonymousName = createAnonymousNameFactory();
|
|
171
|
+
const scopeHandles = /* @__PURE__ */ new WeakMap();
|
|
172
|
+
const metadata = /* @__PURE__ */ new WeakMap();
|
|
173
|
+
const visit = (node) => {
|
|
174
|
+
if (typescript.isCallExpression(node) && isGqlDefinitionCall(node, typescript)) {
|
|
175
|
+
const depthBeforeRegister = tracker.currentDepth();
|
|
176
|
+
const { astPath } = tracker.registerDefinition();
|
|
177
|
+
const isTopLevel = depthBeforeRegister <= 1;
|
|
178
|
+
const exportInfo = isTopLevel ? resolveTopLevelExport(node, exportBindings, typescript) : null;
|
|
179
|
+
metadata.set(node, {
|
|
180
|
+
astPath,
|
|
181
|
+
isTopLevel,
|
|
182
|
+
isExported: exportInfo?.isExported ?? false,
|
|
183
|
+
exportBinding: exportInfo?.exportBinding
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const handle = maybeEnterScope(node, tracker, getAnonymousName, typescript);
|
|
188
|
+
if (handle) scopeHandles.set(node, handle);
|
|
189
|
+
typescript.forEachChild(node, visit);
|
|
190
|
+
const scopeHandle = scopeHandles.get(node);
|
|
191
|
+
if (scopeHandle) {
|
|
192
|
+
tracker.exitScope(scopeHandle);
|
|
193
|
+
scopeHandles.delete(node);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
visit(sourceFile);
|
|
197
|
+
return metadata;
|
|
198
|
+
};
|
|
199
|
+
const collectExportBindings = (sourceFile) => {
|
|
200
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
201
|
+
for (const statement of sourceFile.statements) {
|
|
202
|
+
if (typescript.isExportDeclaration(statement) && statement.exportClause && typescript.isNamedExports(statement.exportClause)) {
|
|
203
|
+
for (const element of statement.exportClause.elements) {
|
|
204
|
+
const name = element.name.text;
|
|
205
|
+
bindings.set(name, name);
|
|
206
|
+
}
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (typescript.isVariableStatement(statement) && statement.modifiers?.some((m) => m.kind === typescript.SyntaxKind.ExportKeyword)) {
|
|
210
|
+
for (const declaration of statement.declarationList.declarations) if (typescript.isIdentifier(declaration.name)) bindings.set(declaration.name.text, declaration.name.text);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if ((typescript.isFunctionDeclaration(statement) || typescript.isClassDeclaration(statement)) && statement.modifiers?.some((m) => m.kind === typescript.SyntaxKind.ExportKeyword) && statement.name) {
|
|
214
|
+
bindings.set(statement.name.text, statement.name.text);
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (typescript.isExpressionStatement(statement) && typescript.isBinaryExpression(statement.expression)) {
|
|
218
|
+
const exportName = getCommonJsExportName(statement.expression.left);
|
|
219
|
+
if (exportName) bindings.set(exportName, exportName);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return bindings;
|
|
223
|
+
};
|
|
224
|
+
const getCommonJsExportName = (node) => {
|
|
225
|
+
if (!typescript.isPropertyAccessExpression(node)) return null;
|
|
226
|
+
const isExports = typescript.isIdentifier(node.expression) && node.expression.text === "exports";
|
|
227
|
+
const isModuleExports = typescript.isPropertyAccessExpression(node.expression) && typescript.isIdentifier(node.expression.expression) && node.expression.expression.text === "module" && typescript.isIdentifier(node.expression.name) && node.expression.name.text === "exports";
|
|
228
|
+
if (!isExports && !isModuleExports) return null;
|
|
229
|
+
if (typescript.isIdentifier(node.name)) return node.name.text;
|
|
230
|
+
return null;
|
|
231
|
+
};
|
|
232
|
+
const createAnonymousNameFactory = () => {
|
|
233
|
+
const counters = /* @__PURE__ */ new Map();
|
|
234
|
+
return (kind) => {
|
|
235
|
+
const count = counters.get(kind) ?? 0;
|
|
236
|
+
counters.set(kind, count + 1);
|
|
237
|
+
return `${kind}#${count}`;
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
const isGqlDefinitionCall = (node, typescript$1) => {
|
|
241
|
+
if (!typescript$1.isCallExpression(node)) return false;
|
|
242
|
+
if (!typescript$1.isPropertyAccessExpression(node.expression)) return false;
|
|
243
|
+
if (!isGqlReference(node.expression.expression, typescript$1)) return false;
|
|
244
|
+
if (node.arguments.length === 0) return false;
|
|
245
|
+
const firstArg = node.arguments[0];
|
|
246
|
+
if (firstArg === void 0) return false;
|
|
247
|
+
return typescript$1.isArrowFunction(firstArg);
|
|
248
|
+
};
|
|
249
|
+
const isGqlReference = (expr, typescript$1) => {
|
|
250
|
+
if (typescript$1.isIdentifier(expr) && expr.text === "gql") return true;
|
|
251
|
+
if (!typescript$1.isPropertyAccessExpression(expr)) return false;
|
|
252
|
+
if (typescript$1.isIdentifier(expr.name) && expr.name.text === "gql") return true;
|
|
253
|
+
return isGqlReference(expr.expression, typescript$1);
|
|
254
|
+
};
|
|
255
|
+
const resolveTopLevelExport = (callNode, exportBindings, typescript$1) => {
|
|
256
|
+
const parent = callNode.parent;
|
|
257
|
+
if (!parent) return null;
|
|
258
|
+
if (typescript$1.isVariableDeclaration(parent)) {
|
|
259
|
+
const { name } = parent;
|
|
260
|
+
if (typescript$1.isIdentifier(name)) {
|
|
261
|
+
const exportBinding = exportBindings.get(name.text);
|
|
262
|
+
if (exportBinding) return {
|
|
263
|
+
isExported: true,
|
|
264
|
+
exportBinding
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (typescript$1.isBinaryExpression(parent)) {
|
|
269
|
+
const exportName = getCommonJsExportName(parent.left);
|
|
270
|
+
if (exportName && exportBindings.has(exportName)) return {
|
|
271
|
+
isExported: true,
|
|
272
|
+
exportBinding: exportName
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
};
|
|
277
|
+
const maybeEnterScope = (node, tracker, getAnonymousName, typescript$1) => {
|
|
278
|
+
if (typescript$1.isBinaryExpression(node)) {
|
|
279
|
+
const exportName = getCommonJsExportName(node.left);
|
|
280
|
+
if (exportName) return tracker.enterScope({
|
|
281
|
+
segment: exportName,
|
|
282
|
+
kind: "variable",
|
|
283
|
+
stableKey: `var:${exportName}`
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
if (typescript$1.isVariableDeclaration(node) && typescript$1.isIdentifier(node.name)) {
|
|
287
|
+
const name = node.name.text;
|
|
288
|
+
return tracker.enterScope({
|
|
289
|
+
segment: name,
|
|
290
|
+
kind: "variable",
|
|
291
|
+
stableKey: `var:${name}`
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
if (typescript$1.isArrowFunction(node)) {
|
|
295
|
+
const name = getAnonymousName("arrow");
|
|
296
|
+
return tracker.enterScope({
|
|
297
|
+
segment: name,
|
|
298
|
+
kind: "function",
|
|
299
|
+
stableKey: "arrow"
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (typescript$1.isFunctionDeclaration(node) || typescript$1.isFunctionExpression(node)) {
|
|
303
|
+
const name = node.name?.text ?? getAnonymousName("function");
|
|
304
|
+
return tracker.enterScope({
|
|
305
|
+
segment: name,
|
|
306
|
+
kind: "function",
|
|
307
|
+
stableKey: `func:${name}`
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
if (typescript$1.isClassDeclaration(node)) {
|
|
311
|
+
const name = node.name?.text ?? getAnonymousName("class");
|
|
312
|
+
return tracker.enterScope({
|
|
313
|
+
segment: name,
|
|
314
|
+
kind: "class",
|
|
315
|
+
stableKey: `class:${name}`
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (typescript$1.isMethodDeclaration(node) && typescript$1.isIdentifier(node.name)) {
|
|
319
|
+
const name = node.name.text;
|
|
320
|
+
return tracker.enterScope({
|
|
321
|
+
segment: name,
|
|
322
|
+
kind: "method",
|
|
323
|
+
stableKey: `member:${name}`
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
if (typescript$1.isPropertyDeclaration(node) && typescript$1.isIdentifier(node.name)) {
|
|
327
|
+
const name = node.name.text;
|
|
328
|
+
return tracker.enterScope({
|
|
329
|
+
segment: name,
|
|
330
|
+
kind: "property",
|
|
331
|
+
stableKey: `member:${name}`
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
if (typescript$1.isPropertyAssignment(node)) {
|
|
335
|
+
const key = node.name;
|
|
336
|
+
const name = typescript$1.isIdentifier(key) ? key.text : typescript$1.isStringLiteral(key) ? key.text : null;
|
|
337
|
+
if (name) return tracker.enterScope({
|
|
338
|
+
segment: name,
|
|
339
|
+
kind: "property",
|
|
340
|
+
stableKey: `prop:${name}`
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
return null;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region packages/tsc-transformer/src/ast/analysis.ts
|
|
348
|
+
const extractGqlCall = ({ callNode, filename, metadata, getArtifact }) => {
|
|
349
|
+
const meta = metadata.get(callNode);
|
|
350
|
+
if (!meta) return (0, neverthrow.err)(createMetadataMissingError({ filename }));
|
|
351
|
+
const canonicalId = (0, __soda_gql_plugin_common.resolveCanonicalId)(filename, meta.astPath);
|
|
352
|
+
const artifact = getArtifact(canonicalId);
|
|
353
|
+
if (!artifact) return (0, neverthrow.err)(createArtifactMissingError({
|
|
354
|
+
filename,
|
|
355
|
+
canonicalId
|
|
356
|
+
}));
|
|
357
|
+
if (artifact.type === "fragment") return (0, neverthrow.ok)({
|
|
358
|
+
callNode,
|
|
359
|
+
canonicalId,
|
|
360
|
+
type: "fragment",
|
|
361
|
+
artifact
|
|
362
|
+
});
|
|
363
|
+
if (artifact.type === "operation") return (0, neverthrow.ok)({
|
|
364
|
+
callNode,
|
|
365
|
+
canonicalId,
|
|
366
|
+
type: "operation",
|
|
367
|
+
artifact
|
|
368
|
+
});
|
|
369
|
+
return (0, neverthrow.err)(createUnsupportedArtifactTypeError({
|
|
370
|
+
filename,
|
|
371
|
+
canonicalId,
|
|
372
|
+
artifactType: artifact.type
|
|
373
|
+
}));
|
|
374
|
+
};
|
|
375
|
+
const createMetadataMissingError = ({ filename }) => ({
|
|
376
|
+
type: "PluginError",
|
|
377
|
+
stage: "analysis",
|
|
378
|
+
code: "SODA_GQL_METADATA_NOT_FOUND",
|
|
379
|
+
message: `No GraphQL metadata found for ${filename}`,
|
|
380
|
+
cause: { filename },
|
|
381
|
+
filename
|
|
382
|
+
});
|
|
383
|
+
const createArtifactMissingError = ({ filename, canonicalId }) => ({
|
|
384
|
+
type: "PluginError",
|
|
385
|
+
stage: "analysis",
|
|
386
|
+
code: "SODA_GQL_ANALYSIS_ARTIFACT_NOT_FOUND",
|
|
387
|
+
message: `No builder artifact found for canonical ID ${canonicalId}`,
|
|
388
|
+
cause: {
|
|
389
|
+
filename,
|
|
390
|
+
canonicalId
|
|
391
|
+
},
|
|
392
|
+
filename,
|
|
393
|
+
canonicalId
|
|
394
|
+
});
|
|
395
|
+
const createUnsupportedArtifactTypeError = ({ filename, canonicalId, artifactType }) => ({
|
|
396
|
+
type: "PluginError",
|
|
397
|
+
stage: "analysis",
|
|
398
|
+
code: "SODA_GQL_UNSUPPORTED_ARTIFACT_TYPE",
|
|
399
|
+
message: `Unsupported builder artifact type "${artifactType}" for canonical ID ${canonicalId}`,
|
|
400
|
+
cause: {
|
|
401
|
+
filename,
|
|
402
|
+
canonicalId,
|
|
403
|
+
artifactType
|
|
404
|
+
},
|
|
405
|
+
filename,
|
|
406
|
+
canonicalId,
|
|
407
|
+
artifactType
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region packages/tsc-transformer/src/ast/ast.ts
|
|
412
|
+
/**
|
|
413
|
+
* Build an object literal expression from a record of properties.
|
|
414
|
+
*/
|
|
415
|
+
const buildObjectExpression = (factory, properties) => {
|
|
416
|
+
const propertyAssignments = Object.entries(properties).map(([key, value]) => factory.createPropertyAssignment(factory.createIdentifier(key), value));
|
|
417
|
+
return factory.createObjectLiteralExpression(propertyAssignments);
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* Build a JSON.parse expression from an object value.
|
|
421
|
+
* This is used to emit large objects as JSON strings to reduce the calculation cost of both the compiler and the runtime.
|
|
422
|
+
*/
|
|
423
|
+
const buildJsonParseExpression = (factory, value) => factory.createCallExpression(factory.createPropertyAccessExpression(factory.createIdentifier("JSON"), factory.createIdentifier("parse")), void 0, [factory.createStringLiteral(JSON.stringify(value))]);
|
|
424
|
+
|
|
425
|
+
//#endregion
|
|
426
|
+
//#region packages/tsc-transformer/src/ast/runtime.ts
|
|
427
|
+
const createRuntimeAccessor = ({ isCJS, factory }) => isCJS ? factory.createPropertyAccessExpression(factory.createIdentifier("__soda_gql_runtime"), factory.createIdentifier("gqlRuntime")) : factory.createIdentifier("gqlRuntime");
|
|
428
|
+
const buildFragmentRuntimeCall = ({ gqlCall, factory, isCJS }) => {
|
|
429
|
+
return (0, neverthrow.ok)(factory.createCallExpression(factory.createPropertyAccessExpression(createRuntimeAccessor({
|
|
430
|
+
isCJS,
|
|
431
|
+
factory
|
|
432
|
+
}), factory.createIdentifier("fragment")), void 0, [buildObjectExpression(factory, { prebuild: buildObjectExpression(factory, { typename: factory.createStringLiteral(gqlCall.artifact.prebuild.typename) }) })]));
|
|
433
|
+
};
|
|
434
|
+
const buildOperationRuntimeComponents = ({ gqlCall, factory, isCJS }) => {
|
|
435
|
+
const runtimeCall = factory.createCallExpression(factory.createPropertyAccessExpression(createRuntimeAccessor({
|
|
436
|
+
isCJS,
|
|
437
|
+
factory
|
|
438
|
+
}), factory.createIdentifier("operation")), void 0, [buildObjectExpression(factory, {
|
|
439
|
+
prebuild: buildJsonParseExpression(factory, gqlCall.artifact.prebuild),
|
|
440
|
+
runtime: buildObjectExpression(factory, {})
|
|
441
|
+
})]);
|
|
442
|
+
return (0, neverthrow.ok)({
|
|
443
|
+
referenceCall: factory.createCallExpression(factory.createPropertyAccessExpression(createRuntimeAccessor({
|
|
444
|
+
isCJS,
|
|
445
|
+
factory
|
|
446
|
+
}), factory.createIdentifier("getOperation")), void 0, [factory.createStringLiteral(gqlCall.artifact.prebuild.operationName)]),
|
|
447
|
+
runtimeCall
|
|
448
|
+
});
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
//#endregion
|
|
452
|
+
//#region packages/tsc-transformer/src/ast/transformer.ts
|
|
453
|
+
const transformCallExpression = ({ callNode, filename, metadata, getArtifact, factory, isCJS }) => {
|
|
454
|
+
if (!metadata.has(callNode)) return (0, neverthrow.ok)({ transformed: false });
|
|
455
|
+
const gqlCallResult = extractGqlCall({
|
|
456
|
+
callNode,
|
|
457
|
+
filename,
|
|
458
|
+
metadata,
|
|
459
|
+
getArtifact
|
|
460
|
+
});
|
|
461
|
+
if (gqlCallResult.isErr()) return (0, neverthrow.err)(gqlCallResult.error);
|
|
462
|
+
const gqlCall = gqlCallResult.value;
|
|
463
|
+
return replaceWithRuntimeCall({
|
|
464
|
+
gqlCall,
|
|
465
|
+
factory,
|
|
466
|
+
isCJS,
|
|
467
|
+
filename
|
|
468
|
+
});
|
|
469
|
+
};
|
|
470
|
+
const replaceWithRuntimeCall = ({ gqlCall, factory, isCJS, filename }) => {
|
|
471
|
+
if (gqlCall.type === "fragment") {
|
|
472
|
+
const result = buildFragmentRuntimeCall({
|
|
473
|
+
gqlCall,
|
|
474
|
+
factory,
|
|
475
|
+
isCJS,
|
|
476
|
+
filename
|
|
477
|
+
});
|
|
478
|
+
if (result.isErr()) return (0, neverthrow.err)(result.error);
|
|
479
|
+
return (0, neverthrow.ok)({
|
|
480
|
+
transformed: true,
|
|
481
|
+
replacement: result.value
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
if (gqlCall.type === "operation") {
|
|
485
|
+
const result = buildOperationRuntimeComponents({
|
|
486
|
+
gqlCall,
|
|
487
|
+
factory,
|
|
488
|
+
isCJS
|
|
489
|
+
});
|
|
490
|
+
if (result.isErr()) return (0, neverthrow.err)(result.error);
|
|
491
|
+
const { referenceCall, runtimeCall } = result.value;
|
|
492
|
+
return (0, neverthrow.ok)({
|
|
493
|
+
transformed: true,
|
|
494
|
+
replacement: referenceCall,
|
|
495
|
+
runtimeCall
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
return (0, neverthrow.ok)({ transformed: false });
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
//#endregion
|
|
502
|
+
//#region packages/tsc-transformer/src/transformer.ts
|
|
503
|
+
const findLastImportIndex = ({ sourceFile }) => {
|
|
504
|
+
let lastIndex = -1;
|
|
505
|
+
const statements = sourceFile.statements;
|
|
506
|
+
for (let i = 0; i < statements.length; i++) {
|
|
507
|
+
const statement = statements[i];
|
|
508
|
+
if (statement && typescript.isImportDeclaration(statement)) lastIndex = i;
|
|
509
|
+
}
|
|
510
|
+
return lastIndex;
|
|
511
|
+
};
|
|
512
|
+
const createTransformer = ({ compilerOptions, config, artifact }) => {
|
|
513
|
+
const isCJS = compilerOptions.module === typescript.ModuleKind.CommonJS || compilerOptions.target === typescript.ScriptTarget.ES5;
|
|
514
|
+
const graphqlSystemIdentifyHelper = (0, __soda_gql_builder.createGraphqlSystemIdentifyHelper)(config);
|
|
515
|
+
const makeSourceFileEmpty = ({ factory, sourceFile }) => {
|
|
516
|
+
return factory.updateSourceFile(sourceFile, [factory.createExportDeclaration(void 0, false, factory.createNamedExports([]), void 0)]);
|
|
517
|
+
};
|
|
518
|
+
const transformGqlCalls = ({ sourceFile, context }) => {
|
|
519
|
+
let transformed = false;
|
|
520
|
+
const metadata = collectGqlDefinitionMetadata({
|
|
521
|
+
sourceFile,
|
|
522
|
+
filename: sourceFile.fileName
|
|
523
|
+
});
|
|
524
|
+
const runtimeCallsFromLastTransform = [];
|
|
525
|
+
const visitor = (node) => {
|
|
526
|
+
if (typescript.isCallExpression(node)) {
|
|
527
|
+
const result = transformCallExpression({
|
|
528
|
+
callNode: node,
|
|
529
|
+
filename: sourceFile.fileName,
|
|
530
|
+
metadata,
|
|
531
|
+
getArtifact: (canonicalId) => artifact.elements[canonicalId],
|
|
532
|
+
factory: context.factory,
|
|
533
|
+
isCJS
|
|
534
|
+
});
|
|
535
|
+
if (result.isErr()) {
|
|
536
|
+
console.error(`[@soda-gql/tsc-plugin] ${(0, __soda_gql_plugin_common.formatPluginError)(result.error)}`);
|
|
537
|
+
return node;
|
|
538
|
+
}
|
|
539
|
+
const transformResult = result.value;
|
|
540
|
+
if (transformResult.transformed) {
|
|
541
|
+
transformed = true;
|
|
542
|
+
if (transformResult.runtimeCall) runtimeCallsFromLastTransform.push(transformResult.runtimeCall);
|
|
543
|
+
return transformResult.replacement;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return typescript.visitEachChild(node, visitor, context);
|
|
547
|
+
};
|
|
548
|
+
const visitedNode = typescript.visitNode(sourceFile, visitor);
|
|
549
|
+
if (!visitedNode || !typescript.isSourceFile(visitedNode)) {
|
|
550
|
+
console.error(`[@soda-gql/tsc-plugin] Failed to transform source file: ${sourceFile.fileName}`);
|
|
551
|
+
return sourceFile;
|
|
552
|
+
}
|
|
553
|
+
if (!transformed) return sourceFile;
|
|
554
|
+
if (runtimeCallsFromLastTransform.length === 0) return visitedNode;
|
|
555
|
+
const lastImportIndex = findLastImportIndex({ sourceFile });
|
|
556
|
+
return context.factory.updateSourceFile(visitedNode, [
|
|
557
|
+
...visitedNode.statements.slice(0, lastImportIndex + 1),
|
|
558
|
+
...runtimeCallsFromLastTransform.map((expr) => context.factory.createExpressionStatement(expr)),
|
|
559
|
+
...visitedNode.statements.slice(lastImportIndex + 1)
|
|
560
|
+
]);
|
|
561
|
+
};
|
|
562
|
+
return { transform: ({ sourceFile, context }) => {
|
|
563
|
+
if (graphqlSystemIdentifyHelper.isGraphqlSystemFile({ filePath: sourceFile.fileName })) return {
|
|
564
|
+
transformed: true,
|
|
565
|
+
sourceFile: makeSourceFileEmpty({
|
|
566
|
+
factory: context.factory,
|
|
567
|
+
sourceFile
|
|
568
|
+
})
|
|
569
|
+
};
|
|
570
|
+
const original = sourceFile;
|
|
571
|
+
let current = sourceFile;
|
|
572
|
+
current = transformGqlCalls({
|
|
573
|
+
sourceFile: current,
|
|
574
|
+
context
|
|
575
|
+
});
|
|
576
|
+
if (current !== sourceFile) current = isCJS ? ensureGqlRuntimeRequire({
|
|
577
|
+
sourceFile: current,
|
|
578
|
+
factory: context.factory
|
|
579
|
+
}) : ensureGqlRuntimeImport({
|
|
580
|
+
sourceFile: current,
|
|
581
|
+
factory: context.factory
|
|
582
|
+
});
|
|
583
|
+
current = removeGraphqlSystemImports({
|
|
584
|
+
sourceFile: current,
|
|
585
|
+
factory: context.factory,
|
|
586
|
+
graphqlSystemIdentifyHelper
|
|
587
|
+
});
|
|
588
|
+
return {
|
|
589
|
+
transformed: current !== original,
|
|
590
|
+
sourceFile: current
|
|
591
|
+
};
|
|
592
|
+
} };
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
//#endregion
|
|
596
|
+
exports.createAfterStubTransformer = createAfterStubTransformer;
|
|
597
|
+
exports.createTransformer = createTransformer;
|
|
598
|
+
//# sourceMappingURL=index.cjs.map
|