@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/dist/inspect.js
CHANGED
|
@@ -8,7 +8,9 @@ import { contractFactoryBindings, isImportedContractFactoryCall, } from "./analy
|
|
|
8
8
|
import { canonicalFeatureLayerPath } from "./analysis/layers.js";
|
|
9
9
|
import { analyzePortWiringFiles } from "./analysis/port-wiring.js";
|
|
10
10
|
import { analysisSourceFileExtensions, isAnalysisSourceFile, isAnalysisSourceFileExtension, isJavaScriptSourceFile, javascriptAnalysisSourceFileExtensions, sourceFileScriptKind, stripSourceFileExtension, typescriptAnalysisSourceFileExtensions, } from "./analysis/source-files.js";
|
|
11
|
-
import { exportedVariableDeclarations } from "./analysis/source-index.js";
|
|
11
|
+
import { createSourceIndex, exportedVariableDeclarations, } from "./analysis/source-index.js";
|
|
12
|
+
import { createAnalysisWorkspace, projectPath } from "./analysis/workspace.js";
|
|
13
|
+
import { workspaceRouteContracts } from "./analysis/workspace-routes.js";
|
|
12
14
|
import { createPainter } from "./ansi.js";
|
|
13
15
|
import { clientDirPath, clientFormsPath, clientIndexPath, defaultBeignetConfig, directoryPath, loadBeignetConfig, normalizePath, resolveConfig, } from "./config.js";
|
|
14
16
|
import { renderDatabaseSchema, resolveDatabaseSchemaDialect } from "./db.js";
|
|
@@ -24,15 +26,67 @@ import { getCliVersion } from "./version.js";
|
|
|
24
26
|
export async function inspectApp(options = {}) {
|
|
25
27
|
const targetDir = path.resolve(options.cwd ?? process.cwd());
|
|
26
28
|
await assertDirectory(targetDir);
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
29
|
+
const workspace = await createAnalysisWorkspace(targetDir, {
|
|
30
|
+
config: options.config,
|
|
31
|
+
});
|
|
32
|
+
const { config } = workspace;
|
|
33
|
+
const files = workspace.projects[0].files;
|
|
32
34
|
const inspectedContracts = await readContracts(targetDir, files, config);
|
|
35
|
+
for (const project of workspace.projects.slice(1)) {
|
|
36
|
+
const shared = await readContracts(project.targetDir, project.files, project.config);
|
|
37
|
+
inspectedContracts.contracts.push(...shared.contracts.map((contract) => ({
|
|
38
|
+
...contract,
|
|
39
|
+
file: projectPath(project, contract.file),
|
|
40
|
+
})));
|
|
41
|
+
inspectedContracts.queryTransportDiagnostics.push(...shared.queryTransportDiagnostics.map((diagnostic) => ({
|
|
42
|
+
...diagnostic,
|
|
43
|
+
...(diagnostic.file
|
|
44
|
+
? { file: projectPath(project, diagnostic.file) }
|
|
45
|
+
: {}),
|
|
46
|
+
...(diagnostic.subject
|
|
47
|
+
? {
|
|
48
|
+
subject: {
|
|
49
|
+
...diagnostic.subject,
|
|
50
|
+
file: projectPath(project, diagnostic.subject.file),
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
: {}),
|
|
54
|
+
})));
|
|
55
|
+
}
|
|
33
56
|
const { contracts } = inspectedContracts;
|
|
34
|
-
|
|
57
|
+
contracts.sort(compareContracts);
|
|
58
|
+
const convention = inspectConvention(files, config, contracts.length > 0 && workspace.projects.length > 1);
|
|
59
|
+
const matchedRoutes = await inspectRouteSurface(targetDir, files, config, contracts, workspace.projects.length > 1 ? createSourceIndex(workspace) : undefined);
|
|
60
|
+
let workspaceRoutesUnresolved = false;
|
|
61
|
+
if (workspace.projects.length > 1) {
|
|
62
|
+
const registration = await workspaceRouteContracts(workspace);
|
|
63
|
+
workspaceRoutesUnresolved = registration.unresolved;
|
|
64
|
+
matchedRoutes.routes = matchedRoutes.routes.map((route) => {
|
|
65
|
+
if (route.handlerSource === "route-local")
|
|
66
|
+
return route;
|
|
67
|
+
if (!registration.contracts.has(`${route.file}#${route.exportName}`))
|
|
68
|
+
return {
|
|
69
|
+
...route,
|
|
70
|
+
handlerFile: undefined,
|
|
71
|
+
handlerSource: "missing",
|
|
72
|
+
};
|
|
73
|
+
return config.framework === "web"
|
|
74
|
+
? {
|
|
75
|
+
...route,
|
|
76
|
+
handlerFile: config.paths.server,
|
|
77
|
+
handlerSource: "web-server",
|
|
78
|
+
}
|
|
79
|
+
: route;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
35
82
|
const diagnostics = await inspectDiagnostics(targetDir, files, config, convention, contracts, matchedRoutes, Boolean(options.strict), inspectedContracts.queryTransportDiagnostics);
|
|
83
|
+
if (workspaceRoutesUnresolved)
|
|
84
|
+
diagnostics.push({
|
|
85
|
+
severity: "warning",
|
|
86
|
+
code: "BEIGNET_WORKSPACE_ROUTES_UNRESOLVED",
|
|
87
|
+
file: config.paths.server,
|
|
88
|
+
message: "Could not fully resolve workspace route composition. Use explicit defineRoutes arrays and defineRouteGroup bindings reachable from the server routes option.",
|
|
89
|
+
});
|
|
36
90
|
return {
|
|
37
91
|
schemaVersion: 1,
|
|
38
92
|
targetDir,
|
|
@@ -87,7 +141,10 @@ async function buildDoctorFixPlan(options = {}) {
|
|
|
87
141
|
: await loadBeignetConfig(targetDir, files);
|
|
88
142
|
const convention = inspectConvention(files, config);
|
|
89
143
|
const { contracts } = await readContracts(targetDir, files, config);
|
|
90
|
-
const
|
|
144
|
+
const sourceIndex = config.workspace.packages.length
|
|
145
|
+
? createSourceIndex(await createAnalysisWorkspace(targetDir, { config }))
|
|
146
|
+
: undefined;
|
|
147
|
+
const matchedRoutes = await inspectRouteSurface(targetDir, files, config, contracts, sourceIndex);
|
|
91
148
|
const operations = [];
|
|
92
149
|
const packageFix = await planPackageScripts(targetDir, files, convention);
|
|
93
150
|
if (packageFix)
|
|
@@ -441,14 +498,14 @@ async function readContracts(targetDir, files, config) {
|
|
|
441
498
|
queryTransportDiagnostics,
|
|
442
499
|
};
|
|
443
500
|
}
|
|
444
|
-
function inspectConvention(files, config) {
|
|
501
|
+
function inspectConvention(files, config, sharedContracts = false) {
|
|
445
502
|
const missingNextLayout = missingRequirements(files, [
|
|
446
|
-
`${directoryPath(config.paths.contracts)}
|
|
503
|
+
...(sharedContracts ? [] : [`${directoryPath(config.paths.contracts)}/`]),
|
|
447
504
|
`${directoryPath(config.paths.routes)}/`,
|
|
448
505
|
config.paths.server,
|
|
449
506
|
]);
|
|
450
507
|
const missingWebLayout = missingRequirements(files, [
|
|
451
|
-
`${directoryPath(config.paths.contracts)}
|
|
508
|
+
...(sharedContracts ? [] : [`${directoryPath(config.paths.contracts)}/`]),
|
|
452
509
|
config.paths.server,
|
|
453
510
|
]);
|
|
454
511
|
const missingResourceGenerator = missingRequirements(files, [
|
|
@@ -930,16 +987,16 @@ function normalizeContractPath(routePath) {
|
|
|
930
987
|
const segments = routePath.split("/").filter(Boolean);
|
|
931
988
|
return segments.length === 0 ? "/" : `/${segments.join("/")}`;
|
|
932
989
|
}
|
|
933
|
-
async function readRouteExports(targetDir, routeFiles, files, config) {
|
|
990
|
+
async function readRouteExports(targetDir, routeFiles, files, config, sourceIndex) {
|
|
934
991
|
const routeExports = new Map();
|
|
935
992
|
for (const file of routeFiles) {
|
|
936
993
|
const routePath = nextRoutePath(file, config.paths.routes);
|
|
937
994
|
const source = await readFile(path.join(targetDir, file), "utf8");
|
|
938
|
-
routeExports.set(file, parseRouteExports(source, file, routePath, files, config));
|
|
995
|
+
routeExports.set(file, await parseRouteExports(source, file, routePath, files, config, sourceIndex));
|
|
939
996
|
}
|
|
940
997
|
return routeExports;
|
|
941
998
|
}
|
|
942
|
-
function parseRouteExports(source, handlerFile, routePath, files, config) {
|
|
999
|
+
async function parseRouteExports(source, handlerFile, routePath, files, config, sourceIndex) {
|
|
943
1000
|
const exports = [];
|
|
944
1001
|
const sourceFile = ts.createSourceFile(handlerFile, source, ts.ScriptTarget.Latest, true, sourceFileScriptKind(handlerFile));
|
|
945
1002
|
const imports = parseRouteNamedImports(sourceFile, config, handlerFile, files);
|
|
@@ -977,11 +1034,13 @@ function parseRouteExports(source, handlerFile, routePath, files, config) {
|
|
|
977
1034
|
const localName = routeLocalContractIdentifier(declaration.initializer);
|
|
978
1035
|
if (localName) {
|
|
979
1036
|
const imported = imports.get(localName);
|
|
1037
|
+
const resolved = await sourceIndex?.resolveValue(handlerFile, ts.factory.createIdentifier(localName));
|
|
980
1038
|
exports.push({
|
|
981
1039
|
method,
|
|
982
1040
|
handlerFile,
|
|
983
|
-
contractRef: imported?.importedName ?? localName,
|
|
984
|
-
contractFile: imported?.contractFile,
|
|
1041
|
+
contractRef: resolved?.exportName ?? imported?.importedName ?? localName,
|
|
1042
|
+
contractFile: resolved?.file ?? imported?.contractFile,
|
|
1043
|
+
...(sourceIndex && !resolved ? { contractUnresolved: true } : {}),
|
|
985
1044
|
source: "route-local",
|
|
986
1045
|
});
|
|
987
1046
|
continue;
|
|
@@ -1262,11 +1321,11 @@ function matchRoutes(contracts, routeExports) {
|
|
|
1262
1321
|
unmatchedRouteHandlers,
|
|
1263
1322
|
};
|
|
1264
1323
|
}
|
|
1265
|
-
async function inspectRouteSurface(targetDir, files, config, contracts) {
|
|
1324
|
+
async function inspectRouteSurface(targetDir, files, config, contracts, sourceIndex) {
|
|
1266
1325
|
if (config.framework === "next") {
|
|
1267
1326
|
const routeFiles = files.filter((file) => file.startsWith(`${directoryPath(config.paths.routes)}/`) &&
|
|
1268
1327
|
isNextRouteFile(file, config));
|
|
1269
|
-
const routeExports = await readRouteExports(targetDir, routeFiles, files, config);
|
|
1328
|
+
const routeExports = await readRouteExports(targetDir, routeFiles, files, config, sourceIndex);
|
|
1270
1329
|
return matchRoutes(contracts, routeExports);
|
|
1271
1330
|
}
|
|
1272
1331
|
if (!files.includes(config.paths.server)) {
|
|
@@ -1303,6 +1362,8 @@ function findContract(contracts, routeExport) {
|
|
|
1303
1362
|
}
|
|
1304
1363
|
function findContracts(contracts, routeExport) {
|
|
1305
1364
|
if (routeExport.source === "route-local") {
|
|
1365
|
+
if (routeExport.contractUnresolved)
|
|
1366
|
+
return [];
|
|
1306
1367
|
const contract = contracts.find((contract) => contract.exportName === routeExport.contractRef &&
|
|
1307
1368
|
(!routeExport.contractFile ||
|
|
1308
1369
|
contract.file === routeExport.contractFile ||
|
|
@@ -1402,6 +1463,7 @@ async function inspectCanonicalConformance(targetDir, files, config, convention,
|
|
|
1402
1463
|
files.some((file) => file.startsWith(`${clientDir}/`)) ||
|
|
1403
1464
|
[
|
|
1404
1465
|
"@beignet/react-query",
|
|
1466
|
+
"@beignet/react-form",
|
|
1405
1467
|
"@beignet/react-hook-form",
|
|
1406
1468
|
"@beignet/react-uploads",
|
|
1407
1469
|
"@beignet/nuqs",
|
|
@@ -1438,11 +1500,12 @@ async function inspectCanonicalConformance(targetDir, files, config, convention,
|
|
|
1438
1500
|
reason: `${clientIndex} should own the typed client and frontend adapter setup.`,
|
|
1439
1501
|
});
|
|
1440
1502
|
}
|
|
1441
|
-
if (installedPackages.has("@beignet/react-
|
|
1503
|
+
if (installedPackages.has("@beignet/react-form") ||
|
|
1504
|
+
installedPackages.has("@beignet/react-hook-form") ||
|
|
1442
1505
|
files.includes(clientForms)) {
|
|
1443
1506
|
requiredFiles.push({
|
|
1444
1507
|
file: clientForms,
|
|
1445
|
-
reason: `${clientForms} should own
|
|
1508
|
+
reason: `${clientForms} should own form adapter setup (createReactForm or createReactHookForm) when form helpers are used.`,
|
|
1446
1509
|
});
|
|
1447
1510
|
}
|
|
1448
1511
|
for (const required of requiredFiles) {
|
|
@@ -3956,6 +4019,9 @@ async function inspectPortProviderDrift(targetDir, files, config, convention) {
|
|
|
3956
4019
|
const portsSource = files.includes(config.paths.ports)
|
|
3957
4020
|
? await readFile(path.join(targetDir, config.paths.ports), "utf8")
|
|
3958
4021
|
: "";
|
|
4022
|
+
const sharedPorts = config.workspace.packages.length > 0
|
|
4023
|
+
? await analyzePortWiringFiles({ targetDir, files, config })
|
|
4024
|
+
: undefined;
|
|
3959
4025
|
const packageJson = files.includes("package.json")
|
|
3960
4026
|
? await readPackageJson(targetDir, files)
|
|
3961
4027
|
: undefined;
|
|
@@ -3973,7 +4039,9 @@ async function inspectPortProviderDrift(targetDir, files, config, convention) {
|
|
|
3973
4039
|
for (const expected of providerPortRequirements) {
|
|
3974
4040
|
if (!installedPackages.has(expected.packageName))
|
|
3975
4041
|
continue;
|
|
3976
|
-
if (
|
|
4042
|
+
if (sharedPorts
|
|
4043
|
+
? sharedPorts.declared.has(expected.portName)
|
|
4044
|
+
: portsSource.includes(`${expected.portName}:`))
|
|
3977
4045
|
continue;
|
|
3978
4046
|
diagnostics.push({
|
|
3979
4047
|
severity: "warning",
|