proteum 2.4.3 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -55
- package/agents/project/AGENTS.md +112 -31
- package/agents/project/CODING_STYLE.md +2 -2
- package/agents/project/app-root/AGENTS.md +1 -3
- package/agents/project/client/AGENTS.md +1 -1
- package/agents/project/client/pages/AGENTS.md +21 -9
- package/agents/project/diagnostics.md +2 -2
- package/agents/project/optimizations.md +1 -1
- package/agents/project/root/AGENTS.md +105 -22
- package/agents/project/server/routes/AGENTS.md +30 -1
- package/agents/project/tests/AGENTS.md +1 -1
- package/cli/commands/doctor.ts +54 -3
- package/cli/commands/runtime.ts +6 -0
- package/cli/commands/worktree.ts +116 -0
- package/cli/compiler/artifacts/controllers.ts +16 -15
- package/cli/compiler/artifacts/discovery.ts +129 -17
- package/cli/compiler/artifacts/routing.ts +0 -5
- package/cli/compiler/artifacts/services.ts +253 -76
- package/cli/compiler/common/controllers.ts +159 -57
- package/cli/compiler/common/generatedRouteModules.ts +457 -363
- package/cli/mcp/router.ts +47 -3
- package/cli/presentation/commands.ts +25 -15
- package/cli/runtime/commands.ts +39 -12
- package/cli/runtime/worktreeBootstrap.ts +608 -0
- package/cli/scaffold/index.ts +28 -18
- package/cli/scaffold/templates.ts +44 -33
- package/cli/utils/agents.ts +14 -1
- package/client/services/router/index.tsx +23 -3
- package/client/services/router/request/api.ts +14 -4
- package/common/dev/contractsDoctor.ts +1 -1
- package/common/dev/mcpPayloads.ts +8 -1
- package/common/env/proteumEnv.ts +14 -2
- package/common/router/contracts.ts +1 -1
- package/common/router/definitions.ts +177 -0
- package/common/router/index.ts +23 -12
- package/common/router/pageData.ts +5 -5
- package/common/router/register.ts +2 -2
- package/common/router/request/api.ts +12 -2
- package/docs/agent-routing.md +5 -2
- package/docs/diagnostics.md +2 -0
- package/docs/mcp.md +6 -3
- package/eslint.js +36 -1
- package/package.json +1 -1
- package/server/app/commands.ts +5 -1
- package/server/app/container/console/http-client-error-context.test.cjs +10 -1
- package/server/app/container/console/index.ts +2 -1
- package/server/app/controller/index.ts +98 -40
- package/server/app/index.ts +92 -1
- package/server/app/service/index.ts +5 -1
- package/server/index.ts +6 -2
- package/server/services/router/index.ts +47 -38
- package/server/services/router/response/index.ts +2 -2
- package/tests/agents-utils.test.cjs +14 -1
- package/tests/cli-mcp-command.test.cjs +84 -0
- package/tests/definition-contracts.test.cjs +453 -0
- package/tests/dev-transpile-watch.test.cjs +37 -28
- package/tests/eslint-rules.test.cjs +39 -1
- package/tests/mcp.test.cjs +90 -0
- package/tests/worktree-bootstrap.test.cjs +206 -0
- package/types/aliases.d.ts +0 -5
- package/types/controller-input.test.ts +23 -17
- package/types/controller-request-context.test.ts +10 -11
- package/cli/commands/migrate.ts +0 -51
- package/cli/migrate/pageContract.ts +0 -516
- package/docs/migrate-from-2.1.3.md +0 -396
- package/scripts/cleanup-generated-controllers.ts +0 -62
- package/scripts/fix-reference-app-typing.ts +0 -490
- package/scripts/format-router-registrations.ts +0 -119
- package/scripts/migrate-explicit-controllers-and-request.ts +0 -423
- package/scripts/refactor-client-app-imports.ts +0 -244
- package/scripts/refactor-client-pages.ts +0 -587
- package/scripts/refactor-server-controllers.ts +0 -471
- package/scripts/refactor-server-runtime-aliases.ts +0 -360
- package/scripts/restore-client-app-import-files.ts +0 -41
|
@@ -1,587 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { parse } from '@babel/parser';
|
|
4
|
-
import traverse, { NodePath } from '@babel/traverse';
|
|
5
|
-
import generate from '@babel/generator';
|
|
6
|
-
import * as types from '@babel/types';
|
|
7
|
-
|
|
8
|
-
const findFiles = (dir: string): string[] => {
|
|
9
|
-
if (!fs.existsSync(dir)) return [];
|
|
10
|
-
|
|
11
|
-
const files: string[] = [];
|
|
12
|
-
|
|
13
|
-
for (const dirent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
14
|
-
const filepath = path.join(dir, dirent.name);
|
|
15
|
-
|
|
16
|
-
if (dirent.isDirectory()) {
|
|
17
|
-
files.push(...findFiles(filepath));
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (dirent.isFile() && /\.(tsx?|jsx?)$/.test(filepath)) files.push(filepath);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return files;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
const ensureRouterImport = (programPath: NodePath<types.Program>) => {
|
|
28
|
-
const hasRouterImport = programPath.node.body.some(
|
|
29
|
-
(statement) =>
|
|
30
|
-
statement.type === 'ImportDeclaration' &&
|
|
31
|
-
statement.source.value === '@/client/router' &&
|
|
32
|
-
statement.specifiers.some(
|
|
33
|
-
(specifier) => specifier.type === 'ImportDefaultSpecifier' && specifier.local.name === 'Router',
|
|
34
|
-
),
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
if (!hasRouterImport) {
|
|
38
|
-
programPath.unshiftContainer(
|
|
39
|
-
'body',
|
|
40
|
-
types.importDeclaration(
|
|
41
|
-
[types.importDefaultSpecifier(types.identifier('Router'))],
|
|
42
|
-
types.stringLiteral('@/client/router'),
|
|
43
|
-
),
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const ensureBlockBody = (
|
|
49
|
-
functionPath: NodePath<types.FunctionExpression | types.ArrowFunctionExpression | types.FunctionDeclaration>,
|
|
50
|
-
) => {
|
|
51
|
-
if (functionPath.isArrowFunctionExpression() && functionPath.node.body.type !== 'BlockStatement') {
|
|
52
|
-
functionPath.node.body = types.blockStatement([types.returnStatement(functionPath.node.body)]);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return functionPath.get('body') as NodePath<types.BlockStatement>;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const ensureObjectPatternParam = (
|
|
59
|
-
functionPath: NodePath<types.FunctionExpression | types.ArrowFunctionExpression | types.FunctionDeclaration>,
|
|
60
|
-
) => {
|
|
61
|
-
if (!functionPath.node.params.length) {
|
|
62
|
-
functionPath.node.params = [types.objectPattern([])];
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const firstParam = functionPath.node.params[0];
|
|
66
|
-
if (firstParam.type === 'ObjectPattern') return firstParam;
|
|
67
|
-
|
|
68
|
-
return null;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const findObjectPatternParam = (
|
|
72
|
-
functionNode: types.FunctionExpression | types.ArrowFunctionExpression | types.FunctionDeclaration,
|
|
73
|
-
) => {
|
|
74
|
-
const firstParam = functionNode.params[0];
|
|
75
|
-
|
|
76
|
-
if (!firstParam || firstParam.type !== 'ObjectPattern') return null;
|
|
77
|
-
|
|
78
|
-
return firstParam;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const getReturnedObjectExpression = (
|
|
82
|
-
functionNode: types.FunctionExpression | types.ArrowFunctionExpression | types.FunctionDeclaration,
|
|
83
|
-
) => {
|
|
84
|
-
if (functionNode.body.type === 'ObjectExpression') return functionNode.body;
|
|
85
|
-
|
|
86
|
-
if (functionNode.body.type !== 'BlockStatement') return null;
|
|
87
|
-
|
|
88
|
-
for (const statement of functionNode.body.body) {
|
|
89
|
-
if (statement.type === 'ReturnStatement' && statement.argument?.type === 'ObjectExpression')
|
|
90
|
-
return statement.argument;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return null;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const objectPatternHasProperty = (pattern: types.ObjectPattern, localName: string) =>
|
|
97
|
-
pattern.properties.some(
|
|
98
|
-
(property) =>
|
|
99
|
-
property.type === 'ObjectProperty' &&
|
|
100
|
-
property.value.type === 'Identifier' &&
|
|
101
|
-
property.value.name === localName,
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
const addObjectPatternProperty = (pattern: types.ObjectPattern, keyName: string, localName: string = keyName) => {
|
|
105
|
-
if (objectPatternHasProperty(pattern, localName)) return;
|
|
106
|
-
|
|
107
|
-
pattern.properties.push(
|
|
108
|
-
types.objectProperty(types.identifier(keyName), types.identifier(localName), false, keyName === localName),
|
|
109
|
-
);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const getObjectPropertyKeyName = (property: types.ObjectProperty) => {
|
|
113
|
-
if (property.key.type === 'Identifier') return property.key.name;
|
|
114
|
-
|
|
115
|
-
if (property.key.type === 'StringLiteral') return property.key.value;
|
|
116
|
-
|
|
117
|
-
return null;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const pushUniqueObjectProperty = (
|
|
121
|
-
properties: (types.ObjectProperty | types.SpreadElement)[],
|
|
122
|
-
property: types.ObjectProperty,
|
|
123
|
-
) => {
|
|
124
|
-
const nextKey = getObjectPropertyKeyName(property);
|
|
125
|
-
if (!nextKey) {
|
|
126
|
-
properties.push(property);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const existingIndex = properties.findIndex(
|
|
131
|
-
(existingProperty) =>
|
|
132
|
-
existingProperty.type === 'ObjectProperty' && getObjectPropertyKeyName(existingProperty) === nextKey,
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
if (existingIndex >= 0) {
|
|
136
|
-
properties[existingIndex] = property;
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
properties.push(property);
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
const getObjectPatternPropertyLocalName = (property: types.ObjectProperty) => {
|
|
144
|
-
if (property.value.type === 'Identifier') return property.value.name;
|
|
145
|
-
|
|
146
|
-
if (property.value.type === 'AssignmentPattern' && property.value.left.type === 'Identifier')
|
|
147
|
-
return property.value.left.name;
|
|
148
|
-
|
|
149
|
-
return null;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const pushUniqueObjectPatternProperty = (properties: types.ObjectProperty[], property: types.ObjectProperty) => {
|
|
153
|
-
const nextLocalName = getObjectPatternPropertyLocalName(property);
|
|
154
|
-
const nextKeyName = getObjectPropertyKeyName(property);
|
|
155
|
-
|
|
156
|
-
const existingIndex = properties.findIndex(
|
|
157
|
-
(existingProperty) =>
|
|
158
|
-
(nextLocalName && getObjectPatternPropertyLocalName(existingProperty) === nextLocalName) ||
|
|
159
|
-
(nextKeyName && getObjectPropertyKeyName(existingProperty) === nextKeyName),
|
|
160
|
-
);
|
|
161
|
-
|
|
162
|
-
if (existingIndex >= 0) {
|
|
163
|
-
properties[existingIndex] = property;
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
properties.push(property);
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const unwrapFetchExpression = (init: types.Expression | null | undefined) => {
|
|
171
|
-
const wrappers: Array<(expression: types.Expression) => types.Expression> = [];
|
|
172
|
-
let current = init || null;
|
|
173
|
-
|
|
174
|
-
while (current) {
|
|
175
|
-
if (current.type === 'ParenthesizedExpression') {
|
|
176
|
-
wrappers.push((expression) => types.parenthesizedExpression(expression));
|
|
177
|
-
current = current.expression;
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (current.type === 'TSAsExpression') {
|
|
182
|
-
const typeAnnotation = current.typeAnnotation;
|
|
183
|
-
wrappers.push((expression) => types.tsAsExpression(expression, typeAnnotation));
|
|
184
|
-
current = current.expression;
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (current.type === 'TSTypeAssertion') {
|
|
189
|
-
const typeAnnotation = current.typeAnnotation;
|
|
190
|
-
wrappers.push((expression) => types.tsTypeAssertion(typeAnnotation, expression));
|
|
191
|
-
current = current.expression;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (current.type === 'TSNonNullExpression') {
|
|
196
|
-
wrappers.push((expression) => types.tsNonNullExpression(expression));
|
|
197
|
-
current = current.expression;
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (
|
|
205
|
-
!current ||
|
|
206
|
-
current.type !== 'CallExpression' ||
|
|
207
|
-
current.callee.type !== 'MemberExpression' ||
|
|
208
|
-
current.callee.object.type !== 'Identifier' ||
|
|
209
|
-
current.callee.object.name !== 'api' ||
|
|
210
|
-
current.callee.property.type !== 'Identifier' ||
|
|
211
|
-
current.callee.property.name !== 'fetch' ||
|
|
212
|
-
current.arguments[0]?.type !== 'ObjectExpression'
|
|
213
|
-
)
|
|
214
|
-
return null;
|
|
215
|
-
|
|
216
|
-
return {
|
|
217
|
-
callExpression: current,
|
|
218
|
-
wrapExpression(expression: types.Expression) {
|
|
219
|
-
return wrappers.reduceRight((acc, applyWrapper) => applyWrapper(acc), expression);
|
|
220
|
-
},
|
|
221
|
-
};
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
const removeUnusedUseContextImport = (programPath: NodePath<types.Program>) => {
|
|
225
|
-
let useContextReferenced = false;
|
|
226
|
-
|
|
227
|
-
traverse(programPath.node, {
|
|
228
|
-
noScope: true,
|
|
229
|
-
Identifier(path) {
|
|
230
|
-
if (path.node.name !== 'useContext') return;
|
|
231
|
-
|
|
232
|
-
if (path.parent.type === 'ImportDefaultSpecifier') return;
|
|
233
|
-
|
|
234
|
-
useContextReferenced = true;
|
|
235
|
-
path.stop();
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
if (useContextReferenced) return;
|
|
240
|
-
|
|
241
|
-
for (const statementPath of programPath.get('body')) {
|
|
242
|
-
if (!statementPath.isImportDeclaration()) continue;
|
|
243
|
-
|
|
244
|
-
if (statementPath.node.source.value !== '@/client/context') continue;
|
|
245
|
-
|
|
246
|
-
const nextSpecifiers = statementPath.node.specifiers.filter(
|
|
247
|
-
(specifier) => specifier.type !== 'ImportDefaultSpecifier' || specifier.local.name !== 'useContext',
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
if (!nextSpecifiers.length) statementPath.remove();
|
|
251
|
-
else statementPath.node.specifiers = nextSpecifiers;
|
|
252
|
-
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
const prefixOptionsObject = (options: types.ObjectExpression) => {
|
|
258
|
-
const properties: (types.ObjectProperty | types.SpreadElement)[] = [];
|
|
259
|
-
|
|
260
|
-
for (const property of options.properties) {
|
|
261
|
-
if (property.type !== 'ObjectProperty') {
|
|
262
|
-
properties.push(property);
|
|
263
|
-
continue;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
if (property.key.type === 'Identifier') {
|
|
267
|
-
properties.push(
|
|
268
|
-
types.objectProperty(types.identifier(`_${property.key.name}`), property.value, false, false),
|
|
269
|
-
);
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (property.key.type === 'StringLiteral') {
|
|
274
|
-
properties.push(types.objectProperty(types.stringLiteral(`_${property.key.value}`), property.value));
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
properties.push(property);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return types.objectExpression(properties);
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const repoRoots = process.argv.slice(2);
|
|
285
|
-
if (!repoRoots.length) throw new Error('Usage: ts-node scripts/refactor-client-pages.ts <repo-root> [repo-root...]');
|
|
286
|
-
|
|
287
|
-
for (const repoRoot of repoRoots) {
|
|
288
|
-
const pagesRoot = path.join(repoRoot, 'client', 'pages');
|
|
289
|
-
const files = findFiles(pagesRoot);
|
|
290
|
-
let changedFiles = 0;
|
|
291
|
-
|
|
292
|
-
for (const filepath of files) {
|
|
293
|
-
const code = fs.readFileSync(filepath, 'utf8');
|
|
294
|
-
if (
|
|
295
|
-
!code.includes('Router.page(') &&
|
|
296
|
-
!code.includes('api.fetch(') &&
|
|
297
|
-
!code.includes('@app') &&
|
|
298
|
-
!code.includes('"@app"')
|
|
299
|
-
)
|
|
300
|
-
continue;
|
|
301
|
-
|
|
302
|
-
const ast = parse(code, {
|
|
303
|
-
sourceType: 'module',
|
|
304
|
-
errorRecovery: true,
|
|
305
|
-
plugins: ['typescript', 'jsx', 'decorators-legacy', 'classProperties'],
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
const importedServices = new Set<string>();
|
|
309
|
-
let hasAppImport = false;
|
|
310
|
-
let routeCallPath: NodePath<types.CallExpression> | null = null;
|
|
311
|
-
let renderFunctionPath: NodePath<
|
|
312
|
-
types.FunctionExpression | types.ArrowFunctionExpression | types.FunctionDeclaration
|
|
313
|
-
> | null = null;
|
|
314
|
-
|
|
315
|
-
traverse(ast, {
|
|
316
|
-
noScope: true,
|
|
317
|
-
ImportDeclaration(path) {
|
|
318
|
-
if (path.node.source.value !== '@app') return;
|
|
319
|
-
|
|
320
|
-
hasAppImport = true;
|
|
321
|
-
|
|
322
|
-
for (const specifier of path.node.specifiers) {
|
|
323
|
-
if (
|
|
324
|
-
specifier.type === 'ImportSpecifier' &&
|
|
325
|
-
specifier.imported.type === 'Identifier' &&
|
|
326
|
-
specifier.imported.name !== 'Router'
|
|
327
|
-
)
|
|
328
|
-
importedServices.add(specifier.local.name);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
path.remove();
|
|
332
|
-
},
|
|
333
|
-
CallExpression(path) {
|
|
334
|
-
if (routeCallPath) return;
|
|
335
|
-
|
|
336
|
-
const callee = path.node.callee;
|
|
337
|
-
if (
|
|
338
|
-
callee.type === 'MemberExpression' &&
|
|
339
|
-
callee.object.type === 'Identifier' &&
|
|
340
|
-
callee.object.name === 'Router' &&
|
|
341
|
-
callee.property.type === 'Identifier' &&
|
|
342
|
-
callee.property.name === 'page'
|
|
343
|
-
) {
|
|
344
|
-
routeCallPath = path;
|
|
345
|
-
|
|
346
|
-
const renderArg = path.get('arguments')[path.node.arguments.length - 1];
|
|
347
|
-
if (
|
|
348
|
-
renderArg &&
|
|
349
|
-
(renderArg.isArrowFunctionExpression() ||
|
|
350
|
-
renderArg.isFunctionExpression() ||
|
|
351
|
-
renderArg.isFunctionDeclaration())
|
|
352
|
-
)
|
|
353
|
-
renderFunctionPath = renderArg as NodePath<
|
|
354
|
-
types.FunctionExpression | types.ArrowFunctionExpression | types.FunctionDeclaration
|
|
355
|
-
>;
|
|
356
|
-
}
|
|
357
|
-
},
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
if (!routeCallPath || !renderFunctionPath) {
|
|
361
|
-
if (!hasAppImport) continue;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
let changed = hasAppImport;
|
|
365
|
-
|
|
366
|
-
traverse(ast, {
|
|
367
|
-
noScope: true,
|
|
368
|
-
Program(programPath) {
|
|
369
|
-
if (hasAppImport) ensureRouterImport(programPath);
|
|
370
|
-
},
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
if (renderFunctionPath) {
|
|
374
|
-
ensureBlockBody(renderFunctionPath);
|
|
375
|
-
const renderParam = ensureObjectPatternParam(renderFunctionPath);
|
|
376
|
-
if (!renderParam) continue;
|
|
377
|
-
|
|
378
|
-
const fetcherProperties: types.ObjectProperty[] = [];
|
|
379
|
-
const extraRenderProperties: Array<{ key: string; local: string }> = [];
|
|
380
|
-
const setupContextNames = new Set<string>();
|
|
381
|
-
let setupNeeded = false;
|
|
382
|
-
|
|
383
|
-
renderFunctionPath.traverse({
|
|
384
|
-
noScope: true,
|
|
385
|
-
VariableDeclarator(path) {
|
|
386
|
-
const fetchExpression = unwrapFetchExpression(path.node.init);
|
|
387
|
-
if (!fetchExpression) return;
|
|
388
|
-
|
|
389
|
-
const declarationPath = path.findParent((parentPath) => parentPath.isVariableDeclaration());
|
|
390
|
-
if (!declarationPath?.isVariableDeclaration()) return;
|
|
391
|
-
|
|
392
|
-
setupNeeded = true;
|
|
393
|
-
changed = true;
|
|
394
|
-
|
|
395
|
-
for (const property of fetchExpression.callExpression.arguments[0].properties) {
|
|
396
|
-
if (property.type === 'ObjectProperty') fetcherProperties.push(property);
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (path.node.id.type === 'ObjectPattern') {
|
|
400
|
-
for (const property of path.node.id.properties) {
|
|
401
|
-
if (property.type !== 'ObjectProperty' || property.key.type !== 'Identifier') continue;
|
|
402
|
-
|
|
403
|
-
if (property.value.type === 'Identifier')
|
|
404
|
-
extraRenderProperties.push({ key: property.key.name, local: property.value.name });
|
|
405
|
-
else if (
|
|
406
|
-
property.value.type === 'AssignmentPattern' &&
|
|
407
|
-
property.value.left.type === 'Identifier'
|
|
408
|
-
)
|
|
409
|
-
extraRenderProperties.push({ key: property.key.name, local: property.value.left.name });
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
declarationPath.remove();
|
|
413
|
-
return;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
if (path.node.id.type === 'Identifier') {
|
|
417
|
-
const objectProperties = fetchExpression.callExpression.arguments[0].properties
|
|
418
|
-
.filter(
|
|
419
|
-
(property): property is types.ObjectProperty =>
|
|
420
|
-
property.type === 'ObjectProperty' && property.key.type === 'Identifier',
|
|
421
|
-
)
|
|
422
|
-
.map((property) => {
|
|
423
|
-
extraRenderProperties.push({ key: property.key.name, local: property.key.name });
|
|
424
|
-
return types.objectProperty(
|
|
425
|
-
types.identifier(property.key.name),
|
|
426
|
-
types.identifier(property.key.name),
|
|
427
|
-
false,
|
|
428
|
-
true,
|
|
429
|
-
);
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
declarationPath.replaceWith(
|
|
433
|
-
types.variableDeclaration('const', [
|
|
434
|
-
types.variableDeclarator(
|
|
435
|
-
types.identifier(path.node.id.name),
|
|
436
|
-
fetchExpression.wrapExpression(types.objectExpression(objectProperties)),
|
|
437
|
-
),
|
|
438
|
-
]),
|
|
439
|
-
);
|
|
440
|
-
}
|
|
441
|
-
},
|
|
442
|
-
VariableDeclaration(path) {
|
|
443
|
-
const declaration = path.node.declarations[0];
|
|
444
|
-
if (
|
|
445
|
-
!declaration ||
|
|
446
|
-
declaration.id.type !== 'ObjectPattern' ||
|
|
447
|
-
declaration.init?.type !== 'CallExpression' ||
|
|
448
|
-
declaration.init.callee.type !== 'Identifier' ||
|
|
449
|
-
declaration.init.callee.name !== 'useContext'
|
|
450
|
-
)
|
|
451
|
-
return;
|
|
452
|
-
|
|
453
|
-
const names = declaration.id.properties
|
|
454
|
-
.filter(
|
|
455
|
-
(property): property is types.ObjectProperty =>
|
|
456
|
-
property.type === 'ObjectProperty' && property.key.type === 'Identifier',
|
|
457
|
-
)
|
|
458
|
-
.map((property) => property.key.name);
|
|
459
|
-
|
|
460
|
-
if (!names.length) return;
|
|
461
|
-
|
|
462
|
-
for (const name of names) {
|
|
463
|
-
setupContextNames.add(name);
|
|
464
|
-
extraRenderProperties.push({ key: name, local: name });
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
path.remove();
|
|
468
|
-
},
|
|
469
|
-
});
|
|
470
|
-
|
|
471
|
-
for (const { key, local } of extraRenderProperties) addObjectPatternProperty(renderParam, key, local);
|
|
472
|
-
|
|
473
|
-
for (const name of [...importedServices].sort((a, b) => a.localeCompare(b)))
|
|
474
|
-
addObjectPatternProperty(renderParam, name);
|
|
475
|
-
|
|
476
|
-
if (routeCallPath) {
|
|
477
|
-
const args = routeCallPath.node.arguments;
|
|
478
|
-
const pathArg = args[0];
|
|
479
|
-
const lastArg = args[args.length - 1];
|
|
480
|
-
const maybeSetupArg =
|
|
481
|
-
args.length >= 3 &&
|
|
482
|
-
(args[1].type === 'ArrowFunctionExpression' || args[1].type === 'FunctionExpression')
|
|
483
|
-
? args[1]
|
|
484
|
-
: null;
|
|
485
|
-
const maybeOptionsArg = args.length >= 3 && args[1].type === 'ObjectExpression' ? args[1] : null;
|
|
486
|
-
|
|
487
|
-
if (setupNeeded || maybeOptionsArg || maybeSetupArg) {
|
|
488
|
-
const setupProperties: (types.ObjectProperty | types.SpreadElement)[] = [];
|
|
489
|
-
const setupParamProperties: types.ObjectProperty[] = [];
|
|
490
|
-
const existingSetupDataKeys = new Set<string>();
|
|
491
|
-
|
|
492
|
-
if (maybeSetupArg) {
|
|
493
|
-
const existingReturnedObject = getReturnedObjectExpression(maybeSetupArg);
|
|
494
|
-
if (existingReturnedObject) {
|
|
495
|
-
for (const property of existingReturnedObject.properties) {
|
|
496
|
-
if (property.type !== 'ObjectProperty') continue;
|
|
497
|
-
|
|
498
|
-
const propertyKey = getObjectPropertyKeyName(property);
|
|
499
|
-
if (!propertyKey || propertyKey.startsWith('_')) continue;
|
|
500
|
-
|
|
501
|
-
existingSetupDataKeys.add(propertyKey);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
const fetcherKeys = new Set(
|
|
507
|
-
fetcherProperties
|
|
508
|
-
.map((property) => getObjectPropertyKeyName(property))
|
|
509
|
-
.filter((key): key is string => Boolean(key)),
|
|
510
|
-
);
|
|
511
|
-
|
|
512
|
-
for (const property of renderParam.properties) {
|
|
513
|
-
if (property.type !== 'ObjectProperty') continue;
|
|
514
|
-
|
|
515
|
-
const propertyKey = getObjectPropertyKeyName(property);
|
|
516
|
-
if (propertyKey && fetcherKeys.has(propertyKey)) continue;
|
|
517
|
-
|
|
518
|
-
if (propertyKey && existingSetupDataKeys.has(propertyKey)) continue;
|
|
519
|
-
|
|
520
|
-
pushUniqueObjectPatternProperty(setupParamProperties, types.cloneNode(property, true));
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
if (maybeOptionsArg) setupProperties.push(...prefixOptionsObject(maybeOptionsArg).properties);
|
|
524
|
-
|
|
525
|
-
if (maybeSetupArg) {
|
|
526
|
-
const existingSetupParam = findObjectPatternParam(maybeSetupArg);
|
|
527
|
-
if (existingSetupParam) {
|
|
528
|
-
for (const property of existingSetupParam.properties) {
|
|
529
|
-
if (property.type !== 'ObjectProperty') continue;
|
|
530
|
-
|
|
531
|
-
const propertyKey = getObjectPropertyKeyName(property);
|
|
532
|
-
if (propertyKey && existingSetupDataKeys.has(propertyKey)) continue;
|
|
533
|
-
|
|
534
|
-
if (property.type === 'ObjectProperty')
|
|
535
|
-
pushUniqueObjectPatternProperty(
|
|
536
|
-
setupParamProperties,
|
|
537
|
-
types.cloneNode(property, true),
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
const returnedObject = getReturnedObjectExpression(maybeSetupArg);
|
|
543
|
-
if (returnedObject) {
|
|
544
|
-
for (const property of returnedObject.properties) {
|
|
545
|
-
if (property.type === 'ObjectProperty')
|
|
546
|
-
pushUniqueObjectProperty(setupProperties, property);
|
|
547
|
-
else setupProperties.push(property);
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
for (const property of fetcherProperties) pushUniqueObjectProperty(setupProperties, property);
|
|
553
|
-
|
|
554
|
-
const setupParam = types.objectPattern(setupParamProperties);
|
|
555
|
-
for (const name of [...new Set([...importedServices, ...setupContextNames])].sort((a, b) =>
|
|
556
|
-
a.localeCompare(b),
|
|
557
|
-
))
|
|
558
|
-
addObjectPatternProperty(setupParam, name);
|
|
559
|
-
|
|
560
|
-
const setupFunction = types.arrowFunctionExpression(
|
|
561
|
-
[setupParam],
|
|
562
|
-
types.objectExpression(setupProperties),
|
|
563
|
-
);
|
|
564
|
-
|
|
565
|
-
routeCallPath.node.arguments = [pathArg, setupFunction, lastArg];
|
|
566
|
-
changed = true;
|
|
567
|
-
} else {
|
|
568
|
-
routeCallPath.node.arguments = [pathArg, lastArg];
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
traverse(ast, {
|
|
574
|
-
noScope: true,
|
|
575
|
-
Program(programPath) {
|
|
576
|
-
removeUnusedUseContextImport(programPath);
|
|
577
|
-
},
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
if (!changed) continue;
|
|
581
|
-
|
|
582
|
-
fs.writeFileSync(filepath, generate(ast, {}, code).code);
|
|
583
|
-
changedFiles++;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
console.log(`[refactor-client-pages] ${repoRoot}: changed ${changedFiles} files`);
|
|
587
|
-
}
|