Files
WRNexusJS/packages/cli/src/inspect.ts
T
Clintchiz 586a6db8ff
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s
release: WRNexusJS 0.8.0
2026-08-02 23:18:51 +05:30

201 lines
6.6 KiB
TypeScript

import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
import { dirname, join, relative, resolve } from "node:path";
import { buildRouter, createRouteManifest, nameRoutes } from "@wrnexus/router";
import { createPluginRunner, discoverPlugins } from "@wrnexus/plugin";
import { loadAppConfig } from "@wrnexus/styles";
import { uiComponentsDir } from "@wrnexus/ui";
import { parse } from "@wrnexus/syntax";
export type InspectTarget =
"packages" | "plugins" | "routes" | "assets" | "runtimes" | "styles" | "migrations" | "bundle";
export function inspectComponent(appRoot: string, requestedName: string): unknown {
const root = resolve(appRoot);
const router = buildRouter(join(root, "app"), { componentDirs: [uiComponentsDir()] });
const component = router.components.find(
(candidate) => candidate.name.toLowerCase() === requestedName.toLowerCase(),
);
if (!component) throw new Error(`Component not found: ${requestedName}`);
const ast = parse(readFileSync(component.file, "utf8"));
return {
name: ast.name,
file: relative(root, component.file).replace(/\\/g, "/"),
props: ast.props.map(({ name, valueType, required, default: defaultValue }) => ({
name,
type: valueType ?? "unknown",
required,
...(defaultValue === undefined || defaultValue === "undefined"
? {}
: { default: defaultValue }),
})),
outputs: ast.outputs.map(({ name, payload }) => ({ name, payload: payload ?? null })),
functions: ast.runtimeFunctions.map(({ name, runtime, async, parameters, returnType }) => ({
name,
runtime,
async,
parameters,
returnType: returnType ?? "unknown",
})),
};
}
function json(path: string): Record<string, any> | null {
try {
return JSON.parse(readFileSync(path, "utf8")) as Record<string, any>;
} catch {
return null;
}
}
function workspaceRoot(start: string): string {
let current = resolve(start);
while (true) {
if (json(join(current, "package.json"))?.workspaces) return current;
const parent = dirname(current);
if (parent === current) return resolve(start);
current = parent;
}
}
function packageRows(root: string) {
const dirs = [join(root, "packages"), join(root, "services")];
const rows: Array<Record<string, unknown>> = [];
for (const dir of dirs) {
if (!existsSync(dir)) continue;
for (const name of readdirSync(dir)) {
const path = join(dir, name);
if (!statSync(path).isDirectory()) continue;
const pkg = json(join(path, "package.json"));
if (!pkg?.name) continue;
rows.push({
name: pkg.name,
version: pkg.version,
private: pkg.private === true,
path: relative(root, path).replace(/\\/g, "/"),
plugin: !!pkg.wrnexus?.plugin,
});
}
}
return rows.sort((a, b) => String(a.name).localeCompare(String(b.name)));
}
export async function inspectProject(appRoot: string, target: InspectTarget): Promise<unknown> {
const root = resolve(appRoot);
const workspace = workspaceRoot(root);
if (target === "packages") return packageRows(workspace);
if (target === "bundle") {
const report = join(root, "dist", "build-report.json");
if (!existsSync(report)) throw new Error("Run `wrnexus build` before inspecting the bundle.");
return json(report);
}
const config = await loadAppConfig(root);
const input = await discoverPlugins(root, config.plugins, {
includeDevDependencies: true,
strict: true,
});
const runner = createPluginRunner(input, {
root,
mode: "development",
command: "dev",
metadata: new Map(),
warn: () => {},
});
await runner.configure(config as Record<string, unknown>);
await runner.configResolved(config as Readonly<Record<string, unknown>>);
const contributions = await runner.contributions();
if (target === "plugins")
return runner.plugins.map((plugin) => ({
name: plugin.name,
version: plugin.version,
enforce: plugin.enforce ?? "normal",
}));
if (target === "assets")
return contributions.assets.map(({ id, publicPath, contentType, immutable }) => ({
id,
publicPath,
contentType,
immutable: immutable ?? false,
}));
if (target === "runtimes")
return contributions.clientRuntimes.map(
({ id, publicPath, type, load, singleton, bundle }) => ({
id,
publicPath,
type,
load,
singleton,
bundle,
}),
);
if (target === "styles")
return contributions.styles.map(({ id, entry, source, order }) => ({
id,
entry,
source,
order: order ?? "normal",
}));
if (target === "migrations")
return contributions.migrations.map(({ id, entry, source, database }) => ({
id,
entry,
inline: source !== undefined,
database: database ?? "default",
}));
const router = buildRouter(join(root, "app"), {
componentDirs: [uiComponentsDir(), ...contributions.componentDirs],
externalRoutes: contributions.routes,
middlewareFiles: contributions.middleware,
});
return {
pages: createRouteManifest(nameRoutes(router.pages)),
api: createRouteManifest(nameRoutes(router.api)),
realtime: createRouteManifest(nameRoutes(router.realtime)),
middleware: router.middlewareFiles.map((file) => relative(root, file).replace(/\\/g, "/")),
components: router.components.map((component) => ({
...component,
file: relative(root, component.file).replace(/\\/g, "/"),
})),
};
}
export async function runInspect(
appRoot: string,
targetArg?: string,
args: string[] = [],
): Promise<void> {
const target = (targetArg ?? "plugins") as InspectTarget;
if (
![
"packages",
"plugins",
"routes",
"assets",
"runtimes",
"styles",
"migrations",
"bundle",
].includes(target)
)
throw new Error(`Unknown inspect target: ${target}`);
const value = await inspectProject(appRoot, target);
if (args.includes("--json")) {
console.log(JSON.stringify(value, null, 2));
return;
}
console.log(`WRNexus ${target}\n`);
if (Array.isArray(value))
for (const row of value)
console.log(
` ${Object.entries(row as Record<string, unknown>)
.map(([key, item]) => `${key}=${String(item)}`)
.join(" ")}`,
);
else console.log(JSON.stringify(value, null, 2));
}
export function runInspectComponent(appRoot: string, name: string, args: string[] = []): void {
const value = inspectComponent(appRoot, name);
if (args.includes("--json")) console.log(JSON.stringify(value, null, 2));
else {
console.log(`WRNexus component ${name}\n`);
console.log(JSON.stringify(value, null, 2));
}
}