create-prisma-php-app 5.0.0-alpha.8 → 5.0.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 +23 -2
- package/dist/.github/copilot-instructions.md +191 -0
- package/dist/AGENTS.md +588 -193
- package/dist/bootstrap.php +426 -213
- package/dist/index.js +2 -2
- package/dist/phpunit.xml +25 -0
- package/dist/postcss.config.js +4 -2
- package/dist/prisma-php.js +2 -2
- package/dist/public/.htaccess +1 -1
- package/dist/public/js/main.js +13 -1
- package/dist/public/js/pp-reactive-v2.min.js +1 -0
- package/dist/settings/auto-swagger-docs.ts +10 -10
- package/dist/settings/bs-config.ts +47 -27
- package/dist/settings/build.ts +2 -32
- package/dist/settings/component-map.ts +476 -0
- package/dist/settings/files-list.ts +2 -2
- package/dist/settings/project-name.ts +3 -6
- package/dist/settings/restart-mcp.ts +2 -2
- package/dist/settings/restart-websocket.ts +2 -2
- package/dist/settings/run-postcss.ts +205 -0
- package/dist/settings/run-tests.ts +35 -0
- package/dist/settings/swagger-config.ts +3 -3
- package/dist/settings/utils.ts +3 -3
- package/dist/src/Lib/Auth/Auth.php +62 -34
- package/dist/src/Lib/MCP/mcp-server.php +2 -3
- package/dist/src/Lib/Middleware/CorsMiddleware.php +72 -0
- package/dist/src/Lib/Websocket/ConnectionManager.php +460 -7
- package/dist/src/Lib/Websocket/Socket.php +170 -0
- package/dist/src/Lib/Websocket/SocketPool.php +50 -0
- package/dist/src/Lib/Websocket/SocketRegistry.php +88 -0
- package/dist/src/Lib/Websocket/sockets.php +50 -0
- package/dist/src/Lib/Websocket/websocket-server.php +10 -3
- package/dist/src/app/globals.css +3 -1
- package/dist/src/app/layout.php +1 -1
- package/dist/tests/AuthTest.php +59 -0
- package/dist/tests/ConnectionManagerTest.php +277 -0
- package/dist/tests/CsrfTest.php +119 -0
- package/dist/tests/DeferComponentRootsTest.php +147 -0
- package/dist/tests/FeaturesTest.php +41 -0
- package/dist/tests/README.md +119 -0
- package/dist/tests/RpcWireContractTest.php +101 -0
- package/dist/tests/SocketPoolTest.php +69 -0
- package/dist/tests/SocketRegistryTest.php +77 -0
- package/dist/tests/SocketTest.php +124 -0
- package/dist/tests/SocketsRegistrationTest.php +40 -0
- package/dist/tests/Support/FakeConnection.php +62 -0
- package/dist/tests/Support/Features.php +38 -0
- package/dist/tests/Support/RequiresFeature.php +26 -0
- package/dist/tests/bootstrap.php +44 -0
- package/dist/ts/main.ts +21 -3
- package/dist/ts/tailwind-merge.ts +13 -0
- package/package.json +4 -4
- package/dist/README.md +0 -213
- package/dist/public/js/pp-reactive-v2.js +0 -1
- package/dist/settings/bs-config.json +0 -6
- package/dist/settings/class-imports.ts +0 -165
- package/dist/settings/class-log.ts +0 -244
- package/dist/settings/component-import-checker.ts +0 -90
- package/dist/settings/files-list.json +0 -1
- package/dist/settings/prisma-schema-config.json +0 -16
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { Engine } from "php-parser";
|
|
4
|
+
import { getFileMeta } from "./utils.js";
|
|
5
|
+
|
|
6
|
+
const { __dirname } = getFileMeta();
|
|
7
|
+
|
|
8
|
+
const parser = new Engine({
|
|
9
|
+
parser: {
|
|
10
|
+
php8: true,
|
|
11
|
+
suppressErrors: true,
|
|
12
|
+
},
|
|
13
|
+
ast: {
|
|
14
|
+
withPositions: false,
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const PROJECT_ROOT = path.join(__dirname, "..");
|
|
19
|
+
const CONFIG_FILE = path.join(PROJECT_ROOT, "prisma-php.json");
|
|
20
|
+
const COMPONENT_MAP_FILE = path.join(
|
|
21
|
+
PROJECT_ROOT,
|
|
22
|
+
"settings/component-map.json",
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const SRC_DIR = path.join(PROJECT_ROOT, "src");
|
|
26
|
+
|
|
27
|
+
const PHPX_BASE_CLASS = "PHPX";
|
|
28
|
+
|
|
29
|
+
type JsonValue =
|
|
30
|
+
| string
|
|
31
|
+
| number
|
|
32
|
+
| boolean
|
|
33
|
+
| null
|
|
34
|
+
| JsonValue[]
|
|
35
|
+
| { [key: string]: JsonValue };
|
|
36
|
+
|
|
37
|
+
export interface ComponentMapProp {
|
|
38
|
+
name: string;
|
|
39
|
+
type: string;
|
|
40
|
+
hasDefault: boolean;
|
|
41
|
+
defaultValue: JsonValue | string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface ComponentMapEntry {
|
|
45
|
+
componentName: string;
|
|
46
|
+
tagName: string;
|
|
47
|
+
filePath: string;
|
|
48
|
+
relativePath: string;
|
|
49
|
+
importRoute: string;
|
|
50
|
+
extendsPHPX: boolean;
|
|
51
|
+
acceptsArbitraryProps: boolean;
|
|
52
|
+
props: ComponentMapProp[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface PrismaPhpConfig {
|
|
56
|
+
projectRootPath?: string;
|
|
57
|
+
excludeFiles?: string[];
|
|
58
|
+
componentScanDirs?: string[];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type DiscoveredComponent = ComponentMapEntry;
|
|
62
|
+
|
|
63
|
+
async function loadConfig(): Promise<PrismaPhpConfig> {
|
|
64
|
+
try {
|
|
65
|
+
const raw = await fs.readFile(CONFIG_FILE, "utf-8");
|
|
66
|
+
return JSON.parse(raw) as PrismaPhpConfig;
|
|
67
|
+
} catch {
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function resolveProjectRoot(config: PrismaPhpConfig): string {
|
|
73
|
+
if (config.projectRootPath && path.isAbsolute(config.projectRootPath)) {
|
|
74
|
+
return config.projectRootPath;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return PROJECT_ROOT;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function resolveScanRoots(
|
|
81
|
+
config: PrismaPhpConfig,
|
|
82
|
+
projectRoot: string,
|
|
83
|
+
): string[] {
|
|
84
|
+
const scanDirs =
|
|
85
|
+
Array.isArray(config.componentScanDirs) &&
|
|
86
|
+
config.componentScanDirs.length > 0
|
|
87
|
+
? config.componentScanDirs
|
|
88
|
+
: ["src"];
|
|
89
|
+
|
|
90
|
+
return scanDirs.map((dirPath) =>
|
|
91
|
+
path.isAbsolute(dirPath) ? dirPath : path.join(projectRoot, dirPath),
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function normalizePathKey(filePath: string): string {
|
|
96
|
+
return path.normalize(filePath).toLowerCase();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function toAbsoluteComponentPath(filePath: string): string {
|
|
100
|
+
return path.normalize(filePath);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function toProjectRelativePath(filePath: string, projectRoot: string): string {
|
|
104
|
+
return path.relative(projectRoot, filePath).replace(/\\/g, "/");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function saveJsonFile(filePath: string, value: unknown): Promise<void> {
|
|
108
|
+
await fs.writeFile(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function componentNameToTagName(componentName: string): string {
|
|
112
|
+
const kebabName = componentName
|
|
113
|
+
.replace(/([a-z\d])([A-Z])/g, "$1-$2")
|
|
114
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2")
|
|
115
|
+
.replace(/[_.\s]+/g, "-")
|
|
116
|
+
.toLowerCase();
|
|
117
|
+
|
|
118
|
+
return `x-${kebabName}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export async function getAllPhpFiles(dir: string): Promise<string[]> {
|
|
122
|
+
const files: string[] = [];
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
126
|
+
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
const fullPath = path.join(dir, entry.name);
|
|
129
|
+
|
|
130
|
+
if (entry.isDirectory()) {
|
|
131
|
+
files.push(...(await getAllPhpFiles(fullPath)));
|
|
132
|
+
} else if (entry.isFile() && fullPath.toLowerCase().endsWith(".php")) {
|
|
133
|
+
files.push(fullPath);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
return files;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return files;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function nameToString(node: any): string {
|
|
144
|
+
if (!node) {
|
|
145
|
+
return "";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (typeof node === "string") {
|
|
149
|
+
return node;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (typeof node.raw === "string" && node.raw.trim() !== "") {
|
|
153
|
+
return node.raw;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (typeof node.name === "string") {
|
|
157
|
+
return node.name;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (Array.isArray(node.name)) {
|
|
161
|
+
return node.name
|
|
162
|
+
.map((part: any) =>
|
|
163
|
+
typeof part === "string" ? part : (part?.name ?? ""),
|
|
164
|
+
)
|
|
165
|
+
.filter(Boolean)
|
|
166
|
+
.join("\\");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (Array.isArray(node.items)) {
|
|
170
|
+
return node.items.map(nameToString).filter(Boolean).join("|");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return String(node.name ?? "");
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function typeToString(typeNode: any, nullable = false): string {
|
|
177
|
+
if (!typeNode) {
|
|
178
|
+
return "mixed";
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let rawType = "";
|
|
182
|
+
|
|
183
|
+
if (typeof typeNode.raw === "string" && typeNode.raw.trim() !== "") {
|
|
184
|
+
rawType = typeNode.raw.replace(/\s+/g, "");
|
|
185
|
+
} else if (Array.isArray(typeNode.types)) {
|
|
186
|
+
rawType = typeNode.types.map((item: any) => typeToString(item)).join("|");
|
|
187
|
+
} else {
|
|
188
|
+
rawType = nameToString(typeNode).replace(/\s+/g, "");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (
|
|
192
|
+
nullable &&
|
|
193
|
+
rawType !== "mixed" &&
|
|
194
|
+
!rawType.startsWith("?") &&
|
|
195
|
+
!rawType.split("|").includes("null")
|
|
196
|
+
) {
|
|
197
|
+
return `?${rawType}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return rawType || "mixed";
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function astValueToJson(valueNode: any): JsonValue | string {
|
|
204
|
+
if (!valueNode || typeof valueNode !== "object") {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
switch (valueNode.kind) {
|
|
209
|
+
case "string":
|
|
210
|
+
return valueNode.value ?? "";
|
|
211
|
+
case "number":
|
|
212
|
+
return Number(valueNode.value ?? valueNode.raw ?? 0);
|
|
213
|
+
case "boolean":
|
|
214
|
+
return Boolean(valueNode.value);
|
|
215
|
+
case "nullkeyword":
|
|
216
|
+
return null;
|
|
217
|
+
case "array": {
|
|
218
|
+
const items = Array.isArray(valueNode.items) ? valueNode.items : [];
|
|
219
|
+
const hasNamedKeys = items.some((item: any) => item?.key);
|
|
220
|
+
|
|
221
|
+
if (!hasNamedKeys) {
|
|
222
|
+
return items.map((item: any) => astValueToJson(item?.value ?? item));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const mapped: Record<string, JsonValue | string> = {};
|
|
226
|
+
|
|
227
|
+
for (const item of items) {
|
|
228
|
+
const key = item?.key ? String(astValueToJson(item.key)) : "";
|
|
229
|
+
mapped[key] = astValueToJson(item?.value);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return mapped;
|
|
233
|
+
}
|
|
234
|
+
case "unary": {
|
|
235
|
+
const inner = astValueToJson(valueNode.what);
|
|
236
|
+
|
|
237
|
+
if (typeof inner === "number") {
|
|
238
|
+
return valueNode.type === "-" ? -inner : inner;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return typeof valueNode.raw === "string" ? valueNode.raw : String(inner);
|
|
242
|
+
}
|
|
243
|
+
case "constref": {
|
|
244
|
+
const constantName = nameToString(valueNode.name).toLowerCase();
|
|
245
|
+
|
|
246
|
+
if (constantName === "true") {
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (constantName === "false") {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (constantName === "null") {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return nameToString(valueNode.name);
|
|
259
|
+
}
|
|
260
|
+
default:
|
|
261
|
+
if (typeof valueNode.raw === "string" && valueNode.raw.trim() !== "") {
|
|
262
|
+
return valueNode.raw;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return nameToString(valueNode);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function extractComponentProps(classNode: any): ComponentMapProp[] {
|
|
270
|
+
const props: ComponentMapProp[] = [];
|
|
271
|
+
|
|
272
|
+
for (const statement of classNode.body ?? []) {
|
|
273
|
+
if (
|
|
274
|
+
!statement ||
|
|
275
|
+
statement.kind !== "propertystatement" ||
|
|
276
|
+
statement.visibility !== "public" ||
|
|
277
|
+
statement.isStatic
|
|
278
|
+
) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
for (const property of statement.properties ?? []) {
|
|
283
|
+
props.push({
|
|
284
|
+
name: nameToString(property.name),
|
|
285
|
+
type: typeToString(property.type, Boolean(property.nullable)),
|
|
286
|
+
hasDefault: property.value !== undefined && property.value !== null,
|
|
287
|
+
defaultValue:
|
|
288
|
+
property.value !== undefined && property.value !== null
|
|
289
|
+
? astValueToJson(property.value)
|
|
290
|
+
: null,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return props;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function classExtendsPHPX(classNode: any): boolean {
|
|
299
|
+
if (!classNode?.extends) {
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const name = nameToString(classNode.extends);
|
|
304
|
+
return name.split("\\").pop() === PHPX_BASE_CLASS;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export async function analyzeComponentsInFile(
|
|
308
|
+
filePath: string,
|
|
309
|
+
projectRoot: string,
|
|
310
|
+
): Promise<DiscoveredComponent[]> {
|
|
311
|
+
const code = await fs.readFile(filePath, "utf-8");
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
const ast = parser.parseCode(code, filePath);
|
|
315
|
+
const components: DiscoveredComponent[] = [];
|
|
316
|
+
|
|
317
|
+
function traverse(node: any, currentNamespace = "") {
|
|
318
|
+
if (Array.isArray(node)) {
|
|
319
|
+
node.forEach((childNode) => traverse(childNode, currentNamespace));
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (!node || typeof node !== "object") {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (node.kind === "namespace") {
|
|
328
|
+
const nextNamespace = nameToString(node.name).replace(/^\\+/, "");
|
|
329
|
+
|
|
330
|
+
for (const [key, value] of Object.entries(node)) {
|
|
331
|
+
if (key === "kind" || key === "name") {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
traverse(value, nextNamespace || currentNamespace);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (node.kind === "class" && node.name?.name) {
|
|
342
|
+
const extendsPHPX = classExtendsPHPX(node);
|
|
343
|
+
|
|
344
|
+
if (extendsPHPX) {
|
|
345
|
+
const componentName = node.name.name as string;
|
|
346
|
+
const importRoute =
|
|
347
|
+
(currentNamespace ? `${currentNamespace}\\` : "") + componentName;
|
|
348
|
+
|
|
349
|
+
components.push({
|
|
350
|
+
componentName,
|
|
351
|
+
tagName: componentNameToTagName(componentName),
|
|
352
|
+
filePath: toAbsoluteComponentPath(filePath),
|
|
353
|
+
relativePath: toProjectRelativePath(filePath, projectRoot),
|
|
354
|
+
importRoute,
|
|
355
|
+
extendsPHPX,
|
|
356
|
+
acceptsArbitraryProps: extendsPHPX,
|
|
357
|
+
props: extractComponentProps(node),
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
for (const [key, value] of Object.entries(node)) {
|
|
363
|
+
if (key === "kind" || key === "name") {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
traverse(value, currentNamespace);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
traverse(ast);
|
|
372
|
+
return components;
|
|
373
|
+
} catch (error) {
|
|
374
|
+
console.error(`Error parsing component file: ${filePath}`, error);
|
|
375
|
+
return [];
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function sortComponentMap(
|
|
380
|
+
componentMap: DiscoveredComponent[],
|
|
381
|
+
): DiscoveredComponent[] {
|
|
382
|
+
return [...componentMap].sort((left, right) => {
|
|
383
|
+
const nameComparison = left.componentName.localeCompare(
|
|
384
|
+
right.componentName,
|
|
385
|
+
);
|
|
386
|
+
|
|
387
|
+
if (nameComparison !== 0) {
|
|
388
|
+
return nameComparison;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return left.relativePath.localeCompare(right.relativePath);
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export function buildRuntimeComponentLookup(
|
|
396
|
+
componentMap: ComponentMapEntry[],
|
|
397
|
+
): Record<
|
|
398
|
+
string,
|
|
399
|
+
Array<{
|
|
400
|
+
tagName: string;
|
|
401
|
+
componentName: string;
|
|
402
|
+
className: string;
|
|
403
|
+
filePath: string;
|
|
404
|
+
}>
|
|
405
|
+
> {
|
|
406
|
+
const lookup: Record<
|
|
407
|
+
string,
|
|
408
|
+
Array<{
|
|
409
|
+
tagName: string;
|
|
410
|
+
componentName: string;
|
|
411
|
+
className: string;
|
|
412
|
+
filePath: string;
|
|
413
|
+
}>
|
|
414
|
+
> = {};
|
|
415
|
+
|
|
416
|
+
for (const entry of componentMap) {
|
|
417
|
+
const tagName = entry.tagName.toLowerCase();
|
|
418
|
+
|
|
419
|
+
if (!lookup[tagName]) {
|
|
420
|
+
lookup[tagName] = [];
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
lookup[tagName].push({
|
|
424
|
+
tagName,
|
|
425
|
+
componentName: entry.componentName,
|
|
426
|
+
className: entry.importRoute,
|
|
427
|
+
filePath: entry.filePath,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return Object.fromEntries(
|
|
432
|
+
Object.entries(lookup).sort(([left], [right]) => left.localeCompare(right)),
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export async function updateComponentMap(): Promise<ComponentMapEntry[]> {
|
|
437
|
+
const config = await loadConfig();
|
|
438
|
+
const projectRoot = resolveProjectRoot(config);
|
|
439
|
+
const scanRoots = resolveScanRoots(config, projectRoot);
|
|
440
|
+
const excludedFiles = new Set(
|
|
441
|
+
(config.excludeFiles ?? []).map((filePath) =>
|
|
442
|
+
normalizePathKey(
|
|
443
|
+
path.isAbsolute(filePath) ? filePath : path.join(projectRoot, filePath),
|
|
444
|
+
),
|
|
445
|
+
),
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
const phpFiles: string[] = [];
|
|
449
|
+
|
|
450
|
+
for (const scanRoot of scanRoots) {
|
|
451
|
+
const discoveredFiles = await getAllPhpFiles(scanRoot);
|
|
452
|
+
|
|
453
|
+
for (const filePath of discoveredFiles) {
|
|
454
|
+
if (!excludedFiles.has(normalizePathKey(filePath))) {
|
|
455
|
+
phpFiles.push(filePath);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
const uniquePhpFiles = [
|
|
461
|
+
...new Set(phpFiles.map((filePath) => path.normalize(filePath))),
|
|
462
|
+
];
|
|
463
|
+
const discoveredComponents: DiscoveredComponent[] = [];
|
|
464
|
+
|
|
465
|
+
for (const filePath of uniquePhpFiles) {
|
|
466
|
+
discoveredComponents.push(
|
|
467
|
+
...(await analyzeComponentsInFile(filePath, projectRoot)),
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const sortedComponents = sortComponentMap(discoveredComponents);
|
|
472
|
+
const componentMap: ComponentMapEntry[] = [...sortedComponents];
|
|
473
|
+
await saveJsonFile(COMPONENT_MAP_FILE, componentMap);
|
|
474
|
+
|
|
475
|
+
return componentMap;
|
|
476
|
+
}
|
|
@@ -23,7 +23,7 @@ const getAllFiles = (dirPath: string): string[] => {
|
|
|
23
23
|
} else {
|
|
24
24
|
const relativePath = `.${sep}${relative(
|
|
25
25
|
join(__dirname, ".."),
|
|
26
|
-
fullPath
|
|
26
|
+
fullPath,
|
|
27
27
|
)}`;
|
|
28
28
|
files.push(relativePath.replace(/\\/g, "/").replace(/^\.\.\//, ""));
|
|
29
29
|
}
|
|
@@ -41,7 +41,7 @@ export const generateFileListJson = async (): Promise<void> => {
|
|
|
41
41
|
if (allFiles.length > 0) {
|
|
42
42
|
writeFileSync(jsonFilePath, JSON.stringify(allFiles, null, 2));
|
|
43
43
|
console.log(
|
|
44
|
-
`File list generated: ${appFiles.length} app files, ${publicFiles.length} public files
|
|
44
|
+
`File list generated: ${appFiles.length} app files, ${publicFiles.length} public files`,
|
|
45
45
|
);
|
|
46
46
|
} else {
|
|
47
47
|
console.error("No files found to save in the JSON file.");
|
|
@@ -3,8 +3,7 @@ import { join, basename, dirname, normalize, sep } from "path";
|
|
|
3
3
|
import prismaPhpConfigJson from "../prisma-php.json";
|
|
4
4
|
import { getFileMeta } from "./utils.js";
|
|
5
5
|
import { promises as fsPromises } from "fs";
|
|
6
|
-
import {
|
|
7
|
-
import { updateComponentImports } from "./class-imports";
|
|
6
|
+
import { updateComponentMap } from "./component-map";
|
|
8
7
|
import { generateFileListJson } from "./files-list";
|
|
9
8
|
|
|
10
9
|
const { __dirname } = getFileMeta();
|
|
@@ -152,8 +151,7 @@ export async function deleteDirectoriesIfExist(
|
|
|
152
151
|
|
|
153
152
|
export const filesToDelete = [
|
|
154
153
|
join(__dirname, "request-data.json"),
|
|
155
|
-
join(__dirname, "
|
|
156
|
-
join(__dirname, "class-imports.json"),
|
|
154
|
+
join(__dirname, "component-map.json"),
|
|
157
155
|
];
|
|
158
156
|
|
|
159
157
|
export const dirsToDelete = [
|
|
@@ -164,5 +162,4 @@ export const dirsToDelete = [
|
|
|
164
162
|
await deleteFilesIfExist(filesToDelete);
|
|
165
163
|
await deleteDirectoriesIfExist(dirsToDelete);
|
|
166
164
|
await generateFileListJson();
|
|
167
|
-
await
|
|
168
|
-
await updateComponentImports();
|
|
165
|
+
await updateComponentMap();
|
|
@@ -19,7 +19,7 @@ const serverScriptPath = join(
|
|
|
19
19
|
"src",
|
|
20
20
|
"Lib",
|
|
21
21
|
"MCP",
|
|
22
|
-
"mcp-server.php"
|
|
22
|
+
"mcp-server.php",
|
|
23
23
|
);
|
|
24
24
|
const watchRoot = join(__dirname, "..", "src");
|
|
25
25
|
|
|
@@ -37,7 +37,7 @@ const restarter = new DebouncedWorker(
|
|
|
37
37
|
await mcp.restart("file change");
|
|
38
38
|
},
|
|
39
39
|
250,
|
|
40
|
-
"mcp-restart"
|
|
40
|
+
"mcp-restart",
|
|
41
41
|
);
|
|
42
42
|
|
|
43
43
|
createSrcWatcher(watchRoot, {
|
|
@@ -13,7 +13,7 @@ const serverScriptPath = join(
|
|
|
13
13
|
SRC_DIR,
|
|
14
14
|
"Lib",
|
|
15
15
|
"Websocket",
|
|
16
|
-
"websocket-server.php"
|
|
16
|
+
"websocket-server.php",
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
const ws = createRestartableProcess({
|
|
@@ -30,7 +30,7 @@ const restarter = new DebouncedWorker(
|
|
|
30
30
|
await ws.restart("file change");
|
|
31
31
|
},
|
|
32
32
|
400,
|
|
33
|
-
"ws-restart"
|
|
33
|
+
"ws-restart",
|
|
34
34
|
);
|
|
35
35
|
|
|
36
36
|
createSrcWatcher(SRC_DIR, {
|