Skip to content

Commit b03bd8a

Browse files
committed
feat: add import declaration in visitor
1 parent 99c88a5 commit b03bd8a

File tree

4 files changed

+117
-52
lines changed

4 files changed

+117
-52
lines changed

.eslintrc.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,5 @@ rules:
2626
caseInsensitive: true
2727
'@typescript-eslint/no-extraneous-class': 0
2828
'@typescript-eslint/no-explicit-any': 0
29+
no-console:
30+
- error
Lines changed: 40 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

33
exports[`HttpInterfaceVisitor > should handle array return type 1`] = `
4-
"import { HttpInterface, GetExchange } from '@r2don/nest-http-interface';
4+
"\\"use strict\\";
5+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
6+
const eager_module_1 = require(\\"@r2don/nest-http-interface\\");
7+
const nest_http_interface_1 = require(\\"@r2don/nest-http-interface\\");
58
class ResponseClass {
69
}
710
let UserService = class UserService {
@@ -16,25 +19,27 @@ let UserService = class UserService {
1619
}
1720
};
1821
__decorate([
19-
GetExchange(),
20-
ResponseBody(ResponseClass)
22+
(0, nest_http_interface_1.GetExchange)(),
23+
eager_module_1.ResponseBody(ResponseClass)
2124
], UserService.prototype, \\"getUsers\\", null);
2225
__decorate([
23-
GetExchange(),
24-
ResponseBody(ResponseClass)
26+
(0, nest_http_interface_1.GetExchange)(),
27+
eager_module_1.ResponseBody(ResponseClass)
2528
], UserService.prototype, \\"getUserList\\", null);
2629
__decorate([
27-
GetExchange(),
28-
ResponseBody(ResponseClass)
30+
(0, nest_http_interface_1.GetExchange)(),
31+
eager_module_1.ResponseBody(ResponseClass)
2932
], UserService.prototype, \\"getUsersReadonly\\", null);
3033
UserService = __decorate([
31-
HttpInterface()
34+
(0, nest_http_interface_1.HttpInterface)()
3235
], UserService);
3336
"
3437
`;
3538

3639
exports[`HttpInterfaceVisitor > should ignore if file name is not match 1`] = `
37-
"import { HttpInterface, GraphQLExchange } from '@r2don/nest-http-interface';
40+
"\\"use strict\\";
41+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
42+
const nest_http_interface_1 = require(\\"@r2don/nest-http-interface\\");
3843
class ResponseClass {
3944
}
4045
let UserService = class UserService {
@@ -43,67 +48,76 @@ let UserService = class UserService {
4348
}
4449
};
4550
__decorate([
46-
GraphQLExchange()
51+
(0, nest_http_interface_1.GraphQLExchange)()
4752
], UserService.prototype, \\"getUser\\", null);
4853
UserService = __decorate([
49-
HttpInterface()
54+
(0, nest_http_interface_1.HttpInterface)()
5055
], UserService);
5156
"
5257
`;
5358

5459
exports[`HttpInterfaceVisitor > should ignore if method has ResponseBody decorator 1`] = `
55-
"import { HttpInterface, PostExchange, ResponseBody } from '@r2don/nest-http-interface';
56-
import { User } from './user.entity';
60+
"\\"use strict\\";
61+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
62+
const nest_http_interface_1 = require(\\"@r2don/nest-http-interface\\");
63+
const user_entity_1 = require(\\"./user.entity\\");
5764
let UserService = class UserService {
5865
async getUser() {
5966
throw new Error('not implemented');
6067
}
6168
};
6269
__decorate([
63-
PostExchange(),
64-
ResponseBody(User)
70+
(0, nest_http_interface_1.PostExchange)(),
71+
(0, nest_http_interface_1.ResponseBody)(user_entity_1.User)
6572
], UserService.prototype, \\"getUser\\", null);
6673
UserService = __decorate([
67-
HttpInterface()
74+
(0, nest_http_interface_1.HttpInterface)()
6875
], UserService);
6976
"
7077
`;
7178

7279
exports[`HttpInterfaceVisitor > should ignore if return type if not a promise 1`] = `
73-
"import { HttpInterface, GetExchange } from '@r2don/nest-http-interface';
80+
"\\"use strict\\";
81+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
82+
const nest_http_interface_1 = require(\\"@r2don/nest-http-interface\\");
7483
let UserService = class UserService {
7584
getUser() {
7685
throw new Error('not implemented');
7786
}
7887
};
7988
__decorate([
80-
GetExchange()
89+
(0, nest_http_interface_1.GetExchange)()
8190
], UserService.prototype, \\"getUser\\", null);
8291
UserService = __decorate([
83-
HttpInterface()
92+
(0, nest_http_interface_1.HttpInterface)()
8493
], UserService);
8594
"
8695
`;
8796

8897
exports[`HttpInterfaceVisitor > should ignore if return type is not a class 1`] = `
89-
"import { HttpInterface, GetExchange } from '@r2don/nest-http-interface';
98+
"\\"use strict\\";
99+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
100+
const nest_http_interface_1 = require(\\"@r2don/nest-http-interface\\");
90101
let TextService = class TextService {
91102
constructor() { }
92103
async getText() {
93104
return 'text';
94105
}
95106
};
96107
__decorate([
97-
GetExchange()
108+
(0, nest_http_interface_1.GetExchange)()
98109
], TextService.prototype, \\"getText\\", null);
99110
TextService = __decorate([
100-
HttpInterface()
111+
(0, nest_http_interface_1.HttpInterface)()
101112
], TextService);
102113
"
103114
`;
104115

105116
exports[`HttpInterfaceVisitor > should override plugin suffix option 1`] = `
106-
"import { HttpInterface, GraphQLExchange } from '@r2don/nest-http-interface';
117+
"\\"use strict\\";
118+
Object.defineProperty(exports, \\"__esModule\\", { value: true });
119+
const eager_module_1 = require(\\"@r2don/nest-http-interface\\");
120+
const nest_http_interface_1 = require(\\"@r2don/nest-http-interface\\");
107121
class ResponseClass {
108122
}
109123
let UserService = class UserService {
@@ -112,11 +126,11 @@ let UserService = class UserService {
112126
}
113127
};
114128
__decorate([
115-
GraphQLExchange(),
116-
ResponseBody(ResponseClass)
129+
(0, nest_http_interface_1.GraphQLExchange)(),
130+
eager_module_1.ResponseBody(ResponseClass)
117131
], UserService.prototype, \\"getUser\\", null);
118132
UserService = __decorate([
119-
HttpInterface()
133+
(0, nest_http_interface_1.HttpInterface)()
120134
], UserService);
121135
"
122136
`;

lib/plugin/visitors/http-interface.visitor.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { before } from '../compiler-plugin';
1111

1212
describe('HttpInterfaceVisitor', () => {
1313
const compilerOptions = {
14-
module: ts.ModuleKind.ES2020,
14+
module: ts.ModuleKind.CommonJS,
1515
target: ts.ScriptTarget.ES2020,
1616
newLine: ts.NewLineKind.LineFeed,
1717
noEmitHelpers: true,

lib/plugin/visitors/http-interface.visitor.ts

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import { GraphQLExchange } from '../../decorators/graphql-exchange.decorator';
1313
import { ResponseBody } from '../../decorators/response-body.decorator';
1414

1515
export class HttpInterfaceVisitor {
16-
static HTTP_INTERFACE_DECORATOR = HttpInterface.name;
17-
static RESPONSE_BODY_DECORATOR = ResponseBody.name;
18-
static ALL_EXCHANGE_DECORATORS = [
16+
HTTP_INTERFACE_DECORATOR = HttpInterface.name;
17+
RESPONSE_BODY_DECORATOR = ResponseBody.name;
18+
ALL_EXCHANGE_DECORATORS = [
1919
GetExchange.name,
2020
PostExchange.name,
2121
PutExchange.name,
@@ -26,42 +26,87 @@ export class HttpInterfaceVisitor {
2626
GraphQLExchange.name,
2727
];
2828

29+
libModuleAlias = 'eager_module_1';
30+
libName = '@r2don/nest-http-interface';
31+
importSet = new Set<string>();
32+
2933
visit(
3034
sourceFile: ts.SourceFile,
3135
ctx: ts.TransformationContext,
3236
_program: ts.Program,
3337
): ts.Node {
38+
this.importSet.clear();
3439
const factory = ctx.factory;
3540
const visitNode = (node: ts.Node): ts.Node => {
36-
if (!this.isHttpInterfaceClass(node)) {
37-
return ts.visitEachChild(node, visitNode, ctx);
41+
if (this.isHttpInterfaceClass(node)) {
42+
return this.visitMethods(node, factory);
43+
}
44+
45+
if (ts.isSourceFile(node)) {
46+
return this.updateSourceFile(node, visitNode, ctx, factory);
3847
}
3948

40-
const updatedMembers = node.members.map((member) => {
41-
if (!this.isExchangeMethod(member)) {
42-
return member;
43-
}
44-
45-
return this.appendResponseBodyDecorator(member, factory);
46-
});
47-
48-
return factory.updateClassDeclaration(
49-
node,
50-
this.getDecorators(node),
51-
node.name,
52-
node.typeParameters,
53-
node.heritageClauses,
54-
updatedMembers,
55-
);
49+
return ts.visitEachChild(node, visitNode, ctx);
5650
};
5751

5852
return ts.visitNode(sourceFile, visitNode);
5953
}
6054

55+
private updateSourceFile(
56+
node: ts.SourceFile,
57+
visitNode: (node: ts.Node) => ts.Node,
58+
ctx: ts.TransformationContext,
59+
factory: ts.NodeFactory,
60+
): ts.SourceFile {
61+
const visitedNode = ts.visitEachChild(node, visitNode, ctx);
62+
if (this.importSet.size === 0) {
63+
return visitedNode;
64+
}
65+
66+
const importStatements = [...this.importSet].map((value) =>
67+
factory.createImportEqualsDeclaration(
68+
undefined,
69+
false,
70+
value,
71+
factory.createExternalModuleReference(
72+
factory.createStringLiteral(this.libName),
73+
),
74+
),
75+
);
76+
77+
const existingStatements = Array.from(visitedNode.statements);
78+
return factory.updateSourceFile(visitedNode, [
79+
...importStatements,
80+
...existingStatements,
81+
]);
82+
}
83+
84+
private visitMethods(
85+
node: ts.ClassDeclaration,
86+
factory: ts.NodeFactory,
87+
): ts.ClassDeclaration {
88+
const updatedMembers = node.members.map((member) => {
89+
if (!this.isExchangeMethod(member)) {
90+
return member;
91+
}
92+
93+
return this.appendResponseBodyDecorator(member, factory);
94+
});
95+
96+
return factory.updateClassDeclaration(
97+
node,
98+
node.modifiers,
99+
node.name,
100+
node.typeParameters,
101+
node.heritageClauses,
102+
updatedMembers,
103+
);
104+
}
105+
61106
private isHttpInterfaceClass(node: ts.Node): node is ts.ClassDeclaration {
62107
return (
63108
ts.isClassDeclaration(node) &&
64-
this.hasDecorator(node, [HttpInterfaceVisitor.HTTP_INTERFACE_DECORATOR])
109+
this.hasDecorator(node, [this.HTTP_INTERFACE_DECORATOR])
65110
);
66111
}
67112

@@ -75,9 +120,13 @@ export class HttpInterfaceVisitor {
75120
return node;
76121
}
77122

123+
this.importSet.add(this.libModuleAlias);
78124
const decorator = factory.createDecorator(
79125
factory.createCallExpression(
80-
factory.createIdentifier('ResponseBody'),
126+
factory.createPropertyAccessExpression(
127+
factory.createIdentifier(this.libModuleAlias),
128+
factory.createIdentifier(this.RESPONSE_BODY_DECORATOR),
129+
),
81130
[],
82131
[factory.createIdentifier(returnType)],
83132
),
@@ -151,8 +200,8 @@ export class HttpInterfaceVisitor {
151200
member: ts.ClassElement,
152201
): member is ts.MethodDeclaration {
153202
return (
154-
this.hasDecorator(member, HttpInterfaceVisitor.ALL_EXCHANGE_DECORATORS) &&
155-
!this.hasDecorator(member, [HttpInterfaceVisitor.RESPONSE_BODY_DECORATOR])
203+
this.hasDecorator(member, this.ALL_EXCHANGE_DECORATORS) &&
204+
!this.hasDecorator(member, [this.RESPONSE_BODY_DECORATOR])
156205
);
157206
}
158207

0 commit comments

Comments
 (0)