@beignet/cli 0.0.52 → 0.0.53
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/CHANGELOG.md +26 -0
- package/README.md +131 -1
- package/dist/analysis/layers.d.ts +2 -0
- package/dist/analysis/layers.d.ts.map +1 -1
- package/dist/analysis/layers.js +5 -0
- package/dist/analysis/layers.js.map +1 -1
- package/dist/analysis/port-wiring.d.ts +4 -2
- package/dist/analysis/port-wiring.d.ts.map +1 -1
- package/dist/analysis/port-wiring.js +39 -10
- package/dist/analysis/port-wiring.js.map +1 -1
- package/dist/analysis/source-index.d.ts +2 -1
- package/dist/analysis/source-index.d.ts.map +1 -1
- package/dist/analysis/source-index.js +34 -5
- package/dist/analysis/source-index.js.map +1 -1
- package/dist/analysis/workspace-routes.d.ts +8 -0
- package/dist/analysis/workspace-routes.d.ts.map +1 -0
- package/dist/analysis/workspace-routes.js +291 -0
- package/dist/analysis/workspace-routes.js.map +1 -0
- package/dist/analysis/workspace.d.ts +24 -3
- package/dist/analysis/workspace.d.ts.map +1 -1
- package/dist/analysis/workspace.js +166 -21
- package/dist/analysis/workspace.js.map +1 -1
- package/dist/app-map-changes.d.ts.map +1 -1
- package/dist/app-map-changes.js +19 -7
- package/dist/app-map-changes.js.map +1 -1
- package/dist/app-map.d.ts.map +1 -1
- package/dist/app-map.js +58 -55
- package/dist/app-map.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +19 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -1
- package/dist/inspect.d.ts.map +1 -1
- package/dist/inspect.js +89 -21
- package/dist/inspect.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +106 -38
- package/dist/lint.js.map +1 -1
- package/dist/make/shared.js +1 -1
- package/dist/make.js +18 -1
- package/dist/make.js.map +1 -1
- package/dist/provider-add.js +2 -2
- package/dist/provider-add.js.map +1 -1
- package/dist/provider-audit.d.ts.map +1 -1
- package/dist/provider-audit.js +22 -6
- package/dist/provider-audit.js.map +1 -1
- package/dist/templates/shared.js +3 -3
- package/package.json +3 -2
- package/skills/app-structure/SKILL.md +42 -0
- package/src/analysis/layers.ts +9 -0
- package/src/analysis/port-wiring.ts +66 -10
- package/src/analysis/source-index.ts +56 -4
- package/src/analysis/workspace-routes.ts +385 -0
- package/src/analysis/workspace.ts +281 -34
- package/src/app-map-changes.ts +31 -5
- package/src/app-map.ts +75 -75
- package/src/config.ts +33 -0
- package/src/index.ts +25 -0
- package/src/inspect.ts +123 -16
- package/src/lint.ts +146 -43
- package/src/make/shared.ts +1 -1
- package/src/make.ts +31 -1
- package/src/provider-add.ts +2 -2
- package/src/provider-audit.ts +30 -10
- package/src/templates/shared.ts +3 -3
package/src/inspect.ts
CHANGED
|
@@ -22,7 +22,13 @@ import {
|
|
|
22
22
|
stripSourceFileExtension,
|
|
23
23
|
typescriptAnalysisSourceFileExtensions,
|
|
24
24
|
} from "./analysis/source-files.js";
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
createSourceIndex,
|
|
27
|
+
exportedVariableDeclarations,
|
|
28
|
+
type SourceIndex,
|
|
29
|
+
} from "./analysis/source-index.js";
|
|
30
|
+
import { createAnalysisWorkspace, projectPath } from "./analysis/workspace.js";
|
|
31
|
+
import { workspaceRouteContracts } from "./analysis/workspace-routes.js";
|
|
26
32
|
import { createPainter } from "./ansi.js";
|
|
27
33
|
import type { DatabaseSchemaDialect, DatabaseSchemaTable } from "./choices.js";
|
|
28
34
|
import {
|
|
@@ -213,6 +219,7 @@ type RouteExport = {
|
|
|
213
219
|
handlerFile: string;
|
|
214
220
|
contractRef?: string;
|
|
215
221
|
contractFile?: string;
|
|
222
|
+
contractUnresolved?: boolean;
|
|
216
223
|
catchAllPrefix?: string;
|
|
217
224
|
source: "next-route" | "route-local";
|
|
218
225
|
};
|
|
@@ -261,19 +268,76 @@ export async function inspectApp(
|
|
|
261
268
|
const targetDir = path.resolve(options.cwd ?? process.cwd());
|
|
262
269
|
await assertDirectory(targetDir);
|
|
263
270
|
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const
|
|
271
|
+
const workspace = await createAnalysisWorkspace(targetDir, {
|
|
272
|
+
config: options.config,
|
|
273
|
+
});
|
|
274
|
+
const { config } = workspace;
|
|
275
|
+
const files = workspace.projects[0].files;
|
|
269
276
|
const inspectedContracts = await readContracts(targetDir, files, config);
|
|
277
|
+
for (const project of workspace.projects.slice(1)) {
|
|
278
|
+
const shared = await readContracts(
|
|
279
|
+
project.targetDir,
|
|
280
|
+
project.files,
|
|
281
|
+
project.config,
|
|
282
|
+
);
|
|
283
|
+
inspectedContracts.contracts.push(
|
|
284
|
+
...shared.contracts.map((contract) => ({
|
|
285
|
+
...contract,
|
|
286
|
+
file: projectPath(project, contract.file),
|
|
287
|
+
})),
|
|
288
|
+
);
|
|
289
|
+
inspectedContracts.queryTransportDiagnostics.push(
|
|
290
|
+
...shared.queryTransportDiagnostics.map((diagnostic) => ({
|
|
291
|
+
...diagnostic,
|
|
292
|
+
...(diagnostic.file
|
|
293
|
+
? { file: projectPath(project, diagnostic.file) }
|
|
294
|
+
: {}),
|
|
295
|
+
...(diagnostic.subject
|
|
296
|
+
? {
|
|
297
|
+
subject: {
|
|
298
|
+
...diagnostic.subject,
|
|
299
|
+
file: projectPath(project, diagnostic.subject.file),
|
|
300
|
+
},
|
|
301
|
+
}
|
|
302
|
+
: {}),
|
|
303
|
+
})),
|
|
304
|
+
);
|
|
305
|
+
}
|
|
270
306
|
const { contracts } = inspectedContracts;
|
|
307
|
+
contracts.sort(compareContracts);
|
|
308
|
+
const convention = inspectConvention(
|
|
309
|
+
files,
|
|
310
|
+
config,
|
|
311
|
+
contracts.length > 0 && workspace.projects.length > 1,
|
|
312
|
+
);
|
|
271
313
|
const matchedRoutes = await inspectRouteSurface(
|
|
272
314
|
targetDir,
|
|
273
315
|
files,
|
|
274
316
|
config,
|
|
275
317
|
contracts,
|
|
276
|
-
|
|
318
|
+
workspace.projects.length > 1 ? createSourceIndex(workspace) : undefined,
|
|
319
|
+
);
|
|
320
|
+
let workspaceRoutesUnresolved = false;
|
|
321
|
+
if (workspace.projects.length > 1) {
|
|
322
|
+
const registration = await workspaceRouteContracts(workspace);
|
|
323
|
+
workspaceRoutesUnresolved = registration.unresolved;
|
|
324
|
+
matchedRoutes.routes = matchedRoutes.routes.map((route) => {
|
|
325
|
+
if (route.handlerSource === "route-local") return route;
|
|
326
|
+
if (!registration.contracts.has(`${route.file}#${route.exportName}`))
|
|
327
|
+
return {
|
|
328
|
+
...route,
|
|
329
|
+
handlerFile: undefined,
|
|
330
|
+
handlerSource: "missing" as const,
|
|
331
|
+
};
|
|
332
|
+
return config.framework === "web"
|
|
333
|
+
? {
|
|
334
|
+
...route,
|
|
335
|
+
handlerFile: config.paths.server,
|
|
336
|
+
handlerSource: "web-server" as const,
|
|
337
|
+
}
|
|
338
|
+
: route;
|
|
339
|
+
});
|
|
340
|
+
}
|
|
277
341
|
const diagnostics = await inspectDiagnostics(
|
|
278
342
|
targetDir,
|
|
279
343
|
files,
|
|
@@ -285,6 +349,15 @@ export async function inspectApp(
|
|
|
285
349
|
inspectedContracts.queryTransportDiagnostics,
|
|
286
350
|
);
|
|
287
351
|
|
|
352
|
+
if (workspaceRoutesUnresolved)
|
|
353
|
+
diagnostics.push({
|
|
354
|
+
severity: "warning",
|
|
355
|
+
code: "BEIGNET_WORKSPACE_ROUTES_UNRESOLVED",
|
|
356
|
+
file: config.paths.server,
|
|
357
|
+
message:
|
|
358
|
+
"Could not fully resolve workspace route composition. Use explicit defineRoutes arrays and defineRouteGroup bindings reachable from the server routes option.",
|
|
359
|
+
});
|
|
360
|
+
|
|
288
361
|
return {
|
|
289
362
|
schemaVersion: 1,
|
|
290
363
|
targetDir,
|
|
@@ -367,11 +440,15 @@ async function buildDoctorFixPlan(
|
|
|
367
440
|
: await loadBeignetConfig(targetDir, files);
|
|
368
441
|
const convention = inspectConvention(files, config);
|
|
369
442
|
const { contracts } = await readContracts(targetDir, files, config);
|
|
443
|
+
const sourceIndex = config.workspace.packages.length
|
|
444
|
+
? createSourceIndex(await createAnalysisWorkspace(targetDir, { config }))
|
|
445
|
+
: undefined;
|
|
370
446
|
const matchedRoutes = await inspectRouteSurface(
|
|
371
447
|
targetDir,
|
|
372
448
|
files,
|
|
373
449
|
config,
|
|
374
450
|
contracts,
|
|
451
|
+
sourceIndex,
|
|
375
452
|
);
|
|
376
453
|
const operations: PlannedDoctorFixOperation[] = [];
|
|
377
454
|
|
|
@@ -896,14 +973,15 @@ async function readContracts(
|
|
|
896
973
|
function inspectConvention(
|
|
897
974
|
files: string[],
|
|
898
975
|
config: ResolvedBeignetConfig,
|
|
976
|
+
sharedContracts = false,
|
|
899
977
|
): InspectConvention {
|
|
900
978
|
const missingNextLayout = missingRequirements(files, [
|
|
901
|
-
`${directoryPath(config.paths.contracts)}
|
|
979
|
+
...(sharedContracts ? [] : [`${directoryPath(config.paths.contracts)}/`]),
|
|
902
980
|
`${directoryPath(config.paths.routes)}/`,
|
|
903
981
|
config.paths.server,
|
|
904
982
|
]);
|
|
905
983
|
const missingWebLayout = missingRequirements(files, [
|
|
906
|
-
`${directoryPath(config.paths.contracts)}
|
|
984
|
+
...(sharedContracts ? [] : [`${directoryPath(config.paths.contracts)}/`]),
|
|
907
985
|
config.paths.server,
|
|
908
986
|
]);
|
|
909
987
|
const missingResourceGenerator = missingRequirements(files, [
|
|
@@ -1571,6 +1649,7 @@ async function readRouteExports(
|
|
|
1571
1649
|
routeFiles: string[],
|
|
1572
1650
|
files: string[],
|
|
1573
1651
|
config: ResolvedBeignetConfig,
|
|
1652
|
+
sourceIndex?: SourceIndex,
|
|
1574
1653
|
): Promise<Map<string, RouteExport[]>> {
|
|
1575
1654
|
const routeExports = new Map<string, RouteExport[]>();
|
|
1576
1655
|
|
|
@@ -1579,20 +1658,28 @@ async function readRouteExports(
|
|
|
1579
1658
|
const source = await readFile(path.join(targetDir, file), "utf8");
|
|
1580
1659
|
routeExports.set(
|
|
1581
1660
|
file,
|
|
1582
|
-
parseRouteExports(
|
|
1661
|
+
await parseRouteExports(
|
|
1662
|
+
source,
|
|
1663
|
+
file,
|
|
1664
|
+
routePath,
|
|
1665
|
+
files,
|
|
1666
|
+
config,
|
|
1667
|
+
sourceIndex,
|
|
1668
|
+
),
|
|
1583
1669
|
);
|
|
1584
1670
|
}
|
|
1585
1671
|
|
|
1586
1672
|
return routeExports;
|
|
1587
1673
|
}
|
|
1588
1674
|
|
|
1589
|
-
function parseRouteExports(
|
|
1675
|
+
async function parseRouteExports(
|
|
1590
1676
|
source: string,
|
|
1591
1677
|
handlerFile: string,
|
|
1592
1678
|
routePath: string,
|
|
1593
1679
|
files: string[],
|
|
1594
1680
|
config: ResolvedBeignetConfig,
|
|
1595
|
-
|
|
1681
|
+
sourceIndex?: SourceIndex,
|
|
1682
|
+
): Promise<RouteExport[]> {
|
|
1596
1683
|
const exports: RouteExport[] = [];
|
|
1597
1684
|
const sourceFile = ts.createSourceFile(
|
|
1598
1685
|
handlerFile,
|
|
@@ -1651,11 +1738,17 @@ function parseRouteExports(
|
|
|
1651
1738
|
|
|
1652
1739
|
if (localName) {
|
|
1653
1740
|
const imported = imports.get(localName);
|
|
1741
|
+
const resolved = await sourceIndex?.resolveValue(
|
|
1742
|
+
handlerFile,
|
|
1743
|
+
ts.factory.createIdentifier(localName),
|
|
1744
|
+
);
|
|
1654
1745
|
exports.push({
|
|
1655
1746
|
method,
|
|
1656
1747
|
handlerFile,
|
|
1657
|
-
contractRef:
|
|
1658
|
-
|
|
1748
|
+
contractRef:
|
|
1749
|
+
resolved?.exportName ?? imported?.importedName ?? localName,
|
|
1750
|
+
contractFile: resolved?.file ?? imported?.contractFile,
|
|
1751
|
+
...(sourceIndex && !resolved ? { contractUnresolved: true } : {}),
|
|
1659
1752
|
source: "route-local",
|
|
1660
1753
|
});
|
|
1661
1754
|
continue;
|
|
@@ -2065,6 +2158,7 @@ async function inspectRouteSurface(
|
|
|
2065
2158
|
files: string[],
|
|
2066
2159
|
config: ResolvedBeignetConfig,
|
|
2067
2160
|
contracts: InspectedContract[],
|
|
2161
|
+
sourceIndex?: SourceIndex,
|
|
2068
2162
|
): Promise<MatchedRoutesResult> {
|
|
2069
2163
|
if (config.framework === "next") {
|
|
2070
2164
|
const routeFiles = files.filter(
|
|
@@ -2077,6 +2171,7 @@ async function inspectRouteSurface(
|
|
|
2077
2171
|
routeFiles,
|
|
2078
2172
|
files,
|
|
2079
2173
|
config,
|
|
2174
|
+
sourceIndex,
|
|
2080
2175
|
);
|
|
2081
2176
|
return matchRoutes(contracts, routeExports);
|
|
2082
2177
|
}
|
|
@@ -2144,6 +2239,7 @@ function findContracts(
|
|
|
2144
2239
|
routeExport: RouteExport,
|
|
2145
2240
|
): InspectedContract[] {
|
|
2146
2241
|
if (routeExport.source === "route-local") {
|
|
2242
|
+
if (routeExport.contractUnresolved) return [];
|
|
2147
2243
|
const contract = contracts.find(
|
|
2148
2244
|
(contract) =>
|
|
2149
2245
|
contract.exportName === routeExport.contractRef &&
|
|
@@ -2391,6 +2487,7 @@ async function inspectCanonicalConformance(
|
|
|
2391
2487
|
files.some((file) => file.startsWith(`${clientDir}/`)) ||
|
|
2392
2488
|
[
|
|
2393
2489
|
"@beignet/react-query",
|
|
2490
|
+
"@beignet/react-form",
|
|
2394
2491
|
"@beignet/react-hook-form",
|
|
2395
2492
|
"@beignet/react-uploads",
|
|
2396
2493
|
"@beignet/nuqs",
|
|
@@ -2437,12 +2534,13 @@ async function inspectCanonicalConformance(
|
|
|
2437
2534
|
}
|
|
2438
2535
|
|
|
2439
2536
|
if (
|
|
2537
|
+
installedPackages.has("@beignet/react-form") ||
|
|
2440
2538
|
installedPackages.has("@beignet/react-hook-form") ||
|
|
2441
2539
|
files.includes(clientForms)
|
|
2442
2540
|
) {
|
|
2443
2541
|
requiredFiles.push({
|
|
2444
2542
|
file: clientForms,
|
|
2445
|
-
reason: `${clientForms} should own
|
|
2543
|
+
reason: `${clientForms} should own form adapter setup (createReactForm or createReactHookForm) when form helpers are used.`,
|
|
2446
2544
|
});
|
|
2447
2545
|
}
|
|
2448
2546
|
|
|
@@ -6338,6 +6436,10 @@ async function inspectPortProviderDrift(
|
|
|
6338
6436
|
const portsSource = files.includes(config.paths.ports)
|
|
6339
6437
|
? await readFile(path.join(targetDir, config.paths.ports), "utf8")
|
|
6340
6438
|
: "";
|
|
6439
|
+
const sharedPorts =
|
|
6440
|
+
config.workspace.packages.length > 0
|
|
6441
|
+
? await analyzePortWiringFiles({ targetDir, files, config })
|
|
6442
|
+
: undefined;
|
|
6341
6443
|
const packageJson = files.includes("package.json")
|
|
6342
6444
|
? await readPackageJson(targetDir, files)
|
|
6343
6445
|
: undefined;
|
|
@@ -6361,7 +6463,12 @@ async function inspectPortProviderDrift(
|
|
|
6361
6463
|
|
|
6362
6464
|
for (const expected of providerPortRequirements) {
|
|
6363
6465
|
if (!installedPackages.has(expected.packageName)) continue;
|
|
6364
|
-
if (
|
|
6466
|
+
if (
|
|
6467
|
+
sharedPorts
|
|
6468
|
+
? sharedPorts.declared.has(expected.portName)
|
|
6469
|
+
: portsSource.includes(`${expected.portName}:`)
|
|
6470
|
+
)
|
|
6471
|
+
continue;
|
|
6365
6472
|
diagnostics.push({
|
|
6366
6473
|
severity: "warning",
|
|
6367
6474
|
code: "BEIGNET_PROVIDER_PORT_MISSING",
|