proteum 1.0.0-1
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/.dockerignore +10 -0
- package/Rte.zip +0 -0
- package/cli/app/config.ts +54 -0
- package/cli/app/index.ts +195 -0
- package/cli/bin.js +11 -0
- package/cli/commands/build.ts +34 -0
- package/cli/commands/deploy/app.ts +29 -0
- package/cli/commands/deploy/web.ts +60 -0
- package/cli/commands/dev.ts +109 -0
- package/cli/commands/init.ts +85 -0
- package/cli/compiler/client/identite.ts +72 -0
- package/cli/compiler/client/index.ts +334 -0
- package/cli/compiler/common/babel/index.ts +170 -0
- package/cli/compiler/common/babel/plugins/index.ts +0 -0
- package/cli/compiler/common/babel/plugins/services.ts +579 -0
- package/cli/compiler/common/babel/routes/imports.ts +127 -0
- package/cli/compiler/common/babel/routes/routes.ts +1130 -0
- package/cli/compiler/common/files/autres.ts +39 -0
- package/cli/compiler/common/files/images.ts +35 -0
- package/cli/compiler/common/files/style.ts +78 -0
- package/cli/compiler/common/index.ts +154 -0
- package/cli/compiler/index.ts +532 -0
- package/cli/compiler/server/index.ts +211 -0
- package/cli/index.ts +189 -0
- package/cli/paths.ts +165 -0
- package/cli/print.ts +12 -0
- package/cli/tsconfig.json +38 -0
- package/cli/utils/index.ts +22 -0
- package/cli/utils/keyboard.ts +78 -0
- package/client/app/component.tsx +54 -0
- package/client/app/index.ts +142 -0
- package/client/app/service.ts +34 -0
- package/client/app.tsconfig.json +28 -0
- package/client/components/Button.tsx +298 -0
- package/client/components/Dialog/Manager.tsx +309 -0
- package/client/components/Dialog/card.tsx +208 -0
- package/client/components/Dialog/index.less +151 -0
- package/client/components/Dialog/status.less +176 -0
- package/client/components/Dialog/status.tsx +48 -0
- package/client/components/index.ts +2 -0
- package/client/components/types.d.ts +3 -0
- package/client/data/input.ts +32 -0
- package/client/global.d.ts +5 -0
- package/client/hooks.ts +22 -0
- package/client/index.ts +6 -0
- package/client/pages/_layout/index.less +6 -0
- package/client/pages/_layout/index.tsx +43 -0
- package/client/pages/bug.tsx.old +60 -0
- package/client/pages/useHeader.tsx +50 -0
- package/client/services/captcha/index.ts +67 -0
- package/client/services/router/components/Link.tsx +46 -0
- package/client/services/router/components/Page.tsx +55 -0
- package/client/services/router/components/router.tsx +218 -0
- package/client/services/router/index.tsx +521 -0
- package/client/services/router/request/api.ts +267 -0
- package/client/services/router/request/history.ts +5 -0
- package/client/services/router/request/index.ts +53 -0
- package/client/services/router/request/multipart.ts +147 -0
- package/client/services/router/response/index.tsx +128 -0
- package/client/services/router/response/page.ts +86 -0
- package/client/services/socket/index.ts +147 -0
- package/client/utils/dom.ts +77 -0
- package/common/app/index.ts +9 -0
- package/common/data/chaines/index.ts +54 -0
- package/common/data/dates.ts +179 -0
- package/common/data/markdown.ts +73 -0
- package/common/data/rte/nodes.ts +83 -0
- package/common/data/stats.ts +90 -0
- package/common/errors/index.tsx +326 -0
- package/common/router/index.ts +213 -0
- package/common/router/layouts.ts +93 -0
- package/common/router/register.ts +55 -0
- package/common/router/request/api.ts +77 -0
- package/common/router/request/index.ts +35 -0
- package/common/router/response/index.ts +45 -0
- package/common/router/response/page.ts +128 -0
- package/common/utils/rte.ts +183 -0
- package/common/utils.ts +7 -0
- package/doc/TODO.md +71 -0
- package/doc/front/router.md +27 -0
- package/doc/workspace/workspace.png +0 -0
- package/doc/workspace/workspace2.png +0 -0
- package/doc/workspace/workspace_26.01.22.png +0 -0
- package/package.json +171 -0
- package/server/app/commands.ts +141 -0
- package/server/app/container/config.ts +203 -0
- package/server/app/container/console/index.ts +550 -0
- package/server/app/container/index.ts +137 -0
- package/server/app/index.ts +273 -0
- package/server/app/service/container.ts +88 -0
- package/server/app/service/index.ts +235 -0
- package/server/app.tsconfig.json +28 -0
- package/server/context.ts +4 -0
- package/server/index.ts +4 -0
- package/server/services/auth/index.ts +250 -0
- package/server/services/auth/old.ts +277 -0
- package/server/services/auth/router/index.ts +95 -0
- package/server/services/auth/router/request.ts +54 -0
- package/server/services/auth/router/service.json +6 -0
- package/server/services/auth/service.json +6 -0
- package/server/services/cache/commands.ts +41 -0
- package/server/services/cache/index.ts +297 -0
- package/server/services/cache/service.json +6 -0
- package/server/services/cron/CronTask.ts +86 -0
- package/server/services/cron/index.ts +112 -0
- package/server/services/cron/service.json +6 -0
- package/server/services/disks/driver.ts +103 -0
- package/server/services/disks/drivers/local/index.ts +188 -0
- package/server/services/disks/drivers/local/service.json +6 -0
- package/server/services/disks/drivers/s3/index.ts +301 -0
- package/server/services/disks/drivers/s3/service.json +6 -0
- package/server/services/disks/index.ts +90 -0
- package/server/services/disks/service.json +6 -0
- package/server/services/email/index.ts +188 -0
- package/server/services/email/utils.ts +53 -0
- package/server/services/fetch/index.ts +201 -0
- package/server/services/fetch/service.json +7 -0
- package/server/services/models.7z +0 -0
- package/server/services/prisma/Facet.ts +142 -0
- package/server/services/prisma/index.ts +201 -0
- package/server/services/prisma/service.json +6 -0
- package/server/services/router/http/index.ts +217 -0
- package/server/services/router/http/multipart.ts +102 -0
- package/server/services/router/http/session.ts.old +40 -0
- package/server/services/router/index.ts +801 -0
- package/server/services/router/request/api.ts +87 -0
- package/server/services/router/request/index.ts +184 -0
- package/server/services/router/request/service.ts +21 -0
- package/server/services/router/request/validation/zod.ts +180 -0
- package/server/services/router/response/index.ts +338 -0
- package/server/services/router/response/mask/Filter.ts +323 -0
- package/server/services/router/response/mask/index.ts +60 -0
- package/server/services/router/response/mask/selecteurs.ts +92 -0
- package/server/services/router/response/page/document.tsx +160 -0
- package/server/services/router/response/page/index.tsx +196 -0
- package/server/services/router/service.json +6 -0
- package/server/services/router/service.ts +36 -0
- package/server/services/schema/index.ts +44 -0
- package/server/services/schema/request.ts +49 -0
- package/server/services/schema/router/index.ts +28 -0
- package/server/services/schema/router/service.json +6 -0
- package/server/services/schema/service.json +6 -0
- package/server/services/security/encrypt/aes/index.ts +85 -0
- package/server/services/security/encrypt/aes/service.json +6 -0
- package/server/services/socket/index.ts +162 -0
- package/server/services/socket/scope.ts +226 -0
- package/server/services/socket/service.json +6 -0
- package/server/services_old/SocketClient.ts +92 -0
- package/server/services_old/Token.old.ts +97 -0
- package/server/utils/slug.ts +79 -0
- package/tsconfig.common.json +45 -0
- package/tsconfig.json +3 -0
- package/types/aliases.d.ts +54 -0
- package/types/global/modules.d.ts +49 -0
- package/types/global/utils.d.ts +103 -0
- package/types/icons.d.ts +1 -0
|
@@ -0,0 +1,1130 @@
|
|
|
1
|
+
/*----------------------------------
|
|
2
|
+
- DEPENDANCES
|
|
3
|
+
----------------------------------*/
|
|
4
|
+
|
|
5
|
+
// Npm
|
|
6
|
+
import * as types from '@babel/types'
|
|
7
|
+
import type { PluginObj, NodePath } from '@babel/core';
|
|
8
|
+
|
|
9
|
+
// Core
|
|
10
|
+
import cli from '@cli';
|
|
11
|
+
import { App, TAppSide } from '../../../../app';
|
|
12
|
+
|
|
13
|
+
/*----------------------------------
|
|
14
|
+
- WEBPACK RULE
|
|
15
|
+
----------------------------------*/
|
|
16
|
+
|
|
17
|
+
type TOptions = {
|
|
18
|
+
side: TAppSide,
|
|
19
|
+
app: App,
|
|
20
|
+
debug?: boolean
|
|
21
|
+
}
|
|
22
|
+
type TRouteDefinition = {
|
|
23
|
+
definition: types.CallExpression,
|
|
24
|
+
dataFetchers: types.ObjectProperty[],
|
|
25
|
+
contextName?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type TFileInfos = {
|
|
29
|
+
path: string,
|
|
30
|
+
process: boolean,
|
|
31
|
+
side: 'front'|'back',
|
|
32
|
+
|
|
33
|
+
importedServices: {[local: string]: string},
|
|
34
|
+
routeDefinitions: TRouteDefinition[],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = (options: TOptions) => (
|
|
38
|
+
[Plugin, options]
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
const clientServices = ['Router'];
|
|
42
|
+
// Others will be called via app.<Service> (backend) or api.post(<path>, <params>) (frontend)
|
|
43
|
+
|
|
44
|
+
const routerMethods = ['get', 'post', 'put', 'delete', 'patch'];
|
|
45
|
+
|
|
46
|
+
/*----------------------------------
|
|
47
|
+
- PLUGIN
|
|
48
|
+
----------------------------------*/
|
|
49
|
+
function Plugin(babel, { app, side, debug }: TOptions) {
|
|
50
|
+
|
|
51
|
+
//debug = true;
|
|
52
|
+
|
|
53
|
+
const t = babel.types as typeof types;
|
|
54
|
+
|
|
55
|
+
type TPluginState = {
|
|
56
|
+
filename: string,
|
|
57
|
+
file: TFileInfos,
|
|
58
|
+
apiInjectedRootFunctions: WeakSet<types.Node>,
|
|
59
|
+
needsUseContextImport: boolean
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/*
|
|
63
|
+
- Wrap route.get(...) with (app: Application) => { }
|
|
64
|
+
- Inject chunk ID into client route options
|
|
65
|
+
- Transform api.fetch:
|
|
66
|
+
|
|
67
|
+
Input:
|
|
68
|
+
const { stats } = api.fetch({
|
|
69
|
+
stats: api.get(...)
|
|
70
|
+
}):
|
|
71
|
+
|
|
72
|
+
Output:
|
|
73
|
+
|
|
74
|
+
Route.page('/', { data: { stats: api.get(...) } });
|
|
75
|
+
...
|
|
76
|
+
const stats = page.data.stats;
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
const plugin: PluginObj<TPluginState> = {
|
|
80
|
+
pre(state) {
|
|
81
|
+
this.filename = state.opts.filename as string;
|
|
82
|
+
|
|
83
|
+
this.file = getFileInfos(this.filename);
|
|
84
|
+
|
|
85
|
+
this.apiInjectedRootFunctions = new WeakSet();
|
|
86
|
+
this.needsUseContextImport = false;
|
|
87
|
+
},
|
|
88
|
+
visitor: {
|
|
89
|
+
// Find @app imports
|
|
90
|
+
// Test: import { Router } from '@app';
|
|
91
|
+
// Replace by: nothing
|
|
92
|
+
ImportDeclaration(path) {
|
|
93
|
+
|
|
94
|
+
const shouldTransformImports = this.file.process;
|
|
95
|
+
if (!shouldTransformImports)
|
|
96
|
+
return;
|
|
97
|
+
|
|
98
|
+
if (path.node.source.value !== '@app')
|
|
99
|
+
return;
|
|
100
|
+
|
|
101
|
+
for (const specifier of path.node.specifiers) {
|
|
102
|
+
|
|
103
|
+
if (specifier.type !== 'ImportSpecifier')
|
|
104
|
+
continue;
|
|
105
|
+
|
|
106
|
+
if (specifier.imported.type !== 'Identifier')
|
|
107
|
+
continue;
|
|
108
|
+
|
|
109
|
+
const serviceName = specifier.imported.name;
|
|
110
|
+
|
|
111
|
+
if (clientServices.includes(serviceName))
|
|
112
|
+
this.file.importedServices[ specifier.local.name ] = serviceName;
|
|
113
|
+
else
|
|
114
|
+
this.file.importedServices[ specifier.local.name ] = 'app';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Remove this import
|
|
118
|
+
path.remove();
|
|
119
|
+
|
|
120
|
+
},
|
|
121
|
+
|
|
122
|
+
// Transform services service calls
|
|
123
|
+
CallExpression(path) {
|
|
124
|
+
|
|
125
|
+
if (!this.file.process)
|
|
126
|
+
return;
|
|
127
|
+
|
|
128
|
+
// object.property()
|
|
129
|
+
const callee = path.node.callee
|
|
130
|
+
if (!(
|
|
131
|
+
callee.type === 'MemberExpression'
|
|
132
|
+
))
|
|
133
|
+
return;
|
|
134
|
+
|
|
135
|
+
// Create full path
|
|
136
|
+
const completePath: string[] = [];
|
|
137
|
+
let currCallee: types.MemberExpression = callee;
|
|
138
|
+
while (1) {
|
|
139
|
+
|
|
140
|
+
if (currCallee.property.type === 'Identifier')
|
|
141
|
+
completePath.unshift(currCallee.property.name);
|
|
142
|
+
|
|
143
|
+
if (currCallee.object.type === 'MemberExpression')
|
|
144
|
+
currCallee = currCallee.object;
|
|
145
|
+
else {
|
|
146
|
+
|
|
147
|
+
if (currCallee.object.type === 'Identifier')
|
|
148
|
+
completePath.unshift(currCallee.object.name);
|
|
149
|
+
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// If we actually call a service
|
|
155
|
+
const serviceName = completePath[0];
|
|
156
|
+
|
|
157
|
+
/*
|
|
158
|
+
Router.page: wrap with export const __register = ({ Router }) => Router.page(...)
|
|
159
|
+
*/
|
|
160
|
+
if (
|
|
161
|
+
serviceName === 'Router'
|
|
162
|
+
&&
|
|
163
|
+
callee.property.type === 'Identifier'
|
|
164
|
+
&&
|
|
165
|
+
['page', 'error', ...routerMethods].includes(callee.property.name)
|
|
166
|
+
) {
|
|
167
|
+
|
|
168
|
+
// Should be at the root of the document
|
|
169
|
+
if (!(
|
|
170
|
+
path.parent.type === 'ExpressionStatement'
|
|
171
|
+
&&
|
|
172
|
+
path.parentPath.parent.type === 'Program'
|
|
173
|
+
))
|
|
174
|
+
return;
|
|
175
|
+
|
|
176
|
+
const routeDef: TRouteDefinition = {
|
|
177
|
+
definition: path.node,
|
|
178
|
+
dataFetchers: []
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Adjust
|
|
182
|
+
// /client/pages/*
|
|
183
|
+
if (this.file.side === 'front') {
|
|
184
|
+
transformDataFetchers(path, this, routeDef);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Add to the list of route definitons to wrap
|
|
188
|
+
this.file.routeDefinitions.push(routeDef);
|
|
189
|
+
|
|
190
|
+
// Delete the route def since it will be replaced by a wrapper
|
|
191
|
+
path.replaceWithMultiple([]);
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
} else if (this.file.side === 'front') {
|
|
195
|
+
|
|
196
|
+
const isAService = (
|
|
197
|
+
serviceName in this.file.importedServices
|
|
198
|
+
&&
|
|
199
|
+
serviceName[0] === serviceName[0].toUpperCase()
|
|
200
|
+
);
|
|
201
|
+
if(!isAService)
|
|
202
|
+
return;
|
|
203
|
+
|
|
204
|
+
/* [client] Backend Service calls: Transform to api.post( <method path>, <params> )
|
|
205
|
+
|
|
206
|
+
Events.Create( form.data ).then(res => toast.success(res.message))
|
|
207
|
+
=>
|
|
208
|
+
api.post( '/api/events/create', form.data ).then(res => toast.success(res.message)).catch(app.handleError)
|
|
209
|
+
*/
|
|
210
|
+
if (side === 'client' && !clientServices.includes(serviceName)) {
|
|
211
|
+
|
|
212
|
+
ensureApiExposedInRootFunction(path, this);
|
|
213
|
+
|
|
214
|
+
// Get complete call path
|
|
215
|
+
const apiPath = '/api/' + completePath.join('/');
|
|
216
|
+
|
|
217
|
+
// Replace by api.post( <method path>, <params> )
|
|
218
|
+
const apiPostArgs: types.CallExpression["arguments"] = [t.stringLiteral(apiPath)];
|
|
219
|
+
if (path.node.arguments.length >= 1)
|
|
220
|
+
apiPostArgs.push( path.node.arguments[0] );
|
|
221
|
+
|
|
222
|
+
path.replaceWith(
|
|
223
|
+
t.callExpression(
|
|
224
|
+
t.memberExpression(
|
|
225
|
+
t.identifier('api'), t.identifier('post')
|
|
226
|
+
), apiPostArgs
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
ensureBackendServicePromiseCatch(path);
|
|
231
|
+
|
|
232
|
+
/* [server] Backend Service calls
|
|
233
|
+
|
|
234
|
+
Events.Create( form.data ).then(res => toast.success(res.message))
|
|
235
|
+
=>
|
|
236
|
+
app.Events.Create( form.data, context ).then(res => toast.success(res.message))
|
|
237
|
+
*/
|
|
238
|
+
} else {
|
|
239
|
+
|
|
240
|
+
// Rebuild member expression from completePath, adding a api prefix
|
|
241
|
+
let newCallee = t.memberExpression(
|
|
242
|
+
t.identifier('app'),
|
|
243
|
+
t.identifier(completePath[0])
|
|
244
|
+
);
|
|
245
|
+
for (let i = 1; i < completePath.length; i++) {
|
|
246
|
+
newCallee = t.memberExpression(
|
|
247
|
+
newCallee,
|
|
248
|
+
t.identifier(completePath[i])
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Replace by app.<service>.<method>(...)
|
|
253
|
+
path.replaceWith(
|
|
254
|
+
t.callExpression(
|
|
255
|
+
newCallee,
|
|
256
|
+
[...path.node.arguments]
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
},
|
|
263
|
+
Program: {
|
|
264
|
+
exit(path, parent) {
|
|
265
|
+
|
|
266
|
+
if (!this.file.process)
|
|
267
|
+
return;
|
|
268
|
+
|
|
269
|
+
ensureUseContextImport(path, this);
|
|
270
|
+
|
|
271
|
+
const wrappedrouteDefs = wrapRouteDefs( this.file );
|
|
272
|
+
if (wrappedrouteDefs)
|
|
273
|
+
path.pushContainer('body', [wrappedrouteDefs])
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function ensureBackendServicePromiseCatch(path: NodePath<types.CallExpression>) {
|
|
280
|
+
|
|
281
|
+
const chainRoot = getPromiseChainRoot(path);
|
|
282
|
+
|
|
283
|
+
// Only append if we are in a promise chain (e.g. .then(...))
|
|
284
|
+
if (chainRoot === path)
|
|
285
|
+
return;
|
|
286
|
+
|
|
287
|
+
if (isCatchWithAppHandleErrorArrow(chainRoot))
|
|
288
|
+
return;
|
|
289
|
+
|
|
290
|
+
if (isCatchWithAppHandleErrorMember(chainRoot)) {
|
|
291
|
+
const updatedCatch = t.callExpression(
|
|
292
|
+
chainRoot.node.callee,
|
|
293
|
+
[buildCatchHandler()]
|
|
294
|
+
);
|
|
295
|
+
chainRoot.replaceWith(updatedCatch);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (isCatchWithAppHandleErrorWrapped(chainRoot)) {
|
|
300
|
+
const updatedCatch = t.callExpression(
|
|
301
|
+
chainRoot.node.callee,
|
|
302
|
+
[buildCatchHandler()]
|
|
303
|
+
);
|
|
304
|
+
chainRoot.replaceWith(updatedCatch);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const newChain = t.callExpression(
|
|
309
|
+
t.memberExpression(chainRoot.node, t.identifier('catch')),
|
|
310
|
+
[buildCatchHandler()]
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
chainRoot.replaceWith(newChain);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function buildCatchHandler(): types.ArrowFunctionExpression {
|
|
317
|
+
const errorIdentifier = t.identifier('e');
|
|
318
|
+
return t.arrowFunctionExpression(
|
|
319
|
+
[errorIdentifier],
|
|
320
|
+
t.callExpression(
|
|
321
|
+
t.memberExpression(t.identifier('app'), t.identifier('handleError')),
|
|
322
|
+
[t.identifier(errorIdentifier.name)]
|
|
323
|
+
)
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function getPromiseChainRoot(path: NodePath<types.CallExpression>): NodePath<types.CallExpression> {
|
|
328
|
+
|
|
329
|
+
let current = path;
|
|
330
|
+
|
|
331
|
+
while (1) {
|
|
332
|
+
|
|
333
|
+
const member = current.parentPath;
|
|
334
|
+
if (!member?.isMemberExpression())
|
|
335
|
+
break;
|
|
336
|
+
|
|
337
|
+
if (member.node.object !== current.node)
|
|
338
|
+
break;
|
|
339
|
+
|
|
340
|
+
if (member.node.property.type !== 'Identifier')
|
|
341
|
+
break;
|
|
342
|
+
|
|
343
|
+
const propName = member.node.property.name;
|
|
344
|
+
if (!['then', 'catch', 'finally'].includes(propName))
|
|
345
|
+
break;
|
|
346
|
+
|
|
347
|
+
const call = member.parentPath;
|
|
348
|
+
if (!call?.isCallExpression())
|
|
349
|
+
break;
|
|
350
|
+
|
|
351
|
+
if (call.node.callee !== member.node)
|
|
352
|
+
break;
|
|
353
|
+
|
|
354
|
+
current = call;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return current;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function isCatchCall(path: NodePath<types.CallExpression>): boolean {
|
|
361
|
+
|
|
362
|
+
const callee = path.node.callee;
|
|
363
|
+
if (callee.type !== 'MemberExpression')
|
|
364
|
+
return false;
|
|
365
|
+
|
|
366
|
+
if (callee.property.type !== 'Identifier' || callee.property.name !== 'catch')
|
|
367
|
+
return false;
|
|
368
|
+
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function isCatchWithAppHandleErrorMember(path: NodePath<types.CallExpression>): boolean {
|
|
373
|
+
|
|
374
|
+
if (!isCatchCall(path))
|
|
375
|
+
return false;
|
|
376
|
+
|
|
377
|
+
if (path.node.arguments.length !== 1)
|
|
378
|
+
return false;
|
|
379
|
+
|
|
380
|
+
const handler = path.node.arguments[0];
|
|
381
|
+
if (handler.type !== 'MemberExpression')
|
|
382
|
+
return false;
|
|
383
|
+
|
|
384
|
+
if (handler.object.type !== 'Identifier' || handler.object.name !== 'app')
|
|
385
|
+
return false;
|
|
386
|
+
|
|
387
|
+
if (handler.property.type !== 'Identifier' || handler.property.name !== 'handleError')
|
|
388
|
+
return false;
|
|
389
|
+
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function isCatchWithAppHandleErrorArrow(path: NodePath<types.CallExpression>): boolean {
|
|
394
|
+
|
|
395
|
+
if (!isCatchCall(path))
|
|
396
|
+
return false;
|
|
397
|
+
|
|
398
|
+
if (path.node.arguments.length !== 1)
|
|
399
|
+
return false;
|
|
400
|
+
|
|
401
|
+
const handler = path.node.arguments[0];
|
|
402
|
+
if (handler.type !== 'ArrowFunctionExpression')
|
|
403
|
+
return false;
|
|
404
|
+
|
|
405
|
+
if (
|
|
406
|
+
handler.params.length !== 1
|
|
407
|
+
||
|
|
408
|
+
handler.params[0].type !== 'Identifier'
|
|
409
|
+
)
|
|
410
|
+
return false;
|
|
411
|
+
|
|
412
|
+
const errorName = handler.params[0].name;
|
|
413
|
+
|
|
414
|
+
if (handler.body.type !== 'CallExpression')
|
|
415
|
+
return false;
|
|
416
|
+
|
|
417
|
+
const call = handler.body;
|
|
418
|
+
if (call.callee.type !== 'MemberExpression')
|
|
419
|
+
return false;
|
|
420
|
+
|
|
421
|
+
if (call.callee.object.type !== 'Identifier' || call.callee.object.name !== 'app')
|
|
422
|
+
return false;
|
|
423
|
+
|
|
424
|
+
if (call.callee.property.type !== 'Identifier' || call.callee.property.name !== 'handleError')
|
|
425
|
+
return false;
|
|
426
|
+
|
|
427
|
+
if (call.arguments.length !== 1)
|
|
428
|
+
return false;
|
|
429
|
+
|
|
430
|
+
const arg = call.arguments[0];
|
|
431
|
+
if (arg.type !== 'Identifier' || arg.name !== errorName)
|
|
432
|
+
return false;
|
|
433
|
+
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function isCatchWithAppHandleErrorWrapped(path: NodePath<types.CallExpression>): boolean {
|
|
438
|
+
|
|
439
|
+
if (!isCatchCall(path))
|
|
440
|
+
return false;
|
|
441
|
+
|
|
442
|
+
if (path.node.arguments.length !== 1)
|
|
443
|
+
return false;
|
|
444
|
+
|
|
445
|
+
const handler = path.node.arguments[0];
|
|
446
|
+
if (handler.type !== 'ArrowFunctionExpression')
|
|
447
|
+
return false;
|
|
448
|
+
|
|
449
|
+
if (
|
|
450
|
+
handler.params.length !== 1
|
|
451
|
+
||
|
|
452
|
+
handler.params[0].type !== 'Identifier'
|
|
453
|
+
)
|
|
454
|
+
return false;
|
|
455
|
+
|
|
456
|
+
const errorName = handler.params[0].name;
|
|
457
|
+
|
|
458
|
+
if (handler.body.type !== 'BlockStatement')
|
|
459
|
+
return false;
|
|
460
|
+
|
|
461
|
+
const statements = handler.body.body;
|
|
462
|
+
if (statements.length < 2)
|
|
463
|
+
return false;
|
|
464
|
+
|
|
465
|
+
const first = statements[0];
|
|
466
|
+
if (!(
|
|
467
|
+
first.type === 'ExpressionStatement'
|
|
468
|
+
&&
|
|
469
|
+
first.expression.type === 'CallExpression'
|
|
470
|
+
&&
|
|
471
|
+
first.expression.callee.type === 'MemberExpression'
|
|
472
|
+
&&
|
|
473
|
+
first.expression.callee.object.type === 'Identifier'
|
|
474
|
+
&&
|
|
475
|
+
first.expression.callee.object.name === 'console'
|
|
476
|
+
&&
|
|
477
|
+
first.expression.callee.property.type === 'Identifier'
|
|
478
|
+
&&
|
|
479
|
+
first.expression.callee.property.name === 'log'
|
|
480
|
+
&&
|
|
481
|
+
first.expression.arguments.length === 2
|
|
482
|
+
&&
|
|
483
|
+
first.expression.arguments[0].type === 'StringLiteral'
|
|
484
|
+
&&
|
|
485
|
+
first.expression.arguments[0].value === 'Error catched'
|
|
486
|
+
&&
|
|
487
|
+
first.expression.arguments[1].type === 'Identifier'
|
|
488
|
+
&&
|
|
489
|
+
first.expression.arguments[1].name === errorName
|
|
490
|
+
))
|
|
491
|
+
return false;
|
|
492
|
+
|
|
493
|
+
const second = statements[1];
|
|
494
|
+
if (!(
|
|
495
|
+
second.type === 'ExpressionStatement'
|
|
496
|
+
&&
|
|
497
|
+
second.expression.type === 'CallExpression'
|
|
498
|
+
&&
|
|
499
|
+
second.expression.callee.type === 'MemberExpression'
|
|
500
|
+
&&
|
|
501
|
+
second.expression.callee.object.type === 'Identifier'
|
|
502
|
+
&&
|
|
503
|
+
second.expression.callee.object.name === 'app'
|
|
504
|
+
&&
|
|
505
|
+
second.expression.callee.property.type === 'Identifier'
|
|
506
|
+
&&
|
|
507
|
+
second.expression.callee.property.name === 'handleError'
|
|
508
|
+
&&
|
|
509
|
+
second.expression.arguments.length === 1
|
|
510
|
+
&&
|
|
511
|
+
second.expression.arguments[0].type === 'Identifier'
|
|
512
|
+
&&
|
|
513
|
+
second.expression.arguments[0].name === errorName
|
|
514
|
+
))
|
|
515
|
+
return false;
|
|
516
|
+
|
|
517
|
+
return true;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function ensureApiExposedInRootFunction(
|
|
521
|
+
path: NodePath<types.CallExpression>,
|
|
522
|
+
pluginState: TPluginState
|
|
523
|
+
) {
|
|
524
|
+
const needsApi = !path.scope.hasBinding('api');
|
|
525
|
+
const needsApp = !path.scope.hasBinding('app');
|
|
526
|
+
if (!needsApi && !needsApp)
|
|
527
|
+
return;
|
|
528
|
+
|
|
529
|
+
const rootFunctionPath = getRootFunctionPath(path);
|
|
530
|
+
if (!rootFunctionPath)
|
|
531
|
+
return;
|
|
532
|
+
|
|
533
|
+
// Root function should be at the program body level (not nested in another function / expression)
|
|
534
|
+
if (rootFunctionPath.getFunctionParent())
|
|
535
|
+
return;
|
|
536
|
+
if (!isProgramBodyLevelFunction(rootFunctionPath))
|
|
537
|
+
return;
|
|
538
|
+
|
|
539
|
+
const existingContextObjectPattern = findUseContextDestructuringObjectPattern(rootFunctionPath);
|
|
540
|
+
if (existingContextObjectPattern) {
|
|
541
|
+
|
|
542
|
+
const existingKeys = new Set(
|
|
543
|
+
existingContextObjectPattern.properties
|
|
544
|
+
.filter((p): p is types.ObjectProperty =>
|
|
545
|
+
p.type === 'ObjectProperty'
|
|
546
|
+
&& p.key.type === 'Identifier'
|
|
547
|
+
)
|
|
548
|
+
.map(p => (p.key as types.Identifier).name)
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
if (needsApi && !existingKeys.has('api')) {
|
|
552
|
+
existingContextObjectPattern.properties.push(
|
|
553
|
+
t.objectProperty(t.identifier('api'), t.identifier('api'), false, true),
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (needsApp && !existingKeys.has('app')) {
|
|
558
|
+
existingContextObjectPattern.properties.push(
|
|
559
|
+
t.objectProperty(t.identifier('app'), t.identifier('app'), false, true),
|
|
560
|
+
);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
pluginState.needsUseContextImport = true;
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (pluginState.apiInjectedRootFunctions.has(rootFunctionPath.node))
|
|
568
|
+
return;
|
|
569
|
+
|
|
570
|
+
const exposeApiDeclaration = t.variableDeclaration('const', [
|
|
571
|
+
t.variableDeclarator(
|
|
572
|
+
t.objectPattern([
|
|
573
|
+
...(needsApi ? [t.objectProperty(t.identifier('api'), t.identifier('api'), false, true)] : []),
|
|
574
|
+
...(needsApp ? [t.objectProperty(t.identifier('app'), t.identifier('app'), false, true)] : []),
|
|
575
|
+
]),
|
|
576
|
+
t.callExpression(t.identifier('useContext'), [])
|
|
577
|
+
)
|
|
578
|
+
]);
|
|
579
|
+
|
|
580
|
+
const body = rootFunctionPath.node.body;
|
|
581
|
+
if (body.type === 'BlockStatement') {
|
|
582
|
+
body.body.unshift(exposeApiDeclaration);
|
|
583
|
+
} else {
|
|
584
|
+
rootFunctionPath.node.body = t.blockStatement([
|
|
585
|
+
exposeApiDeclaration,
|
|
586
|
+
t.returnStatement(body)
|
|
587
|
+
]);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
pluginState.apiInjectedRootFunctions.add(rootFunctionPath.node);
|
|
591
|
+
pluginState.needsUseContextImport = true;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function findUseContextDestructuringObjectPattern(
|
|
595
|
+
rootFunctionPath: NodePath<types.Function | types.ArrowFunctionExpression>
|
|
596
|
+
): types.ObjectPattern | undefined {
|
|
597
|
+
|
|
598
|
+
const body = rootFunctionPath.node.body;
|
|
599
|
+
if (body.type !== 'BlockStatement')
|
|
600
|
+
return;
|
|
601
|
+
|
|
602
|
+
for (const stmt of body.body) {
|
|
603
|
+
if (stmt.type !== 'VariableDeclaration' || stmt.kind !== 'const')
|
|
604
|
+
continue;
|
|
605
|
+
|
|
606
|
+
for (const declarator of stmt.declarations) {
|
|
607
|
+
if (declarator.id.type !== 'ObjectPattern')
|
|
608
|
+
continue;
|
|
609
|
+
if (!declarator.init || declarator.init.type !== 'CallExpression')
|
|
610
|
+
continue;
|
|
611
|
+
if (declarator.init.callee.type !== 'Identifier' || declarator.init.callee.name !== 'useContext')
|
|
612
|
+
continue;
|
|
613
|
+
if (declarator.init.arguments.length !== 0)
|
|
614
|
+
continue;
|
|
615
|
+
return declarator.id;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function getRootFunctionPath(path: NodePath): NodePath<types.Function | types.ArrowFunctionExpression> | undefined {
|
|
621
|
+
|
|
622
|
+
let functionPath = path.getFunctionParent();
|
|
623
|
+
if (!functionPath)
|
|
624
|
+
return;
|
|
625
|
+
|
|
626
|
+
// Only support plain functions / arrow functions (no class/object methods)
|
|
627
|
+
if (!(
|
|
628
|
+
functionPath.isFunctionDeclaration()
|
|
629
|
+
|| functionPath.isFunctionExpression()
|
|
630
|
+
|| functionPath.isArrowFunctionExpression()
|
|
631
|
+
))
|
|
632
|
+
return;
|
|
633
|
+
|
|
634
|
+
let parentFunction = functionPath.getFunctionParent();
|
|
635
|
+
while (parentFunction) {
|
|
636
|
+
|
|
637
|
+
if (!(
|
|
638
|
+
parentFunction.isFunctionDeclaration()
|
|
639
|
+
|| parentFunction.isFunctionExpression()
|
|
640
|
+
|| parentFunction.isArrowFunctionExpression()
|
|
641
|
+
))
|
|
642
|
+
break;
|
|
643
|
+
|
|
644
|
+
functionPath = parentFunction;
|
|
645
|
+
parentFunction = functionPath.getFunctionParent();
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
return functionPath;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
function isProgramBodyLevelFunction(path: NodePath): boolean {
|
|
652
|
+
|
|
653
|
+
const parent = path.parentPath;
|
|
654
|
+
if (!parent)
|
|
655
|
+
return false;
|
|
656
|
+
|
|
657
|
+
// function Foo() {}
|
|
658
|
+
if (parent.isProgram())
|
|
659
|
+
return true;
|
|
660
|
+
|
|
661
|
+
// export default function Foo() {} / export default () => {}
|
|
662
|
+
if (
|
|
663
|
+
parent.isExportDefaultDeclaration()
|
|
664
|
+
&&
|
|
665
|
+
parent.parentPath?.isProgram()
|
|
666
|
+
)
|
|
667
|
+
return true;
|
|
668
|
+
|
|
669
|
+
// export const Foo = () => {}
|
|
670
|
+
if (
|
|
671
|
+
parent.isExportNamedDeclaration()
|
|
672
|
+
&&
|
|
673
|
+
parent.parentPath?.isProgram()
|
|
674
|
+
)
|
|
675
|
+
return true;
|
|
676
|
+
|
|
677
|
+
// const Foo = () => {} (top-level) / export const Foo = () => {}
|
|
678
|
+
if (parent.isVariableDeclarator()) {
|
|
679
|
+
|
|
680
|
+
const declaration = parent.parentPath;
|
|
681
|
+
if (!declaration?.isVariableDeclaration())
|
|
682
|
+
return false;
|
|
683
|
+
|
|
684
|
+
const declarationParent = declaration.parentPath;
|
|
685
|
+
if (!declarationParent)
|
|
686
|
+
return false;
|
|
687
|
+
|
|
688
|
+
if (declarationParent.isProgram())
|
|
689
|
+
return true;
|
|
690
|
+
|
|
691
|
+
if (
|
|
692
|
+
declarationParent.isExportNamedDeclaration()
|
|
693
|
+
&&
|
|
694
|
+
declarationParent.parentPath?.isProgram()
|
|
695
|
+
)
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
function ensureUseContextImport(path: NodePath<types.Program>, pluginState: TPluginState) {
|
|
703
|
+
|
|
704
|
+
if (!pluginState.needsUseContextImport)
|
|
705
|
+
return;
|
|
706
|
+
|
|
707
|
+
const body = path.node.body;
|
|
708
|
+
|
|
709
|
+
// Already imported as a value import
|
|
710
|
+
for (const stmt of body) {
|
|
711
|
+
if (
|
|
712
|
+
stmt.type === 'ImportDeclaration'
|
|
713
|
+
&&
|
|
714
|
+
stmt.source.value === '@/client/context'
|
|
715
|
+
&&
|
|
716
|
+
stmt.importKind !== 'type'
|
|
717
|
+
&&
|
|
718
|
+
stmt.specifiers.some(s =>
|
|
719
|
+
s.type === 'ImportDefaultSpecifier' && s.local.name === 'useContext'
|
|
720
|
+
)
|
|
721
|
+
)
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// Try to reuse an existing value import from the same module
|
|
726
|
+
for (const stmt of body) {
|
|
727
|
+
if (
|
|
728
|
+
stmt.type !== 'ImportDeclaration'
|
|
729
|
+
||
|
|
730
|
+
stmt.source.value !== '@/client/context'
|
|
731
|
+
||
|
|
732
|
+
stmt.importKind === 'type'
|
|
733
|
+
)
|
|
734
|
+
continue;
|
|
735
|
+
|
|
736
|
+
const hasDefaultImport = stmt.specifiers.some(s => s.type === 'ImportDefaultSpecifier');
|
|
737
|
+
if (!hasDefaultImport) {
|
|
738
|
+
stmt.specifiers.unshift(
|
|
739
|
+
t.importDefaultSpecifier(t.identifier('useContext'))
|
|
740
|
+
);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Otherwise, add a new import (placed after existing imports)
|
|
746
|
+
const importDeclaration = t.importDeclaration(
|
|
747
|
+
[t.importDefaultSpecifier(t.identifier('useContext'))],
|
|
748
|
+
t.stringLiteral('@/client/context')
|
|
749
|
+
);
|
|
750
|
+
|
|
751
|
+
let insertIndex = 0;
|
|
752
|
+
while (insertIndex < body.length && body[insertIndex].type === 'ImportDeclaration')
|
|
753
|
+
insertIndex++;
|
|
754
|
+
|
|
755
|
+
body.splice(insertIndex, 0, importDeclaration);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function getFileInfos( filename: string ): TFileInfos {
|
|
759
|
+
|
|
760
|
+
const file: TFileInfos = {
|
|
761
|
+
process: true,
|
|
762
|
+
side: 'back',
|
|
763
|
+
path: filename,
|
|
764
|
+
importedServices: {},
|
|
765
|
+
routeDefinitions: []
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Relative path
|
|
769
|
+
let relativeFileName: string | undefined;
|
|
770
|
+
if (filename.startsWith( cli.paths.appRoot ))
|
|
771
|
+
relativeFileName = filename.substring( cli.paths.appRoot.length );
|
|
772
|
+
if (filename.startsWith( cli.paths.coreRoot ))
|
|
773
|
+
relativeFileName = filename.substring( cli.paths.coreRoot.length );
|
|
774
|
+
|
|
775
|
+
// The file isn't a route definition
|
|
776
|
+
if (relativeFileName === undefined) {
|
|
777
|
+
file.process = false;
|
|
778
|
+
return file;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// Differenciate back / front
|
|
782
|
+
if (relativeFileName.startsWith('/client/pages') || relativeFileName.startsWith('/client/components') || relativeFileName.startsWith('/client/hooks')) {
|
|
783
|
+
file.side = 'front';
|
|
784
|
+
} else if (relativeFileName.startsWith('/server/routes')) {
|
|
785
|
+
file.side = 'back';
|
|
786
|
+
} else
|
|
787
|
+
file.process = false;
|
|
788
|
+
|
|
789
|
+
return file
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
function transformDataFetchers(
|
|
793
|
+
path: NodePath<types.CallExpression>,
|
|
794
|
+
routerDefContext: TPluginState,
|
|
795
|
+
routeDef: TRouteDefinition
|
|
796
|
+
) {
|
|
797
|
+
path.traverse({
|
|
798
|
+
// api.load => move data fetchers to route.options.data
|
|
799
|
+
// So the router is able to load data before rendering the component
|
|
800
|
+
CallExpression(path) {
|
|
801
|
+
|
|
802
|
+
const callee = path.node.callee
|
|
803
|
+
|
|
804
|
+
// Detect api.fetch
|
|
805
|
+
if (!(
|
|
806
|
+
callee.type === 'MemberExpression'
|
|
807
|
+
&&
|
|
808
|
+
callee.object.type === 'Identifier'
|
|
809
|
+
&&
|
|
810
|
+
callee.property.type === 'Identifier'
|
|
811
|
+
&&
|
|
812
|
+
callee.object.name === 'api'
|
|
813
|
+
&&
|
|
814
|
+
callee.property.name === 'fetch'
|
|
815
|
+
))
|
|
816
|
+
return;
|
|
817
|
+
|
|
818
|
+
/* Reference data fetchers
|
|
819
|
+
{
|
|
820
|
+
stats: api.get(...)
|
|
821
|
+
}
|
|
822
|
+
*/
|
|
823
|
+
routeDef.dataFetchers.push(
|
|
824
|
+
...path.node.arguments[0].properties.map(p => {
|
|
825
|
+
|
|
826
|
+
// Server side: Pass request context as 2nd argument
|
|
827
|
+
// companies: Companies.create( <params>, context )
|
|
828
|
+
if (
|
|
829
|
+
side === 'server'
|
|
830
|
+
&&
|
|
831
|
+
p.type === 'ObjectProperty'
|
|
832
|
+
&&
|
|
833
|
+
p.key.type === 'Identifier'
|
|
834
|
+
&&
|
|
835
|
+
p.value.type === 'CallExpression'
|
|
836
|
+
&&
|
|
837
|
+
// TODO: reliable way to know if it's a service
|
|
838
|
+
!(
|
|
839
|
+
p.value.callee.type === 'MemberExpression'
|
|
840
|
+
&&
|
|
841
|
+
p.value.callee.object.type === 'Identifier'
|
|
842
|
+
&&
|
|
843
|
+
p.value.callee.object.name === 'api'
|
|
844
|
+
)
|
|
845
|
+
) {
|
|
846
|
+
|
|
847
|
+
// Pass request context as 2nd argument
|
|
848
|
+
p.value.arguments = p.value.arguments.length === 0
|
|
849
|
+
? [ t.objectExpression([]), t.identifier('context') ]
|
|
850
|
+
: [ p.value.arguments[0], t.identifier('context') ];
|
|
851
|
+
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
return p;
|
|
855
|
+
|
|
856
|
+
})
|
|
857
|
+
);
|
|
858
|
+
|
|
859
|
+
/* Replace the:
|
|
860
|
+
const { stats } = api.fetch({
|
|
861
|
+
stats: api.get(...)
|
|
862
|
+
})
|
|
863
|
+
|
|
864
|
+
by:
|
|
865
|
+
const { stats } = context.data.stats;
|
|
866
|
+
*/
|
|
867
|
+
path.replaceWith(
|
|
868
|
+
t.memberExpression(
|
|
869
|
+
t.identifier( routeDef.contextName || 'context' ),
|
|
870
|
+
t.identifier('data'),
|
|
871
|
+
)
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
}, routerDefContext);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function enrichRouteOptions(
|
|
878
|
+
routeDef: TRouteDefinition,
|
|
879
|
+
routeArgs: types.CallExpression["arguments"],
|
|
880
|
+
filename: string
|
|
881
|
+
): types.CallExpression["arguments"] | 'ALREADY_PROCESSED' {
|
|
882
|
+
|
|
883
|
+
// Extract client route definition arguments
|
|
884
|
+
let routeOptions: types.ObjectExpression | undefined;
|
|
885
|
+
let renderer: types.ArrowFunctionExpression;
|
|
886
|
+
if (routeArgs.length === 1)
|
|
887
|
+
([ renderer ] = routeArgs);
|
|
888
|
+
else
|
|
889
|
+
([ routeOptions, renderer ] = routeArgs);
|
|
890
|
+
|
|
891
|
+
// Generate page chunk id
|
|
892
|
+
const { filepath, chunkId } = cli.paths.getPageChunk(app, filename);
|
|
893
|
+
debug && console.log(`[routes]`, filename.replace(cli.paths.appRoot + '/client/pages', ''));
|
|
894
|
+
|
|
895
|
+
// Create new options to add in route.options
|
|
896
|
+
const newProperties = [
|
|
897
|
+
t.objectProperty(
|
|
898
|
+
t.identifier('id'),
|
|
899
|
+
t.stringLiteral(chunkId)
|
|
900
|
+
),
|
|
901
|
+
t.objectProperty(
|
|
902
|
+
t.identifier('filepath'),
|
|
903
|
+
t.stringLiteral(filepath)
|
|
904
|
+
),
|
|
905
|
+
]
|
|
906
|
+
|
|
907
|
+
// Add data fetchers
|
|
908
|
+
if (routeDef.dataFetchers.length !== 0) {
|
|
909
|
+
|
|
910
|
+
const rendererContext = t.cloneNode( renderer.params[0] );
|
|
911
|
+
// If not already present, add context to the 1st argument (a object spread)
|
|
912
|
+
if (!rendererContext.properties.some( p => p.key.name === 'context' ))
|
|
913
|
+
rendererContext.properties.push(
|
|
914
|
+
t.objectProperty(
|
|
915
|
+
t.identifier('context'),
|
|
916
|
+
t.identifier('context')
|
|
917
|
+
)
|
|
918
|
+
)
|
|
919
|
+
|
|
920
|
+
// (contollerParams) => { stats: api.get(...) }
|
|
921
|
+
const dataFetchersFunc = t.arrowFunctionExpression(
|
|
922
|
+
[rendererContext],
|
|
923
|
+
t.objectExpression(
|
|
924
|
+
routeDef.dataFetchers.map( df => t.cloneNode( df ))
|
|
925
|
+
)
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
// Add the data fetchers to options.data
|
|
929
|
+
newProperties.push(
|
|
930
|
+
t.objectProperty(
|
|
931
|
+
t.identifier('data'),
|
|
932
|
+
dataFetchersFunc
|
|
933
|
+
)
|
|
934
|
+
);
|
|
935
|
+
|
|
936
|
+
// Expose the context variable in the renderer
|
|
937
|
+
exposeContextProperty( renderer, routeDef );
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if (routeOptions?.properties === undefined)
|
|
941
|
+
return [
|
|
942
|
+
t.objectExpression(newProperties),
|
|
943
|
+
renderer
|
|
944
|
+
]
|
|
945
|
+
|
|
946
|
+
// Test if the route options were not already processed
|
|
947
|
+
const wasAlreadyProcessed = routeOptions.properties.some( o =>
|
|
948
|
+
o.type === 'ObjectProperty'
|
|
949
|
+
&&
|
|
950
|
+
o.key.type === 'Identifier'
|
|
951
|
+
&&
|
|
952
|
+
o.key.name === 'id'
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
if (wasAlreadyProcessed) {
|
|
956
|
+
// Cancel processing
|
|
957
|
+
debug && console.log(`[routes]`, filename, 'Already Processed');
|
|
958
|
+
return 'ALREADY_PROCESSED';
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// Create the new options object
|
|
962
|
+
return [
|
|
963
|
+
t.objectExpression([
|
|
964
|
+
...routeOptions.properties,
|
|
965
|
+
...newProperties
|
|
966
|
+
]),
|
|
967
|
+
renderer
|
|
968
|
+
]
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function exposeContextProperty(
|
|
972
|
+
renderer: types.ArrowFunctionExpression,
|
|
973
|
+
routeDef: TRouteDefinition
|
|
974
|
+
) {
|
|
975
|
+
const contextParam = renderer.params[0];
|
|
976
|
+
if (contextParam?.type === 'ObjectPattern') {
|
|
977
|
+
|
|
978
|
+
for (const property of contextParam.properties) {
|
|
979
|
+
if (
|
|
980
|
+
property.type === 'ObjectProperty'
|
|
981
|
+
&&
|
|
982
|
+
property.key.type === 'Identifier'
|
|
983
|
+
&&
|
|
984
|
+
property.key.name === 'context'
|
|
985
|
+
&&
|
|
986
|
+
property.value.type === 'Identifier'
|
|
987
|
+
) {
|
|
988
|
+
|
|
989
|
+
routeDef.contextName = property.value.name;
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
if (!routeDef.contextName) {
|
|
995
|
+
routeDef.contextName = 'context';
|
|
996
|
+
contextParam.properties.push(
|
|
997
|
+
t.objectProperty( t.identifier('context'), t.identifier( routeDef.contextName ) )
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
} else if (contextParam?.type === 'Identifier') {
|
|
1002
|
+
console.log("routeDef.contextName", routeDef.contextName);
|
|
1003
|
+
routeDef.contextName = contextParam.name;
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function wrapRouteDefs( file: TFileInfos ) {
|
|
1008
|
+
|
|
1009
|
+
const importedServicesList = Object.entries(file.importedServices);
|
|
1010
|
+
if (importedServicesList.length === 0)
|
|
1011
|
+
return;
|
|
1012
|
+
|
|
1013
|
+
const definitions: types.BlockStatement["body"] = [];
|
|
1014
|
+
if (file.side === 'front') {
|
|
1015
|
+
|
|
1016
|
+
// Limit to one route def per file
|
|
1017
|
+
const routesDefCount = file.routeDefinitions.length;
|
|
1018
|
+
if (routesDefCount === 0)
|
|
1019
|
+
return;
|
|
1020
|
+
else if (routesDefCount > 1)
|
|
1021
|
+
throw new Error(`Frontend route definition files (/client/pages/**/**.ts) can contain only one route definition.
|
|
1022
|
+
${routesDefCount} were given in ${file.path}.`);
|
|
1023
|
+
|
|
1024
|
+
const routeDef = file.routeDefinitions[0];
|
|
1025
|
+
|
|
1026
|
+
// Client route definition: Add chunk id
|
|
1027
|
+
let [routePath, ...routeArgs] = routeDef.definition.arguments;
|
|
1028
|
+
const callee = routeDef.definition.callee;
|
|
1029
|
+
|
|
1030
|
+
if (callee.object.name === 'Router') {
|
|
1031
|
+
|
|
1032
|
+
// Inject chunk id in options (2nd arg)
|
|
1033
|
+
const newRouteArgs = enrichRouteOptions(routeDef, routeArgs, file.path);
|
|
1034
|
+
if (newRouteArgs === 'ALREADY_PROCESSED')
|
|
1035
|
+
return;
|
|
1036
|
+
|
|
1037
|
+
routeArgs = newRouteArgs;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// Force babel to create new fresh nodes
|
|
1041
|
+
// If we directy use statementParent, it will not be included in the final compiler code
|
|
1042
|
+
definitions.push(
|
|
1043
|
+
t.returnStatement(
|
|
1044
|
+
t.callExpression(
|
|
1045
|
+
t.memberExpression(
|
|
1046
|
+
t.identifier( callee.object.name ),
|
|
1047
|
+
callee.property,
|
|
1048
|
+
),
|
|
1049
|
+
[routePath, ...routeArgs]
|
|
1050
|
+
)
|
|
1051
|
+
)
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
} else {
|
|
1055
|
+
|
|
1056
|
+
definitions.push(
|
|
1057
|
+
// Without spread = react jxx need additionnal loader
|
|
1058
|
+
...file.routeDefinitions.map( def =>
|
|
1059
|
+
t.expressionStatement(def.definition)
|
|
1060
|
+
),
|
|
1061
|
+
)
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
/*
|
|
1065
|
+
({ Router, app: { Events } }}) => {
|
|
1066
|
+
...
|
|
1067
|
+
}
|
|
1068
|
+
*/
|
|
1069
|
+
const appSpread: types.ObjectProperty[] = []
|
|
1070
|
+
const servicesSpread: types.ObjectProperty[] = [
|
|
1071
|
+
t.objectProperty(
|
|
1072
|
+
t.identifier('app'),
|
|
1073
|
+
t.identifier('app'),
|
|
1074
|
+
),
|
|
1075
|
+
t.objectProperty(
|
|
1076
|
+
t.identifier('context'),
|
|
1077
|
+
t.identifier('context'),
|
|
1078
|
+
)
|
|
1079
|
+
]
|
|
1080
|
+
for (const [local, imported] of importedServicesList) {
|
|
1081
|
+
if (imported === 'app')
|
|
1082
|
+
appSpread.push(
|
|
1083
|
+
t.objectProperty(
|
|
1084
|
+
t.identifier(local),
|
|
1085
|
+
t.identifier(local),
|
|
1086
|
+
)
|
|
1087
|
+
)
|
|
1088
|
+
else
|
|
1089
|
+
servicesSpread.push(
|
|
1090
|
+
t.objectProperty(
|
|
1091
|
+
t.identifier(local),
|
|
1092
|
+
t.identifier(imported),
|
|
1093
|
+
)
|
|
1094
|
+
)
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
// export const __register = ({ Router, app }) => { ... }
|
|
1098
|
+
const exportDeclaration = t.exportNamedDeclaration(
|
|
1099
|
+
t.variableDeclaration('const', [
|
|
1100
|
+
t.variableDeclarator(
|
|
1101
|
+
t.identifier('__register'),
|
|
1102
|
+
t.arrowFunctionExpression(
|
|
1103
|
+
[
|
|
1104
|
+
t.objectPattern(servicesSpread)
|
|
1105
|
+
],
|
|
1106
|
+
t.blockStatement([
|
|
1107
|
+
// const { Events } = app;
|
|
1108
|
+
t.variableDeclaration('const', [
|
|
1109
|
+
t.variableDeclarator(
|
|
1110
|
+
t.objectPattern(appSpread),
|
|
1111
|
+
t.identifier('app')
|
|
1112
|
+
)
|
|
1113
|
+
]),
|
|
1114
|
+
// Router.post(....)
|
|
1115
|
+
...definitions,
|
|
1116
|
+
])
|
|
1117
|
+
)
|
|
1118
|
+
)
|
|
1119
|
+
])
|
|
1120
|
+
);
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
// (file.path.includes('clients/prospect/search') && side === 'client')
|
|
1124
|
+
// && console.log( file.path, generate(exportDeclaration).code );
|
|
1125
|
+
|
|
1126
|
+
return exportDeclaration;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
return plugin;
|
|
1130
|
+
}
|