@nestbridge/webpack 2.0.3 → 2.1.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.
|
@@ -0,0 +1,1462 @@
|
|
|
1
|
+
import { SyntaxKind, isArrayLiteralExpression, isArrayTypeNode, isArrowFunction, isBlock, isCallExpression, isClassDeclaration, isDecorator, isExportDeclaration, isExpressionStatement, isFalseLiteral, isFunctionExpression, isIdentifier, isImportDeclaration, isMethodDeclaration, isNamedExports, isNamedImports, isNewExpression, isNullLiteral, isNumericLiteral, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, isReturnStatement, isSpreadElement, isStringLiteral, isStringLiteralLikeNode, isTrueLiteral, isTypeReferenceNode, isUnionTypeNode, isVariableStatement } from "typescript/unstable/ast";
|
|
2
|
+
import { API } from "typescript/unstable/sync";
|
|
3
|
+
import { existsSync, globSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, extname, join, posix, relative, resolve, sep } from "node:path";
|
|
5
|
+
import { createUnplugin } from "unplugin";
|
|
6
|
+
//#region ../unplugin/build/nestbridge.unplugin.js
|
|
7
|
+
var getDecorators = (node) => {
|
|
8
|
+
const decorators = node.modifiers?.filter(isDecorator);
|
|
9
|
+
return decorators === void 0 || decorators.length === 0 ? void 0 : decorators;
|
|
10
|
+
};
|
|
11
|
+
var findDecorator = (node, decoratorName) => {
|
|
12
|
+
const decorators = getDecorators(node);
|
|
13
|
+
if (decorators === void 0) return;
|
|
14
|
+
return decorators.find((decorator) => decoratorNameOf(decorator) === decoratorName);
|
|
15
|
+
};
|
|
16
|
+
var findAnyDecorator = (node, decoratorNames) => {
|
|
17
|
+
const decorators = getDecorators(node);
|
|
18
|
+
if (decorators === void 0) return;
|
|
19
|
+
return decorators.find((decorator) => decoratorNames.includes(decoratorNameOf(decorator) ?? ""));
|
|
20
|
+
};
|
|
21
|
+
var decoratorNameOf = (decorator) => {
|
|
22
|
+
const expression = decorator.expression;
|
|
23
|
+
if (isCallExpression(expression) && isIdentifier(expression.expression)) return expression.expression.text;
|
|
24
|
+
if (isIdentifier(expression)) return expression.text;
|
|
25
|
+
};
|
|
26
|
+
var readStaticStringArgument = (decorator) => {
|
|
27
|
+
const expression = decorator.expression;
|
|
28
|
+
if (!isCallExpression(expression)) return {
|
|
29
|
+
ok: true,
|
|
30
|
+
value: void 0
|
|
31
|
+
};
|
|
32
|
+
const [firstArgument] = expression.arguments;
|
|
33
|
+
if (firstArgument === void 0) return {
|
|
34
|
+
ok: true,
|
|
35
|
+
value: void 0
|
|
36
|
+
};
|
|
37
|
+
if (isStringLiteralLikeNode(firstArgument)) return {
|
|
38
|
+
ok: true,
|
|
39
|
+
value: firstArgument.text
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
argumentText: firstArgument.getText()
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
var HTTP_METHOD_DECORATOR_NAMES = {
|
|
47
|
+
Get: "GET",
|
|
48
|
+
Post: "POST",
|
|
49
|
+
Put: "PUT",
|
|
50
|
+
Patch: "PATCH",
|
|
51
|
+
Delete: "DELETE"
|
|
52
|
+
};
|
|
53
|
+
var locationOf = (sourceFile, node) => {
|
|
54
|
+
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
|
|
55
|
+
return {
|
|
56
|
+
line: line + 1,
|
|
57
|
+
column: character + 1
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
var SUPPORTED_PARAMETER_DECORATORS = [
|
|
61
|
+
"Param",
|
|
62
|
+
"Query",
|
|
63
|
+
"Body",
|
|
64
|
+
"Headers"
|
|
65
|
+
];
|
|
66
|
+
var KNOWN_UNSUPPORTED_PARAMETER_DECORATORS = [
|
|
67
|
+
"Req",
|
|
68
|
+
"Res",
|
|
69
|
+
"Request",
|
|
70
|
+
"Response"
|
|
71
|
+
];
|
|
72
|
+
var extractParameterDefinition = (parameter, index, context) => {
|
|
73
|
+
const decorator = findAnyDecorator(parameter, [...SUPPORTED_PARAMETER_DECORATORS, ...KNOWN_UNSUPPORTED_PARAMETER_DECORATORS]);
|
|
74
|
+
const location = locationOf(context.sourceFile, parameter);
|
|
75
|
+
if (decorator === void 0) {
|
|
76
|
+
context.diagnostics.push({
|
|
77
|
+
code: "unsupported-parameter",
|
|
78
|
+
title: "Unsupported parameter.",
|
|
79
|
+
controllerName: context.controllerName,
|
|
80
|
+
memberName: context.methodName,
|
|
81
|
+
detail: `Parameter "${parameter.name.getText()}" must be decorated with one of @Param, @Query, @Body, or @Headers to be transported through a NestBridge HTTP client.`,
|
|
82
|
+
filePath: context.filePath,
|
|
83
|
+
line: location.line,
|
|
84
|
+
column: location.column
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const decoratorName = decoratorNameOf(decorator);
|
|
89
|
+
if (decoratorName !== void 0 && KNOWN_UNSUPPORTED_PARAMETER_DECORATORS.includes(decoratorName)) {
|
|
90
|
+
context.diagnostics.push({
|
|
91
|
+
code: "unsupported-parameter",
|
|
92
|
+
title: "Unsupported parameter.",
|
|
93
|
+
controllerName: context.controllerName,
|
|
94
|
+
memberName: context.methodName,
|
|
95
|
+
detail: `@${decoratorName}() cannot be transported through a NestBridge HTTP client.`,
|
|
96
|
+
filePath: context.filePath,
|
|
97
|
+
line: location.line,
|
|
98
|
+
column: location.column
|
|
99
|
+
});
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!isIdentifier(parameter.name)) {
|
|
103
|
+
context.diagnostics.push({
|
|
104
|
+
code: "unsupported-parameter",
|
|
105
|
+
title: "Unsupported parameter.",
|
|
106
|
+
controllerName: context.controllerName,
|
|
107
|
+
memberName: context.methodName,
|
|
108
|
+
detail: "Destructured parameters cannot be transported through a NestBridge HTTP client.",
|
|
109
|
+
filePath: context.filePath,
|
|
110
|
+
line: location.line,
|
|
111
|
+
column: location.column
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const argument = readStaticStringArgument(decorator);
|
|
116
|
+
if (!argument.ok) {
|
|
117
|
+
context.diagnostics.push({
|
|
118
|
+
code: "unsupported-parameter",
|
|
119
|
+
title: "Unsupported parameter.",
|
|
120
|
+
controllerName: context.controllerName,
|
|
121
|
+
memberName: context.methodName,
|
|
122
|
+
detail: `@${decoratorName}() arguments must be statically analyzable string literals.`,
|
|
123
|
+
found: `@${decoratorName}(${argument.argumentText})`,
|
|
124
|
+
filePath: context.filePath,
|
|
125
|
+
line: location.line,
|
|
126
|
+
column: location.column
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const base = {
|
|
131
|
+
index,
|
|
132
|
+
parameterName: parameter.name.text
|
|
133
|
+
};
|
|
134
|
+
if (decoratorName === "Param") {
|
|
135
|
+
if (argument.value === void 0) {
|
|
136
|
+
context.diagnostics.push({
|
|
137
|
+
code: "unsupported-parameter",
|
|
138
|
+
title: "Unsupported parameter.",
|
|
139
|
+
controllerName: context.controllerName,
|
|
140
|
+
memberName: context.methodName,
|
|
141
|
+
detail: "A whole @Param() is not supported; provide a route parameter name such as @Param('id').",
|
|
142
|
+
filePath: context.filePath,
|
|
143
|
+
line: location.line,
|
|
144
|
+
column: location.column
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
...base,
|
|
150
|
+
kind: "param",
|
|
151
|
+
name: argument.value
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (decoratorName === "Headers") {
|
|
155
|
+
if (argument.value === void 0) {
|
|
156
|
+
context.diagnostics.push({
|
|
157
|
+
code: "unsupported-parameter",
|
|
158
|
+
title: "Unsupported parameter.",
|
|
159
|
+
controllerName: context.controllerName,
|
|
160
|
+
memberName: context.methodName,
|
|
161
|
+
detail: "A whole @Headers() is not supported; provide a header name such as @Headers('x-example').",
|
|
162
|
+
filePath: context.filePath,
|
|
163
|
+
line: location.line,
|
|
164
|
+
column: location.column
|
|
165
|
+
});
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
...base,
|
|
170
|
+
kind: "header",
|
|
171
|
+
name: argument.value
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (decoratorName === "Query") return {
|
|
175
|
+
...base,
|
|
176
|
+
kind: "query",
|
|
177
|
+
name: argument.value
|
|
178
|
+
};
|
|
179
|
+
return {
|
|
180
|
+
...base,
|
|
181
|
+
kind: "body",
|
|
182
|
+
name: argument.value
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
var PRIMITIVE_SCALAR_TYPES = {
|
|
186
|
+
string: "String",
|
|
187
|
+
number: "Float",
|
|
188
|
+
boolean: "Boolean"
|
|
189
|
+
};
|
|
190
|
+
var unwrapPromise = (typeNode) => {
|
|
191
|
+
if (isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments?.[0] !== void 0) return unwrapPromise(typeNode.typeArguments[0]);
|
|
192
|
+
return typeNode;
|
|
193
|
+
};
|
|
194
|
+
var unwrapNullable = (typeNode) => {
|
|
195
|
+
if (!isUnionTypeNode(typeNode)) return {
|
|
196
|
+
inner: typeNode,
|
|
197
|
+
isNullable: false
|
|
198
|
+
};
|
|
199
|
+
const nonNullMembers = typeNode.types.filter((member) => member.kind !== SyntaxKind.NullKeyword && member.kind !== SyntaxKind.UndefinedKeyword);
|
|
200
|
+
const [onlyMember] = nonNullMembers;
|
|
201
|
+
if (nonNullMembers.length !== 1 || onlyMember === void 0) return {
|
|
202
|
+
inner: typeNode,
|
|
203
|
+
isNullable: nonNullMembers.length !== typeNode.types.length
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
inner: onlyMember,
|
|
207
|
+
isNullable: true
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
var unwrapArrayElement = (typeNode) => {
|
|
211
|
+
if (isArrayTypeNode(typeNode)) return typeNode.elementType;
|
|
212
|
+
if (isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Array" && typeNode.typeArguments?.[0] !== void 0) return typeNode.typeArguments[0];
|
|
213
|
+
};
|
|
214
|
+
var identifierTextOf = (typeNode) => {
|
|
215
|
+
if (isTypeReferenceNode(typeNode) && isIdentifier(typeNode.typeName)) return typeNode.typeName.text;
|
|
216
|
+
switch (typeNode.kind) {
|
|
217
|
+
case SyntaxKind.StringKeyword: return "string";
|
|
218
|
+
case SyntaxKind.NumberKeyword: return "number";
|
|
219
|
+
case SyntaxKind.BooleanKeyword: return "boolean";
|
|
220
|
+
default: return;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
var unwrapTypeNode = (typeNode) => {
|
|
224
|
+
const { inner: withoutNullable, isNullable } = unwrapNullable(unwrapPromise(typeNode));
|
|
225
|
+
const arrayElement = unwrapArrayElement(withoutNullable);
|
|
226
|
+
const identifierText = identifierTextOf(arrayElement === void 0 ? withoutNullable : unwrapNullable(arrayElement).inner);
|
|
227
|
+
if (identifierText === void 0) return;
|
|
228
|
+
return {
|
|
229
|
+
identifierText,
|
|
230
|
+
isArray: arrayElement !== void 0,
|
|
231
|
+
isNullable
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
var graphqlScalarNameFor = (identifierText) => PRIMITIVE_SCALAR_TYPES[identifierText] ?? identifierText;
|
|
235
|
+
var graphqlTypeStringFor = (unwrapped, explicitTypeName) => {
|
|
236
|
+
const baseName = explicitTypeName ?? graphqlScalarNameFor(unwrapped.identifierText);
|
|
237
|
+
const wrapped = unwrapped.isArray ? `[${baseName}!]` : baseName;
|
|
238
|
+
return unwrapped.isNullable ? wrapped : `${wrapped}!`;
|
|
239
|
+
};
|
|
240
|
+
var OBSERVABLE_TYPE_NAME = "Observable";
|
|
241
|
+
var STREAMABLE_FILE_TYPE_NAME = "StreamableFile";
|
|
242
|
+
var readResponseKind = (returnType) => {
|
|
243
|
+
if (returnType === void 0) return "json";
|
|
244
|
+
const unwrapped = unwrapPromise(returnType);
|
|
245
|
+
if (!isTypeReferenceNode(unwrapped) || !isIdentifier(unwrapped.typeName)) return "json";
|
|
246
|
+
if (unwrapped.typeName.text === OBSERVABLE_TYPE_NAME) return "observable";
|
|
247
|
+
if (unwrapped.typeName.text === STREAMABLE_FILE_TYPE_NAME) return "stream";
|
|
248
|
+
return "json";
|
|
249
|
+
};
|
|
250
|
+
var trimSlashes = (segment) => segment.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
251
|
+
var joinRoutePath = (controllerPath, methodPath) => {
|
|
252
|
+
return `/${[trimSlashes(controllerPath), trimSlashes(methodPath ?? "")].filter((segment) => segment.length > 0).join("/")}`;
|
|
253
|
+
};
|
|
254
|
+
var extractMethodDefinition = (method, context) => {
|
|
255
|
+
const decorator = findAnyDecorator(method, Object.keys(HTTP_METHOD_DECORATOR_NAMES));
|
|
256
|
+
if (decorator === void 0) return;
|
|
257
|
+
const methodName = method.name.getText();
|
|
258
|
+
const decoratorName = decoratorNameOf(decorator);
|
|
259
|
+
const location = locationOf(context.sourceFile, method);
|
|
260
|
+
if (decoratorName === void 0) return;
|
|
261
|
+
const httpMethod = HTTP_METHOD_DECORATOR_NAMES[decoratorName];
|
|
262
|
+
if (httpMethod === void 0) return;
|
|
263
|
+
const routeArgument = readStaticStringArgument(decorator);
|
|
264
|
+
if (!routeArgument.ok) {
|
|
265
|
+
context.diagnostics.push({
|
|
266
|
+
code: "unsupported-route",
|
|
267
|
+
title: "Unsupported route.",
|
|
268
|
+
controllerName: context.controllerName,
|
|
269
|
+
memberName: methodName,
|
|
270
|
+
detail: "Route paths must be statically analyzable.",
|
|
271
|
+
found: `@${decoratorName}(${routeArgument.argumentText})`,
|
|
272
|
+
filePath: context.filePath,
|
|
273
|
+
line: location.line,
|
|
274
|
+
column: location.column
|
|
275
|
+
});
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const parameters = method.parameters.map((parameter, index) => extractParameterDefinition(parameter, index, {
|
|
279
|
+
sourceFile: context.sourceFile,
|
|
280
|
+
filePath: context.filePath,
|
|
281
|
+
controllerName: context.controllerName,
|
|
282
|
+
methodName,
|
|
283
|
+
diagnostics: context.diagnostics
|
|
284
|
+
}));
|
|
285
|
+
if (parameters.some((parameter) => parameter === void 0)) return;
|
|
286
|
+
return {
|
|
287
|
+
name: methodName,
|
|
288
|
+
httpMethod,
|
|
289
|
+
path: joinRoutePath(context.controllerPath, routeArgument.value),
|
|
290
|
+
parameters: parameters.filter((parameter) => parameter !== void 0),
|
|
291
|
+
responseKind: readResponseKind(method.type),
|
|
292
|
+
line: location.line,
|
|
293
|
+
column: location.column
|
|
294
|
+
};
|
|
295
|
+
};
|
|
296
|
+
var extractControllerDefinition = (classDeclaration, sourceFile, filePath, diagnostics) => {
|
|
297
|
+
const controllerName = classDeclaration.name?.text ?? "(anonymous controller)";
|
|
298
|
+
const decorator = findDecorator(classDeclaration, "Controller");
|
|
299
|
+
if (decorator === void 0) return;
|
|
300
|
+
const location = locationOf(sourceFile, classDeclaration);
|
|
301
|
+
const argument = readStaticStringArgument(decorator);
|
|
302
|
+
if (!argument.ok) {
|
|
303
|
+
diagnostics.push({
|
|
304
|
+
code: "unsupported-route",
|
|
305
|
+
title: "Unsupported route.",
|
|
306
|
+
controllerName,
|
|
307
|
+
memberName: "",
|
|
308
|
+
detail: "Controller paths must be statically analyzable.",
|
|
309
|
+
found: `@Controller(${argument.argumentText})`,
|
|
310
|
+
filePath,
|
|
311
|
+
line: location.line,
|
|
312
|
+
column: location.column
|
|
313
|
+
});
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
const methods = classDeclaration.members.filter((member) => isMethodDeclaration(member)).map((method) => extractMethodDefinition(method, {
|
|
317
|
+
sourceFile,
|
|
318
|
+
filePath,
|
|
319
|
+
controllerName,
|
|
320
|
+
controllerPath: argument.value ?? "",
|
|
321
|
+
diagnostics
|
|
322
|
+
})).filter((method) => method !== void 0);
|
|
323
|
+
return {
|
|
324
|
+
name: controllerName,
|
|
325
|
+
path: argument.value ?? "",
|
|
326
|
+
sourceFile: filePath,
|
|
327
|
+
methods
|
|
328
|
+
};
|
|
329
|
+
};
|
|
330
|
+
var findControllerClasses = (sourceFile) => {
|
|
331
|
+
return sourceFile.statements.filter((statement) => isClassDeclaration(statement) && findDecorator(statement, "Controller") !== void 0);
|
|
332
|
+
};
|
|
333
|
+
var sharedApi;
|
|
334
|
+
var parseSourceFile = (filePath) => {
|
|
335
|
+
sharedApi ??= new API();
|
|
336
|
+
sharedApi.updateSnapshot({ closeFiles: [filePath] });
|
|
337
|
+
const sourceFile = sharedApi.updateSnapshot({
|
|
338
|
+
openFiles: [filePath],
|
|
339
|
+
fileChanges: { changed: [filePath] }
|
|
340
|
+
}).getDefaultProjectForFile(filePath)?.program.getSourceFile(filePath);
|
|
341
|
+
if (sourceFile === void 0) throw new Error(`Unable to parse source file: ${filePath}`);
|
|
342
|
+
return sourceFile;
|
|
343
|
+
};
|
|
344
|
+
var analyzeControllers = (filePaths) => {
|
|
345
|
+
const diagnostics = [];
|
|
346
|
+
const controllers = [];
|
|
347
|
+
for (const filePath of filePaths) {
|
|
348
|
+
const sourceFile = parseSourceFile(filePath);
|
|
349
|
+
for (const classDeclaration of findControllerClasses(sourceFile)) {
|
|
350
|
+
const controller = extractControllerDefinition(classDeclaration, sourceFile, filePath, diagnostics);
|
|
351
|
+
if (controller !== void 0) controllers.push(controller);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
controllers,
|
|
356
|
+
diagnostics
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
var GRAPHQL_OPERATION_DECORATOR_NAMES = {
|
|
360
|
+
Query: "query",
|
|
361
|
+
Mutation: "mutation"
|
|
362
|
+
};
|
|
363
|
+
var readTypeThunkExpression = (thunk) => {
|
|
364
|
+
if (!isBlock(thunk.body)) return thunk.body;
|
|
365
|
+
return thunk.body.statements.find(isReturnStatement)?.expression;
|
|
366
|
+
};
|
|
367
|
+
var readArgsTypeOption = (decorator) => {
|
|
368
|
+
if (!isCallExpression(decorator.expression)) return;
|
|
369
|
+
const optionsArgument = decorator.expression.arguments.find(isObjectLiteralExpression);
|
|
370
|
+
if (optionsArgument === void 0) return;
|
|
371
|
+
const typeProperty = optionsArgument.properties.find((property) => isPropertyAssignment(property) && isIdentifier(property.name) && property.name.text === "type");
|
|
372
|
+
if (typeProperty === void 0) return;
|
|
373
|
+
const thunk = typeProperty.initializer;
|
|
374
|
+
if (!isArrowFunction(thunk) && !isFunctionExpression(thunk)) return;
|
|
375
|
+
const returnedExpression = readTypeThunkExpression(thunk);
|
|
376
|
+
if (returnedExpression === void 0) return;
|
|
377
|
+
if (isArrayLiteralExpression(returnedExpression)) {
|
|
378
|
+
const element = returnedExpression.elements[0];
|
|
379
|
+
return element !== void 0 && isIdentifier(element) ? {
|
|
380
|
+
typeName: element.text,
|
|
381
|
+
isArray: true
|
|
382
|
+
} : void 0;
|
|
383
|
+
}
|
|
384
|
+
return isIdentifier(returnedExpression) ? {
|
|
385
|
+
typeName: returnedExpression.text,
|
|
386
|
+
isArray: false
|
|
387
|
+
} : void 0;
|
|
388
|
+
};
|
|
389
|
+
var extractResolverArgumentDefinition = (parameter, index, context) => {
|
|
390
|
+
const decorator = findAnyDecorator(parameter, ["Args"]);
|
|
391
|
+
const location = locationOf(context.sourceFile, parameter);
|
|
392
|
+
if (decorator === void 0) {
|
|
393
|
+
context.diagnostics.push({
|
|
394
|
+
code: "unsupported-argument",
|
|
395
|
+
title: "Unsupported argument.",
|
|
396
|
+
controllerName: context.resolverName,
|
|
397
|
+
memberName: context.methodName,
|
|
398
|
+
detail: `Parameter "${parameter.name.getText()}" must be decorated with @Args to be transported through a NestBridge GraphQL client.`,
|
|
399
|
+
filePath: context.filePath,
|
|
400
|
+
line: location.line,
|
|
401
|
+
column: location.column
|
|
402
|
+
});
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
if (!isIdentifier(parameter.name)) {
|
|
406
|
+
context.diagnostics.push({
|
|
407
|
+
code: "unsupported-argument",
|
|
408
|
+
title: "Unsupported argument.",
|
|
409
|
+
controllerName: context.resolverName,
|
|
410
|
+
memberName: context.methodName,
|
|
411
|
+
detail: "Destructured parameters cannot be transported through a NestBridge GraphQL client.",
|
|
412
|
+
filePath: context.filePath,
|
|
413
|
+
line: location.line,
|
|
414
|
+
column: location.column
|
|
415
|
+
});
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
const nameArgument = readStaticStringArgument(decorator);
|
|
419
|
+
if (!nameArgument.ok) {
|
|
420
|
+
context.diagnostics.push({
|
|
421
|
+
code: "unsupported-argument",
|
|
422
|
+
title: "Unsupported argument.",
|
|
423
|
+
controllerName: context.resolverName,
|
|
424
|
+
memberName: context.methodName,
|
|
425
|
+
detail: "@Args() name must be a statically analyzable string literal.",
|
|
426
|
+
found: `@Args(${nameArgument.argumentText})`,
|
|
427
|
+
filePath: context.filePath,
|
|
428
|
+
line: location.line,
|
|
429
|
+
column: location.column
|
|
430
|
+
});
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
if (nameArgument.value === void 0) {
|
|
434
|
+
context.diagnostics.push({
|
|
435
|
+
code: "unsupported-argument",
|
|
436
|
+
title: "Unsupported argument.",
|
|
437
|
+
controllerName: context.resolverName,
|
|
438
|
+
memberName: context.methodName,
|
|
439
|
+
detail: "A whole @Args() is not supported; provide an argument name such as @Args('id').",
|
|
440
|
+
filePath: context.filePath,
|
|
441
|
+
line: location.line,
|
|
442
|
+
column: location.column
|
|
443
|
+
});
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
if (parameter.type === void 0) {
|
|
447
|
+
context.diagnostics.push({
|
|
448
|
+
code: "unsupported-argument",
|
|
449
|
+
title: "Unsupported argument.",
|
|
450
|
+
controllerName: context.resolverName,
|
|
451
|
+
memberName: context.methodName,
|
|
452
|
+
detail: `@Args() parameter "${parameter.name.text}" needs an explicit type annotation.`,
|
|
453
|
+
filePath: context.filePath,
|
|
454
|
+
line: location.line,
|
|
455
|
+
column: location.column
|
|
456
|
+
});
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
const unwrapped = unwrapTypeNode(parameter.type);
|
|
460
|
+
if (unwrapped === void 0) {
|
|
461
|
+
context.diagnostics.push({
|
|
462
|
+
code: "unsupported-argument",
|
|
463
|
+
title: "Unsupported argument.",
|
|
464
|
+
controllerName: context.resolverName,
|
|
465
|
+
memberName: context.methodName,
|
|
466
|
+
detail: `@Args() parameter "${parameter.name.text}" has a type NestBridge cannot statically resolve to a GraphQL type.`,
|
|
467
|
+
found: parameter.type.getText(),
|
|
468
|
+
filePath: context.filePath,
|
|
469
|
+
line: location.line,
|
|
470
|
+
column: location.column
|
|
471
|
+
});
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const explicitType = readArgsTypeOption(decorator);
|
|
475
|
+
return {
|
|
476
|
+
index,
|
|
477
|
+
parameterName: parameter.name.text,
|
|
478
|
+
name: nameArgument.value,
|
|
479
|
+
graphqlType: graphqlTypeStringFor(unwrapped, explicitType?.typeName)
|
|
480
|
+
};
|
|
481
|
+
};
|
|
482
|
+
var parsedFilesByPath = /* @__PURE__ */ new Map();
|
|
483
|
+
var parseFile = (filePath) => {
|
|
484
|
+
const cached = parsedFilesByPath.get(filePath);
|
|
485
|
+
if (cached !== void 0) return cached;
|
|
486
|
+
const sourceFile = parseSourceFile(filePath);
|
|
487
|
+
parsedFilesByPath.set(filePath, sourceFile);
|
|
488
|
+
return sourceFile;
|
|
489
|
+
};
|
|
490
|
+
var resolveModuleFile = (fromFilePath, moduleSpecifierText) => {
|
|
491
|
+
if (!moduleSpecifierText.startsWith(".")) return;
|
|
492
|
+
const base = resolve(dirname(fromFilePath), moduleSpecifierText);
|
|
493
|
+
return [
|
|
494
|
+
`${base}.ts`,
|
|
495
|
+
`${base}.tsx`,
|
|
496
|
+
resolve(base, "index.ts")
|
|
497
|
+
].find((candidate) => existsSync(candidate));
|
|
498
|
+
};
|
|
499
|
+
var findExportedDeclaration = (sourceFile, name) => sourceFile.statements.find((statement) => isClassDeclaration(statement) && statement.name?.text === name);
|
|
500
|
+
var findImportSource = (sourceFile, localName) => {
|
|
501
|
+
for (const statement of sourceFile.statements) {
|
|
502
|
+
if (!isImportDeclaration(statement) || !isStringLiteralLikeNode(statement.moduleSpecifier)) continue;
|
|
503
|
+
const namedBindings = statement.importClause?.namedBindings;
|
|
504
|
+
if (namedBindings === void 0 || !isNamedImports(namedBindings)) continue;
|
|
505
|
+
const element = namedBindings.elements.find((candidate) => candidate.name.text === localName);
|
|
506
|
+
if (element !== void 0) return {
|
|
507
|
+
moduleSpecifierText: statement.moduleSpecifier.text,
|
|
508
|
+
importedName: element.propertyName?.text ?? element.name.text
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
var findReExportSource = (sourceFile, name) => {
|
|
513
|
+
for (const statement of sourceFile.statements) {
|
|
514
|
+
if (!isExportDeclaration(statement) || statement.moduleSpecifier === void 0 || !isStringLiteralLikeNode(statement.moduleSpecifier)) continue;
|
|
515
|
+
if (statement.exportClause === void 0) return statement.moduleSpecifier.text;
|
|
516
|
+
if (isNamedExports(statement.exportClause) && statement.exportClause.elements.some((element) => (element.propertyName?.text ?? element.name.text) === name)) return statement.moduleSpecifier.text;
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
var resolveClassDeclaration = (name, fromSourceFile, fromFilePath, visited = /* @__PURE__ */ new Set()) => {
|
|
520
|
+
if (visited.has(fromFilePath)) return;
|
|
521
|
+
visited.add(fromFilePath);
|
|
522
|
+
const direct = findExportedDeclaration(fromSourceFile, name);
|
|
523
|
+
if (direct !== void 0) return {
|
|
524
|
+
classDeclaration: direct,
|
|
525
|
+
sourceFile: fromSourceFile,
|
|
526
|
+
filePath: fromFilePath
|
|
527
|
+
};
|
|
528
|
+
const importSource = findImportSource(fromSourceFile, name);
|
|
529
|
+
if (importSource !== void 0) {
|
|
530
|
+
const resolvedPath = resolveModuleFile(fromFilePath, importSource.moduleSpecifierText);
|
|
531
|
+
if (resolvedPath !== void 0) {
|
|
532
|
+
const nextSourceFile = parseFile(resolvedPath);
|
|
533
|
+
const result = resolveClassDeclaration(importSource.importedName, nextSourceFile, resolvedPath, visited);
|
|
534
|
+
if (result !== void 0) return result;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const reExportModuleSpecifierText = findReExportSource(fromSourceFile, name);
|
|
538
|
+
if (reExportModuleSpecifierText !== void 0) {
|
|
539
|
+
const resolvedPath = resolveModuleFile(fromFilePath, reExportModuleSpecifierText);
|
|
540
|
+
if (resolvedPath !== void 0) return resolveClassDeclaration(name, parseFile(resolvedPath), resolvedPath, visited);
|
|
541
|
+
}
|
|
542
|
+
};
|
|
543
|
+
var PRIMITIVE_SCALAR_NAMES$1 = /* @__PURE__ */ new Set([
|
|
544
|
+
"string",
|
|
545
|
+
"number",
|
|
546
|
+
"boolean"
|
|
547
|
+
]);
|
|
548
|
+
var extractSelectionFields = (classDeclaration, sourceFile, filePath, context) => {
|
|
549
|
+
const typeName = classDeclaration.name?.text ?? "(anonymous type)";
|
|
550
|
+
const location = locationOf(sourceFile, classDeclaration);
|
|
551
|
+
if (context.visitedTypeNames.has(typeName)) {
|
|
552
|
+
context.diagnostics.push({
|
|
553
|
+
code: "circular-type",
|
|
554
|
+
title: "Circular type.",
|
|
555
|
+
controllerName: context.resolverName,
|
|
556
|
+
memberName: context.methodName,
|
|
557
|
+
detail: `Type "${typeName}" references itself, directly or indirectly. NestBridge cannot build a finite GraphQL selection set for it.`,
|
|
558
|
+
filePath,
|
|
559
|
+
line: location.line,
|
|
560
|
+
column: location.column
|
|
561
|
+
});
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const nextVisited = new Set(context.visitedTypeNames).add(typeName);
|
|
565
|
+
const properties = classDeclaration.members.filter((member) => isPropertyDeclaration(member) && findDecorator(member, "Field") !== void 0);
|
|
566
|
+
const fields = [];
|
|
567
|
+
for (const property of properties) {
|
|
568
|
+
if (!isIdentifier(property.name)) continue;
|
|
569
|
+
const fieldName = property.name.text;
|
|
570
|
+
const fieldLocation = locationOf(sourceFile, property);
|
|
571
|
+
if (property.type === void 0) {
|
|
572
|
+
context.diagnostics.push({
|
|
573
|
+
code: "unsupported-return-type",
|
|
574
|
+
title: "Unsupported field type.",
|
|
575
|
+
controllerName: context.resolverName,
|
|
576
|
+
memberName: context.methodName,
|
|
577
|
+
detail: `Field "${fieldName}" on "${typeName}" needs an explicit type annotation.`,
|
|
578
|
+
filePath,
|
|
579
|
+
line: fieldLocation.line,
|
|
580
|
+
column: fieldLocation.column
|
|
581
|
+
});
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
const unwrapped = unwrapTypeNode(property.type);
|
|
585
|
+
if (unwrapped === void 0) {
|
|
586
|
+
context.diagnostics.push({
|
|
587
|
+
code: "unsupported-return-type",
|
|
588
|
+
title: "Unsupported field type.",
|
|
589
|
+
controllerName: context.resolverName,
|
|
590
|
+
memberName: context.methodName,
|
|
591
|
+
detail: `Field "${fieldName}" on "${typeName}" has a type NestBridge cannot statically resolve.`,
|
|
592
|
+
found: property.type.getText(),
|
|
593
|
+
filePath,
|
|
594
|
+
line: fieldLocation.line,
|
|
595
|
+
column: fieldLocation.column
|
|
596
|
+
});
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (PRIMITIVE_SCALAR_NAMES$1.has(unwrapped.identifierText)) {
|
|
600
|
+
fields.push({ name: fieldName });
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
const resolved = resolveClassDeclaration(unwrapped.identifierText, sourceFile, filePath);
|
|
604
|
+
if (resolved === void 0) {
|
|
605
|
+
fields.push({ name: fieldName });
|
|
606
|
+
continue;
|
|
607
|
+
}
|
|
608
|
+
if (findDecorator(resolved.classDeclaration, "ObjectType") === void 0) {
|
|
609
|
+
context.diagnostics.push({
|
|
610
|
+
code: "unsupported-return-type",
|
|
611
|
+
title: "Unsupported field type.",
|
|
612
|
+
controllerName: context.resolverName,
|
|
613
|
+
memberName: context.methodName,
|
|
614
|
+
detail: `Field "${fieldName}" on "${typeName}" resolves to class "${unwrapped.identifierText}", which is not decorated with @ObjectType(). NestBridge cannot build a GraphQL selection set for it.`,
|
|
615
|
+
filePath,
|
|
616
|
+
line: fieldLocation.line,
|
|
617
|
+
column: fieldLocation.column
|
|
618
|
+
});
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
const children = extractSelectionFields(resolved.classDeclaration, resolved.sourceFile, resolved.filePath, {
|
|
622
|
+
...context,
|
|
623
|
+
visitedTypeNames: nextVisited
|
|
624
|
+
});
|
|
625
|
+
if (children === void 0) return;
|
|
626
|
+
fields.push({
|
|
627
|
+
name: fieldName,
|
|
628
|
+
children
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return fields;
|
|
632
|
+
};
|
|
633
|
+
var readNameFromOptions = (optionsArgument) => {
|
|
634
|
+
const nameProperty = optionsArgument.properties.find((property) => isPropertyAssignment(property) && isIdentifier(property.name) && property.name.text === "name");
|
|
635
|
+
if (nameProperty === void 0 || !isStringLiteralLikeNode(nameProperty.initializer)) return;
|
|
636
|
+
return nameProperty.initializer.text;
|
|
637
|
+
};
|
|
638
|
+
var readGraphqlOperationName = (decorator) => {
|
|
639
|
+
const expression = decorator.expression;
|
|
640
|
+
if (!isCallExpression(expression)) return {
|
|
641
|
+
ok: true,
|
|
642
|
+
value: void 0
|
|
643
|
+
};
|
|
644
|
+
let value;
|
|
645
|
+
for (const argument of expression.arguments) {
|
|
646
|
+
if (isArrowFunction(argument) || isFunctionExpression(argument)) continue;
|
|
647
|
+
if (isStringLiteralLikeNode(argument)) {
|
|
648
|
+
value = argument.text;
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (isObjectLiteralExpression(argument)) {
|
|
652
|
+
value = readNameFromOptions(argument) ?? value;
|
|
653
|
+
continue;
|
|
654
|
+
}
|
|
655
|
+
return {
|
|
656
|
+
ok: false,
|
|
657
|
+
argumentText: argument.getText()
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
return {
|
|
661
|
+
ok: true,
|
|
662
|
+
value
|
|
663
|
+
};
|
|
664
|
+
};
|
|
665
|
+
var readGraphqlReturnTypeOption = (decorator) => {
|
|
666
|
+
if (!isCallExpression(decorator.expression)) return;
|
|
667
|
+
const thunk = decorator.expression.arguments.find((argument) => isArrowFunction(argument) || isFunctionExpression(argument));
|
|
668
|
+
if (thunk === void 0) return;
|
|
669
|
+
const returnedExpression = readTypeThunkExpression(thunk);
|
|
670
|
+
if (returnedExpression === void 0) return;
|
|
671
|
+
if (isArrayLiteralExpression(returnedExpression)) {
|
|
672
|
+
const element = returnedExpression.elements[0];
|
|
673
|
+
return element !== void 0 && isIdentifier(element) ? element.text : void 0;
|
|
674
|
+
}
|
|
675
|
+
return isIdentifier(returnedExpression) ? returnedExpression.text : void 0;
|
|
676
|
+
};
|
|
677
|
+
var PRIMITIVE_SCALAR_NAMES = /* @__PURE__ */ new Set([
|
|
678
|
+
"string",
|
|
679
|
+
"number",
|
|
680
|
+
"boolean"
|
|
681
|
+
]);
|
|
682
|
+
var extractResolverMethodDefinition = (method, context) => {
|
|
683
|
+
const decorator = findAnyDecorator(method, Object.keys(GRAPHQL_OPERATION_DECORATOR_NAMES));
|
|
684
|
+
if (decorator === void 0) return;
|
|
685
|
+
const methodName = method.name.getText();
|
|
686
|
+
const decoratorName = decoratorNameOf(decorator);
|
|
687
|
+
const location = locationOf(context.sourceFile, method);
|
|
688
|
+
if (decoratorName === void 0) return;
|
|
689
|
+
const operationKind = GRAPHQL_OPERATION_DECORATOR_NAMES[decoratorName];
|
|
690
|
+
if (operationKind === void 0) return;
|
|
691
|
+
const nameArgument = readGraphqlOperationName(decorator);
|
|
692
|
+
if (!nameArgument.ok) {
|
|
693
|
+
context.diagnostics.push({
|
|
694
|
+
code: "unsupported-route",
|
|
695
|
+
title: "Unsupported operation name.",
|
|
696
|
+
controllerName: context.resolverName,
|
|
697
|
+
memberName: methodName,
|
|
698
|
+
detail: "GraphQL operation names must be statically analyzable string literals.",
|
|
699
|
+
found: `@${decoratorName}(${nameArgument.argumentText})`,
|
|
700
|
+
filePath: context.filePath,
|
|
701
|
+
line: location.line,
|
|
702
|
+
column: location.column
|
|
703
|
+
});
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
const decoratorReturnType = readGraphqlReturnTypeOption(decorator);
|
|
707
|
+
const identifierText = decoratorReturnType ?? (method.type === void 0 ? void 0 : unwrapTypeNode(method.type)?.identifierText);
|
|
708
|
+
if (identifierText === void 0) {
|
|
709
|
+
context.diagnostics.push({
|
|
710
|
+
code: "unsupported-return-type",
|
|
711
|
+
title: "Unsupported return type.",
|
|
712
|
+
controllerName: context.resolverName,
|
|
713
|
+
memberName: methodName,
|
|
714
|
+
detail: "GraphQL resolver methods need a type thunk on @Query()/@Mutation() (e.g. \"() => UserType\") or an explicit return type annotation so NestBridge can build a selection set.",
|
|
715
|
+
filePath: context.filePath,
|
|
716
|
+
line: location.line,
|
|
717
|
+
column: location.column
|
|
718
|
+
});
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
let selection = [];
|
|
722
|
+
if (!PRIMITIVE_SCALAR_NAMES.has(identifierText.toLowerCase())) {
|
|
723
|
+
const resolved = resolveClassDeclaration(identifierText, context.sourceFile, context.filePath);
|
|
724
|
+
if (resolved === void 0 && decoratorReturnType === void 0) {
|
|
725
|
+
context.diagnostics.push({
|
|
726
|
+
code: "unsupported-return-type",
|
|
727
|
+
title: "Unsupported return type.",
|
|
728
|
+
controllerName: context.resolverName,
|
|
729
|
+
memberName: methodName,
|
|
730
|
+
detail: `NestBridge could not locate the declaration of type "${identifierText}".`,
|
|
731
|
+
filePath: context.filePath,
|
|
732
|
+
line: location.line,
|
|
733
|
+
column: location.column
|
|
734
|
+
});
|
|
735
|
+
return;
|
|
736
|
+
}
|
|
737
|
+
if (resolved !== void 0) {
|
|
738
|
+
const fields = extractSelectionFields(resolved.classDeclaration, resolved.sourceFile, resolved.filePath, {
|
|
739
|
+
resolverName: context.resolverName,
|
|
740
|
+
methodName,
|
|
741
|
+
diagnostics: context.diagnostics,
|
|
742
|
+
visitedTypeNames: /* @__PURE__ */ new Set()
|
|
743
|
+
});
|
|
744
|
+
if (fields === void 0) return;
|
|
745
|
+
selection = fields;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
const args = method.parameters.map((parameter, index) => extractResolverArgumentDefinition(parameter, index, {
|
|
749
|
+
sourceFile: context.sourceFile,
|
|
750
|
+
filePath: context.filePath,
|
|
751
|
+
resolverName: context.resolverName,
|
|
752
|
+
methodName,
|
|
753
|
+
diagnostics: context.diagnostics
|
|
754
|
+
}));
|
|
755
|
+
if (args.some((argument) => argument === void 0)) return;
|
|
756
|
+
return {
|
|
757
|
+
name: methodName,
|
|
758
|
+
operationKind,
|
|
759
|
+
operationName: nameArgument.value ?? methodName,
|
|
760
|
+
arguments: args.filter((argument) => argument !== void 0),
|
|
761
|
+
selection,
|
|
762
|
+
line: location.line,
|
|
763
|
+
column: location.column
|
|
764
|
+
};
|
|
765
|
+
};
|
|
766
|
+
var extractResolverDefinition = (classDeclaration, sourceFile, filePath, diagnostics) => {
|
|
767
|
+
const resolverName = classDeclaration.name?.text ?? "(anonymous resolver)";
|
|
768
|
+
return {
|
|
769
|
+
name: resolverName,
|
|
770
|
+
sourceFile: filePath,
|
|
771
|
+
methods: classDeclaration.members.filter((member) => isMethodDeclaration(member)).map((method) => extractResolverMethodDefinition(method, {
|
|
772
|
+
sourceFile,
|
|
773
|
+
filePath,
|
|
774
|
+
resolverName,
|
|
775
|
+
diagnostics
|
|
776
|
+
})).filter((method) => method !== void 0)
|
|
777
|
+
};
|
|
778
|
+
};
|
|
779
|
+
var findResolverClasses = (sourceFile) => {
|
|
780
|
+
return sourceFile.statements.filter((statement) => isClassDeclaration(statement) && findDecorator(statement, "Resolver") !== void 0);
|
|
781
|
+
};
|
|
782
|
+
var analyzeResolvers = (filePaths) => {
|
|
783
|
+
const diagnostics = [];
|
|
784
|
+
const resolvers = [];
|
|
785
|
+
for (const filePath of filePaths) {
|
|
786
|
+
const sourceFile = parseSourceFile(filePath);
|
|
787
|
+
for (const classDeclaration of findResolverClasses(sourceFile)) {
|
|
788
|
+
const resolver = extractResolverDefinition(classDeclaration, sourceFile, filePath, diagnostics);
|
|
789
|
+
if (resolver !== void 0) resolvers.push(resolver);
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
resolvers,
|
|
794
|
+
diagnostics
|
|
795
|
+
};
|
|
796
|
+
};
|
|
797
|
+
var MODULE_DECORATOR_NAME = "Module";
|
|
798
|
+
var PROVIDERS_PROPERTY_NAME = "providers";
|
|
799
|
+
var PROVIDE_PROPERTY_NAME = "provide";
|
|
800
|
+
var USE_CLASS_PROPERTY_NAME = "useClass";
|
|
801
|
+
var APP_FILTER_LOCAL_NAME = "APP_FILTER";
|
|
802
|
+
var NEST_CORE_MODULE_SPECIFIER$1 = "@nestjs/core";
|
|
803
|
+
var findAppFilterProviderClassName = (moduleClassDeclaration, moduleSourceFile) => {
|
|
804
|
+
const moduleDecorator = findDecorator(moduleClassDeclaration, MODULE_DECORATOR_NAME);
|
|
805
|
+
if (moduleDecorator === void 0 || !isCallExpression(moduleDecorator.expression)) return;
|
|
806
|
+
const [moduleOptions] = moduleDecorator.expression.arguments;
|
|
807
|
+
if (moduleOptions === void 0 || !isObjectLiteralExpression(moduleOptions)) return;
|
|
808
|
+
const providersProperty = moduleOptions.properties.find((property) => isPropertyAssignment(property) && isIdentifier(property.name) && property.name.text === PROVIDERS_PROPERTY_NAME);
|
|
809
|
+
if (providersProperty === void 0 || !isArrayLiteralExpression(providersProperty.initializer)) return;
|
|
810
|
+
const appFilterImportSource = findImportSource(moduleSourceFile, APP_FILTER_LOCAL_NAME);
|
|
811
|
+
if (appFilterImportSource === void 0 || appFilterImportSource.moduleSpecifierText !== NEST_CORE_MODULE_SPECIFIER$1) return;
|
|
812
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
813
|
+
for (const element of providersProperty.initializer.elements) {
|
|
814
|
+
if (isSpreadElement(element)) return;
|
|
815
|
+
if (!isObjectLiteralExpression(element)) continue;
|
|
816
|
+
const provideProperty = element.properties.find((property) => isPropertyAssignment(property) && isIdentifier(property.name) && property.name.text === PROVIDE_PROPERTY_NAME);
|
|
817
|
+
if (provideProperty === void 0 || !isIdentifier(provideProperty.initializer) || provideProperty.initializer.text !== APP_FILTER_LOCAL_NAME) continue;
|
|
818
|
+
const useClassProperty = element.properties.find((property) => isPropertyAssignment(property) && isIdentifier(property.name) && property.name.text === USE_CLASS_PROPERTY_NAME);
|
|
819
|
+
if (useClassProperty === void 0 || !isIdentifier(useClassProperty.initializer)) return;
|
|
820
|
+
classNames.add(useClassProperty.initializer.text);
|
|
821
|
+
}
|
|
822
|
+
return classNames.size === 1 ? [...classNames][0] : void 0;
|
|
823
|
+
};
|
|
824
|
+
var USE_GLOBAL_FILTERS_METHOD_NAME = "useGlobalFilters";
|
|
825
|
+
var collectUseGlobalFiltersCalls = (node, calls) => {
|
|
826
|
+
if (isCallExpression(node) && isPropertyAccessExpression(node.expression) && node.expression.name.text === USE_GLOBAL_FILTERS_METHOD_NAME) calls.push(node);
|
|
827
|
+
node.forEachChild((child) => {
|
|
828
|
+
collectUseGlobalFiltersCalls(child, calls);
|
|
829
|
+
});
|
|
830
|
+
};
|
|
831
|
+
var findGlobalFilterClassName = (sourceFile) => {
|
|
832
|
+
const calls = [];
|
|
833
|
+
collectUseGlobalFiltersCalls(sourceFile, calls);
|
|
834
|
+
if (calls.length === 0) return;
|
|
835
|
+
const classNames = /* @__PURE__ */ new Set();
|
|
836
|
+
for (const call of calls) for (const argument of call.arguments) {
|
|
837
|
+
if (!isNewExpression(argument) || !isIdentifier(argument.expression)) return;
|
|
838
|
+
classNames.add(argument.expression.text);
|
|
839
|
+
}
|
|
840
|
+
return classNames.size === 1 ? [...classNames][0] : void 0;
|
|
841
|
+
};
|
|
842
|
+
var NEST_FACTORY_LOCAL_NAME = "NestFactory";
|
|
843
|
+
var NEST_FACTORY_CREATE_METHOD_NAME = "create";
|
|
844
|
+
var NEST_CORE_MODULE_SPECIFIER = "@nestjs/core";
|
|
845
|
+
var collectNestFactoryCreateCalls = (node, calls) => {
|
|
846
|
+
if (isCallExpression(node) && isPropertyAccessExpression(node.expression) && isIdentifier(node.expression.expression) && node.expression.expression.text === NEST_FACTORY_LOCAL_NAME && node.expression.name.text === NEST_FACTORY_CREATE_METHOD_NAME) calls.push(node);
|
|
847
|
+
node.forEachChild((child) => {
|
|
848
|
+
collectNestFactoryCreateCalls(child, calls);
|
|
849
|
+
});
|
|
850
|
+
};
|
|
851
|
+
var findRootModuleClassName = (sourceFile) => {
|
|
852
|
+
const nestFactoryImportSource = findImportSource(sourceFile, NEST_FACTORY_LOCAL_NAME);
|
|
853
|
+
if (nestFactoryImportSource === void 0 || nestFactoryImportSource.moduleSpecifierText !== NEST_CORE_MODULE_SPECIFIER) return;
|
|
854
|
+
const calls = [];
|
|
855
|
+
collectNestFactoryCreateCalls(sourceFile, calls);
|
|
856
|
+
if (calls.length !== 1) return;
|
|
857
|
+
const [call] = calls;
|
|
858
|
+
if (call === void 0) return;
|
|
859
|
+
const [moduleArgument] = call.arguments;
|
|
860
|
+
return moduleArgument !== void 0 && isIdentifier(moduleArgument) ? moduleArgument.text : void 0;
|
|
861
|
+
};
|
|
862
|
+
var RESPONSE_BODY_CALL_NAMES = /* @__PURE__ */ new Set(["json", "send"]);
|
|
863
|
+
var matchResponseBodyArgument = (expression) => {
|
|
864
|
+
if (!isCallExpression(expression) || !isPropertyAccessExpression(expression.expression) || !RESPONSE_BODY_CALL_NAMES.has(expression.expression.name.text) || expression.arguments.length !== 1) return;
|
|
865
|
+
const [argument] = expression.arguments;
|
|
866
|
+
return argument;
|
|
867
|
+
};
|
|
868
|
+
var resolveCatchMethodBody = (catchMethod) => {
|
|
869
|
+
const [exceptionParameter] = catchMethod.parameters;
|
|
870
|
+
if (exceptionParameter === void 0 || !isIdentifier(exceptionParameter.name)) return;
|
|
871
|
+
const body = catchMethod.body;
|
|
872
|
+
if (body === void 0) return;
|
|
873
|
+
const localDeclarations = /* @__PURE__ */ new Map();
|
|
874
|
+
let responseBodyArgument;
|
|
875
|
+
let sawResponseCall = false;
|
|
876
|
+
let sawTrailingReturn = false;
|
|
877
|
+
for (const statement of body.statements) {
|
|
878
|
+
if (sawTrailingReturn) return;
|
|
879
|
+
if (!sawResponseCall && isVariableStatement(statement)) {
|
|
880
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
881
|
+
if (!isIdentifier(declaration.name) || declaration.initializer === void 0) return;
|
|
882
|
+
localDeclarations.set(declaration.name.text, declaration.initializer);
|
|
883
|
+
}
|
|
884
|
+
continue;
|
|
885
|
+
}
|
|
886
|
+
if (!sawResponseCall && isExpressionStatement(statement)) {
|
|
887
|
+
const argument = matchResponseBodyArgument(statement.expression);
|
|
888
|
+
if (argument === void 0) return;
|
|
889
|
+
responseBodyArgument = argument;
|
|
890
|
+
sawResponseCall = true;
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
if (sawResponseCall && isReturnStatement(statement) && statement.expression === void 0) {
|
|
894
|
+
sawTrailingReturn = true;
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
if (!sawResponseCall || responseBodyArgument === void 0) return;
|
|
900
|
+
return {
|
|
901
|
+
exceptionParameterName: exceptionParameter.name.text,
|
|
902
|
+
localDeclarations,
|
|
903
|
+
responseBodyArgument
|
|
904
|
+
};
|
|
905
|
+
};
|
|
906
|
+
var EXCEPTION_MESSAGE_MEMBER_NAME = "message";
|
|
907
|
+
var EXCEPTION_STATUS_METHOD_NAME = "getStatus";
|
|
908
|
+
var resolvesToExceptionParameter = (expression, exceptionParameterName, localDeclarations, visited) => {
|
|
909
|
+
if (!isIdentifier(expression)) return false;
|
|
910
|
+
if (expression.text === exceptionParameterName) return true;
|
|
911
|
+
if (visited.has(expression.text)) return false;
|
|
912
|
+
visited.add(expression.text);
|
|
913
|
+
const aliasedExpression = localDeclarations.get(expression.text);
|
|
914
|
+
return aliasedExpression !== void 0 ? resolvesToExceptionParameter(aliasedExpression, exceptionParameterName, localDeclarations, visited) : false;
|
|
915
|
+
};
|
|
916
|
+
var synthesizeErrorValueType = (expression, exceptionParameterName, localDeclarations) => {
|
|
917
|
+
if (isPropertyAccessExpression(expression) && expression.name.text === EXCEPTION_MESSAGE_MEMBER_NAME && resolvesToExceptionParameter(expression.expression, exceptionParameterName, localDeclarations, /* @__PURE__ */ new Set())) return "string";
|
|
918
|
+
if (isCallExpression(expression) && expression.arguments.length === 0 && isPropertyAccessExpression(expression.expression) && expression.expression.name.text === EXCEPTION_STATUS_METHOD_NAME && resolvesToExceptionParameter(expression.expression.expression, exceptionParameterName, localDeclarations, /* @__PURE__ */ new Set())) return "number";
|
|
919
|
+
if (isObjectLiteralExpression(expression)) {
|
|
920
|
+
const memberTypes = [];
|
|
921
|
+
for (const property of expression.properties) {
|
|
922
|
+
if (!isPropertyAssignment(property) || !(isIdentifier(property.name) || isStringLiteral(property.name))) return;
|
|
923
|
+
const valueType = synthesizeErrorValueType(property.initializer, exceptionParameterName, localDeclarations);
|
|
924
|
+
if (valueType === void 0) return;
|
|
925
|
+
memberTypes.push(`${property.name.text}: ${valueType}`);
|
|
926
|
+
}
|
|
927
|
+
return `{ ${memberTypes.join("; ")} }`;
|
|
928
|
+
}
|
|
929
|
+
if (isStringLiteral(expression)) return "string";
|
|
930
|
+
if (isNumericLiteral(expression)) return "number";
|
|
931
|
+
if (isTrueLiteral(expression) || isFalseLiteral(expression)) return "boolean";
|
|
932
|
+
if (isNullLiteral(expression)) return "null";
|
|
933
|
+
if (isIdentifier(expression)) {
|
|
934
|
+
const aliasedExpression = localDeclarations.get(expression.text);
|
|
935
|
+
return aliasedExpression !== void 0 ? synthesizeErrorValueType(aliasedExpression, exceptionParameterName, localDeclarations) : void 0;
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
var synthesizeErrorBodyType = (objectLiteral, exceptionParameterName, localDeclarations) => synthesizeErrorValueType(objectLiteral, exceptionParameterName, localDeclarations);
|
|
939
|
+
var CATCH_DECORATOR_NAME = "Catch";
|
|
940
|
+
var CATCH_METHOD_NAME = "catch";
|
|
941
|
+
var findCatchMethod = (members) => members.find((member) => isMethodDeclaration(member) && isIdentifier(member.name) && member.name.text === CATCH_METHOD_NAME);
|
|
942
|
+
var resolveViaGlobalFilters = (bootstrapSourceFile, bootstrapFilePath) => {
|
|
943
|
+
const filterClassName = findGlobalFilterClassName(bootstrapSourceFile);
|
|
944
|
+
return filterClassName === void 0 ? void 0 : resolveClassDeclaration(filterClassName, bootstrapSourceFile, bootstrapFilePath);
|
|
945
|
+
};
|
|
946
|
+
var resolveViaAppFilterProvider = (bootstrapSourceFile, bootstrapFilePath) => {
|
|
947
|
+
const rootModuleClassName = findRootModuleClassName(bootstrapSourceFile);
|
|
948
|
+
if (rootModuleClassName === void 0) return;
|
|
949
|
+
const resolvedModule = resolveClassDeclaration(rootModuleClassName, bootstrapSourceFile, bootstrapFilePath);
|
|
950
|
+
if (resolvedModule === void 0) return;
|
|
951
|
+
const filterClassName = findAppFilterProviderClassName(resolvedModule.classDeclaration, resolvedModule.sourceFile);
|
|
952
|
+
return filterClassName === void 0 ? void 0 : resolveClassDeclaration(filterClassName, resolvedModule.sourceFile, resolvedModule.filePath);
|
|
953
|
+
};
|
|
954
|
+
var isSameDeclaration = (a, b) => a.filePath === b.filePath && a.classDeclaration.name?.text === b.classDeclaration.name?.text;
|
|
955
|
+
var reconcileResolvedFilter = (viaGlobalFilters, viaAppFilterProvider) => {
|
|
956
|
+
if (viaGlobalFilters !== void 0 && viaAppFilterProvider !== void 0) return isSameDeclaration(viaGlobalFilters, viaAppFilterProvider) ? viaGlobalFilters : void 0;
|
|
957
|
+
return viaGlobalFilters ?? viaAppFilterProvider;
|
|
958
|
+
};
|
|
959
|
+
var detectErrorResponseShape = (bootstrapFilePath) => {
|
|
960
|
+
const bootstrapSourceFile = parseSourceFile(bootstrapFilePath);
|
|
961
|
+
const resolved = reconcileResolvedFilter(resolveViaGlobalFilters(bootstrapSourceFile, bootstrapFilePath), resolveViaAppFilterProvider(bootstrapSourceFile, bootstrapFilePath));
|
|
962
|
+
if (resolved === void 0 || findDecorator(resolved.classDeclaration, CATCH_DECORATOR_NAME) === void 0) return;
|
|
963
|
+
const catchMethod = findCatchMethod(resolved.classDeclaration.members);
|
|
964
|
+
if (catchMethod?.body === void 0) return;
|
|
965
|
+
const bodyResolution = resolveCatchMethodBody(catchMethod);
|
|
966
|
+
if (bodyResolution === void 0 || !isObjectLiteralExpression(bodyResolution.responseBodyArgument)) return;
|
|
967
|
+
const errorBodyTypeSource = synthesizeErrorBodyType(bodyResolution.responseBodyArgument, bodyResolution.exceptionParameterName, bodyResolution.localDeclarations);
|
|
968
|
+
return errorBodyTypeSource === void 0 ? void 0 : { errorBodyTypeSource };
|
|
969
|
+
};
|
|
970
|
+
var USE_GLOBAL_INTERCEPTORS_METHOD_NAME = "useGlobalInterceptors";
|
|
971
|
+
var collectUseGlobalInterceptorsCalls = (node, calls) => {
|
|
972
|
+
if (isCallExpression(node) && isPropertyAccessExpression(node.expression) && node.expression.name.text === USE_GLOBAL_INTERCEPTORS_METHOD_NAME) calls.push(node);
|
|
973
|
+
node.forEachChild((child) => {
|
|
974
|
+
collectUseGlobalInterceptorsCalls(child, calls);
|
|
975
|
+
});
|
|
976
|
+
};
|
|
977
|
+
/**
|
|
978
|
+
* Returned in registration order: `useGlobalInterceptors` concatenates onto a
|
|
979
|
+
* single global list, so a later call (or a later argument within one call)
|
|
980
|
+
* always registers an interceptor that wraps closer to the raw handler result.
|
|
981
|
+
*/
|
|
982
|
+
var findGlobalInterceptorClassNames = (sourceFile) => {
|
|
983
|
+
const calls = [];
|
|
984
|
+
collectUseGlobalInterceptorsCalls(sourceFile, calls);
|
|
985
|
+
if (calls.length === 0) return;
|
|
986
|
+
const classNames = [];
|
|
987
|
+
for (const call of calls) for (const argument of call.arguments) {
|
|
988
|
+
if (!isNewExpression(argument) || !isIdentifier(argument.expression)) return;
|
|
989
|
+
classNames.push(argument.expression.text);
|
|
990
|
+
}
|
|
991
|
+
return classNames.length === 0 ? void 0 : classNames;
|
|
992
|
+
};
|
|
993
|
+
var synthesizeExpressionType = (expression, parameterName, substitution, state) => {
|
|
994
|
+
if (isIdentifier(expression) && expression.text === parameterName) {
|
|
995
|
+
state.substitutionCount += 1;
|
|
996
|
+
return substitution;
|
|
997
|
+
}
|
|
998
|
+
if (isObjectLiteralExpression(expression)) {
|
|
999
|
+
const memberTypes = [];
|
|
1000
|
+
for (const property of expression.properties) {
|
|
1001
|
+
if (!isPropertyAssignment(property) || !(isIdentifier(property.name) || isStringLiteral(property.name))) return;
|
|
1002
|
+
const valueType = synthesizeExpressionType(property.initializer, parameterName, substitution, state);
|
|
1003
|
+
if (valueType === void 0) return;
|
|
1004
|
+
memberTypes.push(`${property.name.text}: ${valueType}`);
|
|
1005
|
+
}
|
|
1006
|
+
return `{ ${memberTypes.join("; ")} }`;
|
|
1007
|
+
}
|
|
1008
|
+
if (isStringLiteral(expression)) return "string";
|
|
1009
|
+
if (isNumericLiteral(expression)) return "number";
|
|
1010
|
+
if (isTrueLiteral(expression) || isFalseLiteral(expression)) return "boolean";
|
|
1011
|
+
if (isNullLiteral(expression)) return "null";
|
|
1012
|
+
};
|
|
1013
|
+
var synthesizeGenericObjectType = (objectLiteral, parameterName, substitution) => {
|
|
1014
|
+
const state = { substitutionCount: 0 };
|
|
1015
|
+
const objectType = synthesizeExpressionType(objectLiteral, parameterName, substitution, state);
|
|
1016
|
+
return objectType !== void 0 && state.substitutionCount === 1 ? objectType : void 0;
|
|
1017
|
+
};
|
|
1018
|
+
var INTERCEPT_METHOD_NAME = "intercept";
|
|
1019
|
+
var PIPE_MEMBER_NAME = "pipe";
|
|
1020
|
+
var MAP_OPERATOR_NAME = "map";
|
|
1021
|
+
var RXJS_MODULE_SPECIFIER = "rxjs";
|
|
1022
|
+
var WRAPPED_RESULT_SUBSTITUTION = "Awaited<Result>";
|
|
1023
|
+
var unwrapParenthesizedExpression = (expression) => {
|
|
1024
|
+
let unwrapped = expression;
|
|
1025
|
+
while (isParenthesizedExpression(unwrapped)) unwrapped = unwrapped.expression;
|
|
1026
|
+
return unwrapped;
|
|
1027
|
+
};
|
|
1028
|
+
var resolveSoleReturnExpression = (body) => {
|
|
1029
|
+
if (!isBlock(body)) return unwrapParenthesizedExpression(body);
|
|
1030
|
+
const localDeclarations = /* @__PURE__ */ new Map();
|
|
1031
|
+
let returnExpression;
|
|
1032
|
+
let sawReturn = false;
|
|
1033
|
+
for (const statement of body.statements) {
|
|
1034
|
+
if (sawReturn) return;
|
|
1035
|
+
if (isVariableStatement(statement)) {
|
|
1036
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1037
|
+
if (!isIdentifier(declaration.name) || declaration.initializer === void 0) return;
|
|
1038
|
+
localDeclarations.set(declaration.name.text, declaration.initializer);
|
|
1039
|
+
}
|
|
1040
|
+
continue;
|
|
1041
|
+
}
|
|
1042
|
+
if (isReturnStatement(statement)) {
|
|
1043
|
+
sawReturn = true;
|
|
1044
|
+
returnExpression = statement.expression;
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
if (returnExpression === void 0) return;
|
|
1050
|
+
return isIdentifier(returnExpression) ? localDeclarations.get(returnExpression.text) : returnExpression;
|
|
1051
|
+
};
|
|
1052
|
+
var matchPipedMapCallback = (expression, interceptorSourceFile) => {
|
|
1053
|
+
if (!isCallExpression(expression) || !isPropertyAccessExpression(expression.expression) || expression.expression.name.text !== PIPE_MEMBER_NAME || expression.arguments.length !== 1) return;
|
|
1054
|
+
const [operator] = expression.arguments;
|
|
1055
|
+
if (operator === void 0 || !isCallExpression(operator) || !isIdentifier(operator.expression) || operator.expression.text !== MAP_OPERATOR_NAME || operator.arguments.length !== 1) return;
|
|
1056
|
+
const mapImportSource = findImportSource(interceptorSourceFile, operator.expression.text);
|
|
1057
|
+
if (mapImportSource === void 0 || mapImportSource.moduleSpecifierText !== RXJS_MODULE_SPECIFIER) return;
|
|
1058
|
+
const [callback] = operator.arguments;
|
|
1059
|
+
return callback !== void 0 && (isArrowFunction(callback) || isFunctionExpression(callback)) ? callback : void 0;
|
|
1060
|
+
};
|
|
1061
|
+
var findInterceptMethod = (members) => members.find((member) => isMethodDeclaration(member) && isIdentifier(member.name) && member.name.text === INTERCEPT_METHOD_NAME);
|
|
1062
|
+
var synthesizeInterceptorWrapperType = (interceptorClassName, bootstrapSourceFile, bootstrapFilePath, innerTypeSource) => {
|
|
1063
|
+
const resolved = resolveClassDeclaration(interceptorClassName, bootstrapSourceFile, bootstrapFilePath);
|
|
1064
|
+
if (resolved === void 0) return;
|
|
1065
|
+
const interceptMethod = findInterceptMethod(resolved.classDeclaration.members);
|
|
1066
|
+
if (interceptMethod?.body === void 0) return;
|
|
1067
|
+
const returnExpression = resolveSoleReturnExpression(interceptMethod.body);
|
|
1068
|
+
if (returnExpression === void 0) return;
|
|
1069
|
+
const mapCallback = matchPipedMapCallback(returnExpression, resolved.sourceFile);
|
|
1070
|
+
if (mapCallback === void 0 || mapCallback.parameters.length !== 1) return;
|
|
1071
|
+
const [callbackParameter] = mapCallback.parameters;
|
|
1072
|
+
if (callbackParameter === void 0 || !isIdentifier(callbackParameter.name)) return;
|
|
1073
|
+
const callbackReturnExpression = resolveSoleReturnExpression(mapCallback.body);
|
|
1074
|
+
if (callbackReturnExpression === void 0 || !isObjectLiteralExpression(callbackReturnExpression)) return;
|
|
1075
|
+
return synthesizeGenericObjectType(callbackReturnExpression, callbackParameter.name.text, innerTypeSource);
|
|
1076
|
+
};
|
|
1077
|
+
/**
|
|
1078
|
+
* Interceptors registered first wrap those registered after them, so the
|
|
1079
|
+
* composed type is built from the last-registered interceptor (closest to the
|
|
1080
|
+
* raw handler result) outward to the first-registered one.
|
|
1081
|
+
*/
|
|
1082
|
+
var detectResponseWrapper = (bootstrapFilePath) => {
|
|
1083
|
+
const bootstrapSourceFile = parseSourceFile(bootstrapFilePath);
|
|
1084
|
+
const interceptorClassNames = findGlobalInterceptorClassNames(bootstrapSourceFile);
|
|
1085
|
+
if (interceptorClassNames === void 0) return;
|
|
1086
|
+
let composedTypeSource = WRAPPED_RESULT_SUBSTITUTION;
|
|
1087
|
+
for (const interceptorClassName of [...interceptorClassNames].reverse()) {
|
|
1088
|
+
const wrapperTypeSource = synthesizeInterceptorWrapperType(interceptorClassName, bootstrapSourceFile, bootstrapFilePath, composedTypeSource);
|
|
1089
|
+
if (wrapperTypeSource === void 0) return;
|
|
1090
|
+
composedTypeSource = wrapperTypeSource;
|
|
1091
|
+
}
|
|
1092
|
+
return { wrappedResultTypeSource: composedTypeSource };
|
|
1093
|
+
};
|
|
1094
|
+
var REMOTE_RESULT_HELPERS = {
|
|
1095
|
+
json: "RemoteResult",
|
|
1096
|
+
observable: "RemoteObservableResult",
|
|
1097
|
+
stream: "RemoteStreamResult"
|
|
1098
|
+
};
|
|
1099
|
+
var WRAPPED_RESULT_HELPER = "__NestBridgeWrapped";
|
|
1100
|
+
var toPosixPath = (filePath) => filePath.replace(/\\/g, "/");
|
|
1101
|
+
var importSpecifierFor = (sourceFile, outputFilePath) => {
|
|
1102
|
+
if (!sourceFile.startsWith(".") && !/^[A-Za-z]:[\\/]/.test(sourceFile) && !sourceFile.startsWith("/")) return sourceFile;
|
|
1103
|
+
const relativePath = toPosixPath(relative(dirname(outputFilePath), sourceFile));
|
|
1104
|
+
return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
1105
|
+
};
|
|
1106
|
+
var stripExtension = (filePath) => {
|
|
1107
|
+
const extension = extname(filePath);
|
|
1108
|
+
return extension.length > 0 ? filePath.slice(0, -extension.length) : filePath;
|
|
1109
|
+
};
|
|
1110
|
+
var generateControllerDeclaration = (controller, outputFilePath, responseWrapper) => {
|
|
1111
|
+
const sourceFileSpecifier = importSpecifierFor(controller.sourceFile, outputFilePath);
|
|
1112
|
+
const importSpecifier = stripExtension(sourceFileSpecifier);
|
|
1113
|
+
const serverAlias = `__Server${controller.name}`;
|
|
1114
|
+
const helperNameFor = (method) => responseWrapper !== void 0 && (method.responseKind ?? "json") === "json" ? WRAPPED_RESULT_HELPER : REMOTE_RESULT_HELPERS[method.responseKind ?? "json"];
|
|
1115
|
+
const classBody = [
|
|
1116
|
+
" constructor();",
|
|
1117
|
+
"",
|
|
1118
|
+
...controller.methods.map((method) => {
|
|
1119
|
+
const helperName = helperNameFor(method);
|
|
1120
|
+
return ` ${method.name}(...args: Parameters<${serverAlias}['${method.name}']>): ${helperName}<${serverAlias}['${method.name}']>;`;
|
|
1121
|
+
})
|
|
1122
|
+
].join("\n");
|
|
1123
|
+
const rawHelperNames = controller.methods.map((method) => helperNameFor(method));
|
|
1124
|
+
const hasWrappedMethod = rawHelperNames.includes(WRAPPED_RESULT_HELPER);
|
|
1125
|
+
const helperImports = rawHelperNames.length === 0 ? ["RemoteResult"] : Array.from(new Set(rawHelperNames.filter((name) => name !== WRAPPED_RESULT_HELPER))).sort();
|
|
1126
|
+
const wrappedResultTypeAliasLines = responseWrapper !== void 0 && hasWrappedMethod ? [`type ${WRAPPED_RESULT_HELPER}<T> = T extends (...args: never[]) => infer Result ? Promise<${responseWrapper.wrappedResultTypeSource}> : never;`, ""] : [];
|
|
1127
|
+
return [
|
|
1128
|
+
`import type { ${controller.name} as ${serverAlias} } from '${importSpecifier}';`,
|
|
1129
|
+
...helperImports.length > 0 ? [`import type { ${helperImports.join(", ")} } from 'nestbridge';`] : [],
|
|
1130
|
+
"",
|
|
1131
|
+
"/**",
|
|
1132
|
+
" * Generated by NestBridge — client-facing methods only.",
|
|
1133
|
+
` * @see {@link ${sourceFileSpecifier}} for the real controller implementation.`,
|
|
1134
|
+
" */",
|
|
1135
|
+
...wrappedResultTypeAliasLines,
|
|
1136
|
+
`export declare class ${controller.name} {`,
|
|
1137
|
+
classBody,
|
|
1138
|
+
"}",
|
|
1139
|
+
""
|
|
1140
|
+
].join("\n");
|
|
1141
|
+
};
|
|
1142
|
+
var ERROR_BODY_TYPE_NAME = "NestBridgeErrorBody";
|
|
1143
|
+
var generateErrorBodyDeclaration = (errorBodyTypeSource) => [
|
|
1144
|
+
"/**",
|
|
1145
|
+
" * Generated by NestBridge — inferred shape of the global exception filter's response body.",
|
|
1146
|
+
" */",
|
|
1147
|
+
`export type ${ERROR_BODY_TYPE_NAME} = ${errorBodyTypeSource};`,
|
|
1148
|
+
""
|
|
1149
|
+
].join("\n");
|
|
1150
|
+
var formatDiagnostic = (diagnostic) => {
|
|
1151
|
+
const subject = diagnostic.memberName.length > 0 ? `${diagnostic.controllerName}.${diagnostic.memberName}` : diagnostic.controllerName;
|
|
1152
|
+
const lines = [
|
|
1153
|
+
`[NestBridge] ${diagnostic.title}`,
|
|
1154
|
+
"",
|
|
1155
|
+
subject,
|
|
1156
|
+
"",
|
|
1157
|
+
diagnostic.detail
|
|
1158
|
+
];
|
|
1159
|
+
if (diagnostic.found !== void 0) lines.push("", "Found:", diagnostic.found);
|
|
1160
|
+
if (diagnostic.expected !== void 0) lines.push("", "Expected:", diagnostic.expected);
|
|
1161
|
+
lines.push("", `${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}`);
|
|
1162
|
+
return lines.join("\n");
|
|
1163
|
+
};
|
|
1164
|
+
var generateConfigModule = (options) => {
|
|
1165
|
+
if (options.baseURL === void 0) return "";
|
|
1166
|
+
return [
|
|
1167
|
+
"import { setNestBridgeBaseURL } from 'nestbridge';",
|
|
1168
|
+
"",
|
|
1169
|
+
`setNestBridgeBaseURL(${JSON.stringify(options.baseURL)});`,
|
|
1170
|
+
""
|
|
1171
|
+
].join("\n");
|
|
1172
|
+
};
|
|
1173
|
+
/**
|
|
1174
|
+
* Must not contain a colon: webpack treats any `letter+:` prefix as a URL
|
|
1175
|
+
* scheme and routes the request past its filesystem resolver entirely,
|
|
1176
|
+
* bypassing the resolveId hook this virtual module relies on.
|
|
1177
|
+
*/
|
|
1178
|
+
var CONFIG_VIRTUAL_MODULE_ID = "virtual-nestbridge-config";
|
|
1179
|
+
var RESOLVED_CONFIG_VIRTUAL_MODULE_ID = `\0${CONFIG_VIRTUAL_MODULE_ID}`;
|
|
1180
|
+
var mergeNamedParametersExpression = (parameters) => {
|
|
1181
|
+
if (parameters.length === 0) return;
|
|
1182
|
+
const whole = parameters.find((parameter) => parameter.name === void 0);
|
|
1183
|
+
const named = parameters.filter((parameter) => parameter.name !== void 0);
|
|
1184
|
+
const namedEntries = named.map((parameter) => `${JSON.stringify(parameter.name)}: ${parameter.parameterName}`);
|
|
1185
|
+
if (whole === void 0) return `{ ${namedEntries.join(", ")} }`;
|
|
1186
|
+
if (named.length === 0) return whole.parameterName;
|
|
1187
|
+
return `{ ...${whole.parameterName}, ${namedEntries.join(", ")} }`;
|
|
1188
|
+
};
|
|
1189
|
+
var generateBodyExpression = (parameters) => mergeNamedParametersExpression(parameters.filter((parameter) => parameter.kind === "body"));
|
|
1190
|
+
var generateHeadersExpression = (parameters) => {
|
|
1191
|
+
const headerParameters = parameters.filter((parameter) => parameter.kind === "header");
|
|
1192
|
+
if (headerParameters.length === 0) return;
|
|
1193
|
+
return `{ ${headerParameters.map((parameter) => `${JSON.stringify(parameter.name)}: ${parameter.parameterName}`).join(", ")} }`;
|
|
1194
|
+
};
|
|
1195
|
+
var generateParameterList = (parameters) => [...parameters].sort((a, b) => a.index - b.index).map((parameter) => parameter.parameterName).join(", ");
|
|
1196
|
+
var PARAM_TOKEN_PATTERN = /:([A-Za-z0-9_]+)/;
|
|
1197
|
+
var generatePathExpression = (method) => {
|
|
1198
|
+
const paramParameters = method.parameters.filter((parameter) => parameter.kind === "param");
|
|
1199
|
+
const parts = method.path.split(PARAM_TOKEN_PATTERN);
|
|
1200
|
+
const expressionParts = [];
|
|
1201
|
+
parts.forEach((part, index) => {
|
|
1202
|
+
if (!(index % 2 === 1)) {
|
|
1203
|
+
if (part.length > 0) expressionParts.push(JSON.stringify(part));
|
|
1204
|
+
return;
|
|
1205
|
+
}
|
|
1206
|
+
const match = paramParameters.find((parameter) => parameter.name === part);
|
|
1207
|
+
expressionParts.push(match !== void 0 ? `encodeURIComponent(${match.parameterName})` : JSON.stringify(`:${part}`));
|
|
1208
|
+
});
|
|
1209
|
+
return expressionParts.length > 0 ? expressionParts.join(" + ") : "''";
|
|
1210
|
+
};
|
|
1211
|
+
var generateQueryExpression = (parameters) => mergeNamedParametersExpression(parameters.filter((parameter) => parameter.kind === "query"));
|
|
1212
|
+
var generateMethod$1 = (method) => {
|
|
1213
|
+
const requestProperties = [`method: ${JSON.stringify(method.httpMethod)}`, `path: ${generatePathExpression(method)}`];
|
|
1214
|
+
const queryExpression = generateQueryExpression(method.parameters);
|
|
1215
|
+
if (queryExpression !== void 0) requestProperties.push(`query: ${queryExpression}`);
|
|
1216
|
+
const bodyExpression = generateBodyExpression(method.parameters);
|
|
1217
|
+
if (bodyExpression !== void 0) requestProperties.push(`body: ${bodyExpression}`);
|
|
1218
|
+
const headersExpression = generateHeadersExpression(method.parameters);
|
|
1219
|
+
if (headersExpression !== void 0) requestProperties.push(`headers: ${headersExpression}`);
|
|
1220
|
+
if (method.responseKind === "stream") requestProperties.push(`responseType: "blob"`);
|
|
1221
|
+
return [
|
|
1222
|
+
` ${method.name}(${generateParameterList(method.parameters)}) {`,
|
|
1223
|
+
" return request({",
|
|
1224
|
+
...requestProperties.map((property) => ` ${property},`),
|
|
1225
|
+
" });",
|
|
1226
|
+
" }"
|
|
1227
|
+
].join("\n");
|
|
1228
|
+
};
|
|
1229
|
+
var generateControllerModule = (controller) => {
|
|
1230
|
+
const methods = controller.methods.map(generateMethod$1).join("\n\n");
|
|
1231
|
+
return [
|
|
1232
|
+
`import '${CONFIG_VIRTUAL_MODULE_ID}';`,
|
|
1233
|
+
"import { request } from 'nestbridge';",
|
|
1234
|
+
"",
|
|
1235
|
+
`export class ${controller.name} {`,
|
|
1236
|
+
" constructor() {}",
|
|
1237
|
+
"",
|
|
1238
|
+
methods,
|
|
1239
|
+
"}",
|
|
1240
|
+
""
|
|
1241
|
+
].join("\n");
|
|
1242
|
+
};
|
|
1243
|
+
var capitalize = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
1244
|
+
var renderSelectionSet = (fields) => {
|
|
1245
|
+
return `{ ${fields.map((field) => field.children === void 0 ? field.name : `${field.name} ${renderSelectionSet(field.children)}`).join(" ")} }`;
|
|
1246
|
+
};
|
|
1247
|
+
var generateGraphqlDocument = (method) => {
|
|
1248
|
+
const variableDeclarations = method.arguments.map((argument) => `$${argument.name}: ${argument.graphqlType}`).join(", ");
|
|
1249
|
+
const fieldArguments = method.arguments.map((argument) => `${argument.name}: $${argument.name}`).join(", ");
|
|
1250
|
+
const variablesClause = variableDeclarations.length > 0 ? `(${variableDeclarations})` : "";
|
|
1251
|
+
const fieldArgumentsClause = fieldArguments.length > 0 ? `(${fieldArguments})` : "";
|
|
1252
|
+
const fieldSelection = method.selection.length > 0 ? ` ${renderSelectionSet(method.selection)}` : "";
|
|
1253
|
+
return `${method.operationKind} ${capitalize(method.name)}${variablesClause} { ${method.operationName}${fieldArgumentsClause}${fieldSelection} }`;
|
|
1254
|
+
};
|
|
1255
|
+
var generateResolverArgumentList = (resolverArguments) => [...resolverArguments].sort((a, b) => a.index - b.index).map((argument) => argument.parameterName).join(", ");
|
|
1256
|
+
var generateVariablesExpression = (resolverArguments) => {
|
|
1257
|
+
if (resolverArguments.length === 0) return;
|
|
1258
|
+
return `{ ${resolverArguments.map((argument) => `${JSON.stringify(argument.name)}: ${argument.parameterName}`).join(", ")} }`;
|
|
1259
|
+
};
|
|
1260
|
+
var generateMethod = (method) => {
|
|
1261
|
+
const requestProperties = [`document: ${JSON.stringify(generateGraphqlDocument(method))}`];
|
|
1262
|
+
const variablesExpression = generateVariablesExpression(method.arguments);
|
|
1263
|
+
if (variablesExpression !== void 0) requestProperties.push(`variables: ${variablesExpression}`);
|
|
1264
|
+
return [
|
|
1265
|
+
` async ${method.name}(${generateResolverArgumentList(method.arguments)}) {`,
|
|
1266
|
+
" const result = await graphqlRequest({",
|
|
1267
|
+
...requestProperties.map((property) => ` ${property},`),
|
|
1268
|
+
" });",
|
|
1269
|
+
"",
|
|
1270
|
+
` return result.${method.operationName};`,
|
|
1271
|
+
" }"
|
|
1272
|
+
].join("\n");
|
|
1273
|
+
};
|
|
1274
|
+
var generateResolverModule = (resolver) => {
|
|
1275
|
+
const methods = resolver.methods.map(generateMethod).join("\n\n");
|
|
1276
|
+
return [
|
|
1277
|
+
`import '${CONFIG_VIRTUAL_MODULE_ID}';`,
|
|
1278
|
+
"import { graphqlRequest } from 'nestbridge';",
|
|
1279
|
+
"",
|
|
1280
|
+
`export class ${resolver.name} {`,
|
|
1281
|
+
" constructor() {}",
|
|
1282
|
+
"",
|
|
1283
|
+
methods,
|
|
1284
|
+
"}",
|
|
1285
|
+
""
|
|
1286
|
+
].join("\n");
|
|
1287
|
+
};
|
|
1288
|
+
var stripLeadingParentSegments = (relativePath) => {
|
|
1289
|
+
const segments = relativePath.split(/[\\/]/);
|
|
1290
|
+
const firstOwnedIndex = segments.findIndex((segment) => segment !== "..");
|
|
1291
|
+
return segments.slice(firstOwnedIndex === -1 ? segments.length : firstOwnedIndex).join("/");
|
|
1292
|
+
};
|
|
1293
|
+
var mirroredDeclarationPath = (root, outputDir, controllerFilePath) => {
|
|
1294
|
+
const mirroredPath = stripLeadingParentSegments(relative(root, controllerFilePath)).replace(/\.ts$/, ".d.ts");
|
|
1295
|
+
return join(root, outputDir, mirroredPath);
|
|
1296
|
+
};
|
|
1297
|
+
var writeControllerDeclarations = (controllers, root, outputDir, responseWrapper) => {
|
|
1298
|
+
for (const controller of controllers) {
|
|
1299
|
+
const outputFilePath = mirroredDeclarationPath(root, outputDir, controller.sourceFile);
|
|
1300
|
+
mkdirSync(dirname(outputFilePath), { recursive: true });
|
|
1301
|
+
writeFileSync(outputFilePath, generateControllerDeclaration(controller, outputFilePath, responseWrapper), "utf-8");
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
var ERROR_BODY_DECLARATION_FILE_NAME = "nestbridge-error-body.d.ts";
|
|
1305
|
+
var errorBodyDeclarationPath = (root, outputDir) => join(root, outputDir, ERROR_BODY_DECLARATION_FILE_NAME);
|
|
1306
|
+
var writeErrorBodyDeclaration = (root, outputDir, errorResponseShape) => {
|
|
1307
|
+
const outputFilePath = errorBodyDeclarationPath(root, outputDir);
|
|
1308
|
+
if (errorResponseShape === void 0) {
|
|
1309
|
+
rmSync(outputFilePath, { force: true });
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
mkdirSync(dirname(outputFilePath), { recursive: true });
|
|
1313
|
+
writeFileSync(outputFilePath, generateErrorBodyDeclaration(errorResponseShape.errorBodyTypeSource), "utf-8");
|
|
1314
|
+
};
|
|
1315
|
+
var writeResolverDeclarations = (resolvers, root, outputDir) => {
|
|
1316
|
+
for (const resolver of resolvers) {
|
|
1317
|
+
const outputFilePath = mirroredDeclarationPath(root, outputDir, resolver.sourceFile);
|
|
1318
|
+
mkdirSync(dirname(outputFilePath), { recursive: true });
|
|
1319
|
+
writeFileSync(outputFilePath, generateControllerDeclaration(resolver, outputFilePath), "utf-8");
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
var discoverFiles = (patterns, root, exclude) => {
|
|
1323
|
+
const matches = globSync([...patterns], {
|
|
1324
|
+
cwd: root,
|
|
1325
|
+
exclude
|
|
1326
|
+
});
|
|
1327
|
+
return [...new Set(matches.map((match) => resolve(root, match)))].sort();
|
|
1328
|
+
};
|
|
1329
|
+
var BOOTSTRAP_FILE_PATTERN = "**/main.ts";
|
|
1330
|
+
var discoverBootstrapFile = (root, outputDir) => {
|
|
1331
|
+
const resolvedOutputDir = resolve(root, outputDir);
|
|
1332
|
+
const isExcluded = (filePath) => {
|
|
1333
|
+
if (filePath.split(/[\\/]/).includes("node_modules")) return true;
|
|
1334
|
+
const resolvedPath = resolve(root, filePath);
|
|
1335
|
+
return resolvedPath === resolvedOutputDir || resolvedPath.startsWith(`${resolvedOutputDir}${sep}`);
|
|
1336
|
+
};
|
|
1337
|
+
const matches = discoverFiles([BOOTSTRAP_FILE_PATTERN], root, isExcluded);
|
|
1338
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
1339
|
+
};
|
|
1340
|
+
var resolveNestBridgeOptions = (options) => ({
|
|
1341
|
+
controllers: Array.isArray(options.controllers) ? options.controllers : [options.controllers],
|
|
1342
|
+
resolvers: options.resolvers === void 0 ? [] : Array.isArray(options.resolvers) ? options.resolvers : [options.resolvers],
|
|
1343
|
+
baseURL: options.baseURL,
|
|
1344
|
+
debug: options.debug ?? false,
|
|
1345
|
+
outputDir: options.outputDir ?? ".nestbridge",
|
|
1346
|
+
root: options.root
|
|
1347
|
+
});
|
|
1348
|
+
var isWindows = process.platform === "win32";
|
|
1349
|
+
var normalizePath = (filePath) => posix.normalize(isWindows ? filePath.replaceAll("\\", "/") : filePath);
|
|
1350
|
+
var createControllerRegistry = (patterns, root) => {
|
|
1351
|
+
const controllersByFile = /* @__PURE__ */ new Map();
|
|
1352
|
+
const refresh = () => {
|
|
1353
|
+
controllersByFile.clear();
|
|
1354
|
+
const { controllers, diagnostics } = analyzeControllers(discoverFiles(patterns, root));
|
|
1355
|
+
for (const controller of controllers) controllersByFile.set(normalizePath(controller.sourceFile), controller);
|
|
1356
|
+
return {
|
|
1357
|
+
controllers,
|
|
1358
|
+
diagnostics
|
|
1359
|
+
};
|
|
1360
|
+
};
|
|
1361
|
+
return {
|
|
1362
|
+
refresh,
|
|
1363
|
+
get: (filePath) => controllersByFile.get(filePath),
|
|
1364
|
+
has: (filePath) => controllersByFile.has(filePath),
|
|
1365
|
+
all: () => [...controllersByFile.values()]
|
|
1366
|
+
};
|
|
1367
|
+
};
|
|
1368
|
+
var createResolverRegistry = (patterns, root) => {
|
|
1369
|
+
const resolversByFile = /* @__PURE__ */ new Map();
|
|
1370
|
+
const refresh = () => {
|
|
1371
|
+
resolversByFile.clear();
|
|
1372
|
+
const { resolvers, diagnostics } = analyzeResolvers(discoverFiles(patterns, root));
|
|
1373
|
+
for (const resolver of resolvers) resolversByFile.set(normalizePath(resolver.sourceFile), resolver);
|
|
1374
|
+
return {
|
|
1375
|
+
resolvers,
|
|
1376
|
+
diagnostics
|
|
1377
|
+
};
|
|
1378
|
+
};
|
|
1379
|
+
return {
|
|
1380
|
+
refresh,
|
|
1381
|
+
get: (filePath) => resolversByFile.get(filePath),
|
|
1382
|
+
has: (filePath) => resolversByFile.has(filePath),
|
|
1383
|
+
all: () => [...resolversByFile.values()]
|
|
1384
|
+
};
|
|
1385
|
+
};
|
|
1386
|
+
var reportDiagnostics = (diagnostics, watchMode) => {
|
|
1387
|
+
for (const diagnostic of diagnostics) {
|
|
1388
|
+
const message = formatDiagnostic(diagnostic);
|
|
1389
|
+
if (watchMode === false) throw new Error(message);
|
|
1390
|
+
console.warn(message);
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
var nestBridgeUnplugin = createUnplugin((options, meta) => {
|
|
1394
|
+
const resolvedOptions = resolveNestBridgeOptions(options);
|
|
1395
|
+
let root = resolvedOptions.root ?? process.cwd();
|
|
1396
|
+
let controllerRegistry;
|
|
1397
|
+
let resolverRegistry;
|
|
1398
|
+
let bootstrapFilePath;
|
|
1399
|
+
const log = (message) => {
|
|
1400
|
+
if (resolvedOptions.debug) console.log(`[nestbridge] ${message}`);
|
|
1401
|
+
};
|
|
1402
|
+
const runAnalysis = () => {
|
|
1403
|
+
const { controllers, diagnostics: controllerDiagnostics } = controllerRegistry.refresh();
|
|
1404
|
+
const { resolvers, diagnostics: resolverDiagnostics } = resolverRegistry.refresh();
|
|
1405
|
+
reportDiagnostics([...controllerDiagnostics, ...resolverDiagnostics], meta.watchMode);
|
|
1406
|
+
const discoveredBootstrapFile = discoverBootstrapFile(root, resolvedOptions.outputDir);
|
|
1407
|
+
bootstrapFilePath = discoveredBootstrapFile !== void 0 ? normalizePath(discoveredBootstrapFile) : void 0;
|
|
1408
|
+
const responseWrapper = discoveredBootstrapFile !== void 0 ? detectResponseWrapper(discoveredBootstrapFile) : void 0;
|
|
1409
|
+
const errorResponseShape = discoveredBootstrapFile !== void 0 ? detectErrorResponseShape(discoveredBootstrapFile) : void 0;
|
|
1410
|
+
writeControllerDeclarations(controllers, root, resolvedOptions.outputDir, responseWrapper);
|
|
1411
|
+
writeResolverDeclarations(resolvers, root, resolvedOptions.outputDir);
|
|
1412
|
+
writeErrorBodyDeclaration(root, resolvedOptions.outputDir, errorResponseShape);
|
|
1413
|
+
log(`analyzed ${controllers.length} controller(s), ${resolvers.length} resolver(s), ${controllerDiagnostics.length + resolverDiagnostics.length} diagnostic(s)`);
|
|
1414
|
+
};
|
|
1415
|
+
return {
|
|
1416
|
+
name: "nestbridge",
|
|
1417
|
+
enforce: "pre",
|
|
1418
|
+
buildStart() {
|
|
1419
|
+
controllerRegistry = createControllerRegistry(resolvedOptions.controllers, root);
|
|
1420
|
+
resolverRegistry = createResolverRegistry(resolvedOptions.resolvers, root);
|
|
1421
|
+
runAnalysis();
|
|
1422
|
+
},
|
|
1423
|
+
resolveId(source) {
|
|
1424
|
+
if (source === "virtual-nestbridge-config") return RESOLVED_CONFIG_VIRTUAL_MODULE_ID;
|
|
1425
|
+
return null;
|
|
1426
|
+
},
|
|
1427
|
+
load(id) {
|
|
1428
|
+
if (id === RESOLVED_CONFIG_VIRTUAL_MODULE_ID) return generateConfigModule(resolvedOptions);
|
|
1429
|
+
return null;
|
|
1430
|
+
},
|
|
1431
|
+
transform(_code, id) {
|
|
1432
|
+
const absolutePath = normalizePath(id);
|
|
1433
|
+
const controller = controllerRegistry.get(absolutePath);
|
|
1434
|
+
if (controller !== void 0) return generateControllerModule(controller);
|
|
1435
|
+
const resolver = resolverRegistry.get(absolutePath);
|
|
1436
|
+
if (resolver !== void 0) return generateResolverModule(resolver);
|
|
1437
|
+
return null;
|
|
1438
|
+
},
|
|
1439
|
+
watchChange(id) {
|
|
1440
|
+
const changedFile = normalizePath(id);
|
|
1441
|
+
const wasTracked = controllerRegistry.has(changedFile) || resolverRegistry.has(changedFile) || changedFile === bootstrapFilePath;
|
|
1442
|
+
const discoveredBootstrapFile = discoverBootstrapFile(root, resolvedOptions.outputDir);
|
|
1443
|
+
const isTrackedNow = discoverFiles(resolvedOptions.controllers, root).map(normalizePath).includes(changedFile) || discoverFiles(resolvedOptions.resolvers, root).map(normalizePath).includes(changedFile) || discoveredBootstrapFile !== void 0 && normalizePath(discoveredBootstrapFile) === changedFile;
|
|
1444
|
+
if (!wasTracked && !isTrackedNow) return;
|
|
1445
|
+
runAnalysis();
|
|
1446
|
+
log(`nestbridge source changed: ${changedFile}`);
|
|
1447
|
+
},
|
|
1448
|
+
vite: { configResolved(config) {
|
|
1449
|
+
if (resolvedOptions.root === void 0) root = config.root;
|
|
1450
|
+
} }
|
|
1451
|
+
};
|
|
1452
|
+
});
|
|
1453
|
+
//#endregion
|
|
1454
|
+
//#region src/version.ts
|
|
1455
|
+
var VERSION = "2.1.0";
|
|
1456
|
+
//#endregion
|
|
1457
|
+
//#region src/main.ts
|
|
1458
|
+
var nestBridge = nestBridgeUnplugin.webpack;
|
|
1459
|
+
//#endregion
|
|
1460
|
+
export { VERSION, nestBridge };
|
|
1461
|
+
|
|
1462
|
+
//# sourceMappingURL=nestbridge.webpack.js.map
|