argsbarg 6.1.2 → 6.1.3
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 +65 -1
- package/README.md +17 -19
- package/bin/argsbarg +10 -0
- package/docs/README.md +4 -3
- package/docs/ai-skills.md +4 -2
- package/docs/bundled-docs.md +50 -25
- package/docs/cli-program.md +52 -10
- package/docs/config-schema.md +10 -11
- package/docs/configure.md +2 -0
- package/docs/decisions.md +40 -0
- package/docs/developing.md +43 -5
- package/docs/http-server.md +171 -0
- package/docs/json-schema-subset.md +51 -0
- package/docs/mcp.md +4 -2
- package/docs/output-schema.md +55 -62
- package/examples/formats.ts +6 -6
- package/examples/full-example/Formula/full-example.rb +35 -0
- package/examples/full-example/README.md +20 -21
- package/examples/full-example/docs/README.md +1 -1
- package/examples/full-example/docs/cli-schema.json +1790 -98
- package/examples/full-example/docs/cli.md +1990 -0
- package/examples/full-example/docs/http.md +28 -29
- package/examples/full-example/docs/mcp.md +8 -22
- package/examples/full-example/docs/openapi.json +783 -50
- package/examples/full-example/docs/skill.md +10 -10
- package/examples/full-example/justfile +11 -1
- package/examples/full-example/src/commands/render-json/__generated__/RenderJsonInputSchema.json +15 -0
- package/examples/full-example/src/commands/render-json/__generated__/index.ts +5 -0
- package/examples/full-example/src/commands/render-json/command.test.ts +46 -0
- package/examples/full-example/src/commands/render-json/command.ts +30 -0
- package/examples/full-example/src/commands/render-json/types.ts +9 -0
- package/examples/full-example/src/commands/status/__generated__/StatusJsonOutputSchema.json +15 -0
- package/examples/full-example/src/commands/status/__generated__/index.ts +2 -2
- package/examples/full-example/src/commands/status/command.ts +5 -13
- package/examples/full-example/src/commands/status/types.ts +1 -14
- package/examples/full-example/src/commands/workspaces/__generated__/WorkspaceNameInputSchema.json +15 -0
- package/examples/full-example/src/commands/workspaces/__generated__/index.ts +5 -0
- package/examples/full-example/src/commands/workspaces/command.test.ts +58 -0
- package/examples/full-example/src/commands/workspaces/command.ts +94 -0
- package/examples/full-example/src/commands/workspaces/types.ts +6 -0
- package/examples/full-example/src/db/index.test.ts +86 -0
- package/examples/full-example/src/db/index.ts +101 -0
- package/examples/full-example/src/db/migrate.test.ts +35 -0
- package/examples/full-example/src/db/migrate.ts +69 -0
- package/examples/full-example/src/db/migrations/001_workspaces.sql +6 -0
- package/examples/full-example/src/db/tables/workspaces.ts +66 -0
- package/examples/full-example/src/program.ts +11 -36
- package/examples/full-example/src/types/argsbarg.d.ts +11 -0
- package/examples/full-example/src/types/md.d.ts +4 -0
- package/examples/full-example/tsconfig.json +5 -2
- package/examples/mcp-test.ts +1 -2
- package/examples/minimal.ts +1 -7
- package/examples/nested.ts +1 -2
- package/examples/option-required.ts +1 -1
- package/examples/servers.ts +4 -5
- package/index.d.ts +431 -136
- package/package.json +19 -2
- package/src/builtins/builtins.test.ts +7 -7
- package/src/builtins/completion-bash.ts +1 -1
- package/src/builtins/completion-fish.ts +1 -1
- package/src/builtins/completion-group.ts +4 -4
- package/src/builtins/completion-simulate-shared.ts +9 -0
- package/src/builtins/completion-zsh.ts +1 -1
- package/src/builtins/config.test.ts +3 -3
- package/src/builtins/config.ts +9 -9
- package/src/builtins/configure-copy.ts +2 -2
- package/src/builtins/configure.ts +4 -4
- package/src/builtins/dispatch.ts +19 -18
- package/src/builtins/export.ts +7 -5
- package/src/builtins/http.ts +68 -0
- package/src/builtins/mcp.ts +28 -4
- package/src/builtins/presentation.ts +6 -6
- package/src/builtins/registry.ts +6 -6
- package/src/builtins/scopes.ts +2 -2
- package/src/builtins/version.ts +1 -1
- package/src/cli-tool/full-example-capabilities.test.ts +10 -15
- package/src/cli-tool/main.ts +1 -1
- package/src/cli-tool/program.ts +3 -2
- package/src/cli-tool/prompt.ts +1 -1
- package/src/cli-tool/run-schemagen.ts +1 -3
- package/src/cli-tool/schemagen/cleanup.ts +6 -7
- package/src/cli-tool/schemagen/discover-schema-roots.ts +66 -120
- package/src/cli-tool/schemagen/index.ts +2 -2
- package/src/cli-tool/schemagen/names.ts +8 -13
- package/src/cli-tool/schemagen/run.ts +21 -28
- package/src/cli-tool/schemagen/schemagen.test.ts +136 -46
- package/src/config/bindings.test.ts +1 -1
- package/src/config/bindings.ts +1 -1
- package/src/config/bootstrap.test.ts +1 -1
- package/src/config/bootstrap.ts +36 -4
- package/src/config/context.test.ts +1 -1
- package/src/config/context.ts +1 -1
- package/src/config/entry.ts +1 -1
- package/src/config/file.test.ts +1 -1
- package/src/config/file.ts +3 -3
- package/src/config/manifest.ts +1 -1
- package/src/config/resolve.test.ts +1 -1
- package/src/config/resolve.ts +1 -1
- package/src/config/schema.ts +1 -1
- package/src/config/validate.ts +1 -1
- package/src/{install → configure/artifacts}/binary-placement.test.ts +1 -1
- package/src/{install → configure/artifacts}/binary-placement.ts +1 -1
- package/src/{install → configure/artifacts}/gh-release-update.ts +1 -1
- package/src/{install → configure/artifacts}/install-validate.test.ts +3 -3
- package/src/{install → configure/artifacts}/mcp-config.ts +1 -1
- package/src/{install → configure/artifacts}/mcp-opencode.test.ts +1 -1
- package/src/{install → configure/artifacts}/mcp-opencode.ts +1 -1
- package/src/{install → configure/artifacts}/paths.ts +5 -5
- package/src/configure/artifacts/plan.ts +24 -0
- package/src/{install → configure/artifacts}/status.test.ts +1 -1
- package/src/{install → configure/artifacts}/status.ts +2 -2
- package/src/{install → configure/artifacts}/target-base.ts +1 -1
- package/src/{install → configure/artifacts}/target-detect.ts +1 -1
- package/src/{install → configure/artifacts}/target-effective.ts +3 -9
- package/src/{install → configure/artifacts}/target-mcp-cli.ts +1 -1
- package/src/{install → configure/artifacts}/target-mcp-json.ts +1 -1
- package/src/{install → configure/artifacts}/target-plan-build.ts +2 -2
- package/src/{install → configure/artifacts}/target-registry.ts +2 -2
- package/src/{install → configure/artifacts}/target-scope.ts +3 -3
- package/src/{install → configure/artifacts}/target-skill.ts +1 -1
- package/src/{install → configure/artifacts}/target-types.ts +2 -2
- package/src/{install → configure/artifacts}/targets/app.ts +5 -5
- package/src/{install → configure/artifacts}/targets/chatgpt-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-code-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-desktop-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/claude-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/codex-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/codex-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/configure.ts +5 -5
- package/src/{install → configure/artifacts}/targets/cursor-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/cursor-skill.ts +2 -2
- package/src/{install → configure/artifacts}/targets/index.ts +1 -1
- package/src/{install → configure/artifacts}/targets/openclaw-mcp.ts +2 -2
- package/src/{install → configure/artifacts}/targets/openclaw-skill.ts +3 -3
- package/src/{install → configure/artifacts}/targets/opencode-mcp.ts +5 -5
- package/src/{install → configure/artifacts}/targets/opencode-skill.ts +3 -3
- package/src/{install → configure/artifacts}/targets.test.ts +1 -1
- package/src/{install → configure/artifacts}/uninstall.ts +1 -1
- package/src/configure/configure.test.ts +11 -11
- package/src/configure/index.ts +14 -14
- package/src/configure/prompt.ts +2 -2
- package/src/{context.ts → core/context.ts} +26 -20
- package/src/{json-leaf.test.ts → core/json-leaf.test.ts} +4 -4
- package/src/{leaf-inputs.test.ts → core/leaf-inputs.test.ts} +7 -7
- package/src/{leaf-inputs.ts → core/leaf-inputs.ts} +16 -12
- package/src/{parse.test.ts → core/parse.test.ts} +97 -109
- package/src/{parse.ts → core/parse.ts} +129 -31
- package/src/{schema.ts → core/schema.ts} +25 -13
- package/src/{types.ts → core/types.ts} +225 -35
- package/src/{validate.ts → core/validate.ts} +39 -29
- package/src/docs/builtin.ts +8 -19
- package/src/docs/{api-guide.test.ts → cli-guide.test.ts} +21 -21
- package/src/docs/{api-guide.ts → cli-guide.ts} +45 -16
- package/src/docs/docs.test.ts +76 -41
- package/src/docs/http-guide.ts +37 -34
- package/src/docs/mcp-guide.ts +12 -14
- package/src/docs/mcp-resources.test.ts +2 -3
- package/src/docs/mcp-resources.ts +6 -11
- package/src/docs/resolve.ts +22 -30
- package/src/docs/save.ts +3 -3
- package/src/exports/cli.ts +47 -0
- package/src/exports/headless.ts +13 -0
- package/src/exports/http.ts +6 -0
- package/src/exports/mcp.ts +6 -0
- package/src/{headless.test.ts → headless/routing.test.ts} +3 -3
- package/src/{headless.ts → headless/routing.ts} +3 -3
- package/src/headless/tool-call.ts +114 -46
- package/src/help.test.ts +152 -0
- package/src/help.ts +3 -3
- package/src/hooks/builtin.ts +20 -0
- package/src/hooks/run.ts +142 -0
- package/src/http/openapi.ts +182 -0
- package/src/http/readiness.ts +78 -0
- package/src/{api → http}/result.ts +16 -5
- package/src/http/routes.ts +329 -0
- package/src/http/server.ts +225 -0
- package/src/index.ts +36 -25
- package/src/log/ecs.test.ts +43 -0
- package/src/log/ecs.ts +59 -0
- package/src/log/emitter.ts +166 -0
- package/src/mcp/bundle.ts +2 -2
- package/src/mcp/claude.test.ts +1 -1
- package/src/mcp/claude.ts +4 -4
- package/src/{hidden-mcpb.test.ts → mcp/hidden-mcpb.test.ts} +10 -9
- package/src/mcp/result.ts +2 -2
- package/src/mcp/server.ts +54 -6
- package/src/mcp/tools.ts +9 -20
- package/src/{capabilities.ts → runtime/capabilities.ts} +11 -11
- package/src/{cli-errors.ts → runtime/cli-errors.ts} +4 -4
- package/src/{cli.ts → runtime/cli.ts} +159 -49
- package/src/runtime/exposure.ts +102 -0
- package/src/{invoke.test.ts → runtime/invoke.test.ts} +31 -7
- package/src/server/context.ts +25 -0
- package/src/server/overrides.ts +112 -0
- package/src/skill/generate.ts +8 -8
- package/src/skill/hint.ts +1 -1
- package/src/skill/install.ts +2 -2
- package/src/skill/naming.ts +1 -1
- package/src/{test-fixtures.ts → test/fixtures.ts} +3 -2
- package/src/{config.integration.test.ts → test/integration/config.test.ts} +8 -8
- package/src/{api.integration.test.ts → test/integration/http.test.ts} +170 -67
- package/src/{mcp.integration.test.ts → test/integration/mcp.test.ts} +11 -57
- package/docs/api-server.md +0 -141
- package/examples/full-example/docs/api.md +0 -511
- package/examples/full-example/src/commands/status/__generated__/outputSchema.json +0 -28
- package/examples/full-example/src/config/__generated__/configSchema.json +0 -40
- package/examples/full-example/src/config/__generated__/index.ts +0 -5
- package/examples/full-example/src/config/types.ts +0 -24
- package/src/api/openapi.ts +0 -117
- package/src/api/server.ts +0 -120
- package/src/builtins/api.ts +0 -38
- package/src/hidden.ts +0 -30
- package/src/install/plan.ts +0 -53
- /package/src/{install → configure/artifacts}/detect-installed.ts +0 -0
- /package/src/{install → configure/artifacts}/gh-release-update.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-codex.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-codex.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-openclaw.test.ts +0 -0
- /package/src/{install → configure/artifacts}/mcp-openclaw.ts +0 -0
- /package/src/{install → configure/artifacts}/normalize-uninstall.ts +0 -0
- /package/src/{install → configure/artifacts}/normalize.ts +0 -0
- /package/src/{install → configure/artifacts}/opts.ts +0 -0
- /package/src/{install → configure/artifacts}/shell.ts +0 -0
- /package/src/{formats.test.ts → core/formats.test.ts} +0 -0
- /package/src/{formats.ts → core/formats.ts} +0 -0
- /package/src/{respond.ts → core/respond.ts} +0 -0
- /package/src/{types.test.ts → core/types.test.ts} +0 -0
- /package/src/{api → http}/schema-deref.test.ts +0 -0
- /package/src/{api → http}/schema-deref.ts +0 -0
|
@@ -3,9 +3,9 @@ Remove stale __generated__/ directories and files after schemagen runs.
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
|
|
6
|
-
import {
|
|
6
|
+
import { join, relative } from "node:path";
|
|
7
7
|
import type { SchemaRoot } from "./discover-schema-roots.ts";
|
|
8
|
-
import { GENERATED_DIR, schemaJsonBasename
|
|
8
|
+
import { GENERATED_DIR, schemaJsonBasename } from "./names.ts";
|
|
9
9
|
|
|
10
10
|
function listGeneratedDirs(srcDir: string, projectRoot: string, out: string[]): void {
|
|
11
11
|
for (const ent of readdirSync(srcDir)) {
|
|
@@ -22,11 +22,11 @@ function listGeneratedDirs(srcDir: string, projectRoot: string, out: string[]):
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
/** Drop orphan `__generated__/` trees and JSON files for removed schema
|
|
25
|
+
/** Drop orphan `__generated__/` trees and JSON files for removed schema roots. */
|
|
26
26
|
export function cleanStaleGenerated(
|
|
27
27
|
projectRoot: string,
|
|
28
28
|
srcDir: string,
|
|
29
|
-
|
|
29
|
+
activeByGeneratedDir: Map<string, SchemaRoot[]>,
|
|
30
30
|
): void {
|
|
31
31
|
const srcPath = join(projectRoot, srcDir);
|
|
32
32
|
if (!existsSync(srcPath)) {
|
|
@@ -38,8 +38,7 @@ export function cleanStaleGenerated(
|
|
|
38
38
|
|
|
39
39
|
for (const relGeneratedDir of generatedDirs) {
|
|
40
40
|
const generatedDir = join(projectRoot, relGeneratedDir);
|
|
41
|
-
const
|
|
42
|
-
const roots = existsSync(join(projectRoot, relTypesPath)) ? (activeBySchemaFile.get(relTypesPath) ?? []) : [];
|
|
41
|
+
const roots = activeByGeneratedDir.get(generatedDir) ?? [];
|
|
43
42
|
|
|
44
43
|
if (roots.length === 0) {
|
|
45
44
|
rmSync(generatedDir, { recursive: true, force: true });
|
|
@@ -47,7 +46,7 @@ export function cleanStaleGenerated(
|
|
|
47
46
|
continue;
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
const keep = new Set(["index.ts", ...roots.map((root) => schemaJsonBasename(root.
|
|
49
|
+
const keep = new Set(["index.ts", ...roots.map((root) => schemaJsonBasename(root.typeName))]);
|
|
51
50
|
for (const ent of readdirSync(generatedDir)) {
|
|
52
51
|
if (keep.has(ent)) {
|
|
53
52
|
continue;
|
|
@@ -1,160 +1,111 @@
|
|
|
1
1
|
/*
|
|
2
|
-
Discovers schema roots
|
|
2
|
+
Discovers schema roots via @sg JSDoc markers on export interface/type declarations.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
6
|
-
import {
|
|
7
|
-
import { TYPES_FILE } from "./names.ts";
|
|
6
|
+
import { join, relative } from "node:path";
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
/** Directories to scan relative to project root. */
|
|
9
|
+
const SEARCH_DIRS = ["src"] as const;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/** Directory names skipped during walk (any depth). */
|
|
12
|
+
const EXCLUDE_DIRS = ["node_modules", "__generated__"] as const;
|
|
13
|
+
|
|
14
|
+
/** File basename patterns skipped (basename match only). */
|
|
15
|
+
const EXCLUDE_FILE_PATTERNS = [/\.test\.ts$/];
|
|
16
|
+
|
|
17
|
+
const SG_JSDOC_RE = /\/\*\*[\s\S]*?@sg[\s\S]*?\*\//g;
|
|
18
|
+
const EXPORT_DECL_RE = /^export\s+(interface|type)\s+(\w+)/;
|
|
12
19
|
|
|
13
20
|
export interface SchemaRoot {
|
|
14
|
-
kind: SchemaRootKind;
|
|
15
21
|
typeName: string;
|
|
16
|
-
/** Path to
|
|
22
|
+
/** Path to the file containing `@sg`, relative to project root (anchors `__generated__/`). */
|
|
17
23
|
path: string;
|
|
18
|
-
/** Path to the file that defines `typeName` (
|
|
24
|
+
/** Path to the file that defines `typeName` (same as `path` for `@sg`). */
|
|
19
25
|
sourcePath: string;
|
|
20
26
|
}
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
inputType: "input",
|
|
29
|
-
outputType: "output",
|
|
30
|
-
};
|
|
28
|
+
function shouldIncludeFile(basename: string): boolean {
|
|
29
|
+
if (!basename.endsWith(".ts") || basename.endsWith(".d.ts")) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
return !EXCLUDE_FILE_PATTERNS.some((pattern) => pattern.test(basename));
|
|
33
|
+
}
|
|
31
34
|
|
|
32
|
-
function
|
|
33
|
-
for (const ent of readdirSync(
|
|
34
|
-
|
|
35
|
+
function walkDir(dir: string, baseDir: string, out: string[]): void {
|
|
36
|
+
for (const ent of readdirSync(dir)) {
|
|
37
|
+
if ((EXCLUDE_DIRS as readonly string[]).includes(ent)) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const full = join(dir, ent);
|
|
35
41
|
const st = statSync(full);
|
|
36
42
|
if (st.isDirectory()) {
|
|
37
|
-
|
|
43
|
+
walkDir(full, baseDir, out);
|
|
38
44
|
continue;
|
|
39
45
|
}
|
|
40
|
-
if (ent
|
|
46
|
+
if (!shouldIncludeFile(ent)) {
|
|
41
47
|
continue;
|
|
42
48
|
}
|
|
43
|
-
|
|
44
|
-
if (HAS_ROLE_EXPORT_RE.test(text)) {
|
|
45
|
-
out.push(relative(baseDir, full));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** True when `typeName` is declared in this file (not a schemagen role alias). */
|
|
51
|
-
function isTypeDefinedInFile(text: string, typeName: string): boolean {
|
|
52
|
-
if (new RegExp(`export\\s+interface\\s+${typeName}\\b`).test(text)) {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
if (new RegExp(`export\\s+type\\s+${typeName}\\s*=`).test(text)) {
|
|
56
|
-
return !["configType", "inputType", "outputType"].includes(typeName);
|
|
49
|
+
out.push(relative(baseDir, full));
|
|
57
50
|
}
|
|
58
|
-
return false;
|
|
59
51
|
}
|
|
60
52
|
|
|
61
|
-
function
|
|
62
|
-
const
|
|
63
|
-
for (const
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
if (!names || !from?.startsWith(".")) {
|
|
53
|
+
function listScannableFiles(projectRoot: string): string[] {
|
|
54
|
+
const files: string[] = [];
|
|
55
|
+
for (const searchDir of SEARCH_DIRS) {
|
|
56
|
+
const abs = join(projectRoot, searchDir);
|
|
57
|
+
if (!existsSync(abs)) {
|
|
67
58
|
continue;
|
|
68
59
|
}
|
|
69
|
-
|
|
70
|
-
const trimmed = part.trim();
|
|
71
|
-
const nameMatch = trimmed.match(/^(?:type\s+)?(\w+)(?:\s+as\s+(\w+))?$/);
|
|
72
|
-
if (!nameMatch?.[1]) {
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
const localName = nameMatch[2] ?? nameMatch[1];
|
|
76
|
-
imports.set(localName, from);
|
|
77
|
-
}
|
|
60
|
+
walkDir(abs, projectRoot, files);
|
|
78
61
|
}
|
|
79
|
-
return
|
|
62
|
+
return files.sort();
|
|
80
63
|
}
|
|
81
64
|
|
|
82
|
-
function
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
65
|
+
function parseTypeNameAfterSgBlock(text: string, blockEndIndex: number, relPath: string): string {
|
|
66
|
+
const rest = text.slice(blockEndIndex);
|
|
67
|
+
const sameLine = rest.match(/^([^\n]*)/)?.[1] ?? "";
|
|
68
|
+
if (sameLine.trim().length > 0) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`${relPath}: @sg JSDoc must be immediately followed by export interface/type (same-line export not supported)`,
|
|
71
|
+
);
|
|
89
72
|
}
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
73
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
manifestText: string,
|
|
98
|
-
typeName: string,
|
|
99
|
-
): string | null {
|
|
100
|
-
const manifestFile = join(projectRoot, manifestRelPath);
|
|
101
|
-
const specifier = parseLocalTypeImports(manifestText).get(typeName);
|
|
102
|
-
if (!specifier) {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
const moduleFile = resolveModuleFile(manifestFile, specifier);
|
|
106
|
-
if (!moduleFile) {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
const moduleText = readFileSync(moduleFile, "utf8");
|
|
110
|
-
if (!isTypeDefinedInFile(moduleText, typeName)) {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
return relative(projectRoot, moduleFile);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function discoverFromFile(projectRoot: string, path: string, text: string): SchemaRoot[] {
|
|
117
|
-
const rolesSeen = new Set<SchemaRole>();
|
|
118
|
-
const roots: SchemaRoot[] = [];
|
|
119
|
-
|
|
120
|
-
for (const match of text.matchAll(ROLE_EXPORT_RE)) {
|
|
121
|
-
const role = match[1] as SchemaRole | undefined;
|
|
122
|
-
const typeName = match[2];
|
|
123
|
-
if (!role || !typeName) {
|
|
124
|
-
continue;
|
|
74
|
+
const lines = rest.split("\n").slice(1);
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
if (line.trim() === "") {
|
|
77
|
+
throw new Error(`${relPath}: @sg JSDoc must be immediately followed by export interface/type`);
|
|
125
78
|
}
|
|
126
|
-
|
|
127
|
-
|
|
79
|
+
const match = line.match(EXPORT_DECL_RE);
|
|
80
|
+
if (match?.[2]) {
|
|
81
|
+
return match[2];
|
|
128
82
|
}
|
|
129
|
-
|
|
83
|
+
throw new Error(`${relPath}: @sg JSDoc must be immediately followed by export interface/type`);
|
|
84
|
+
}
|
|
130
85
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const resolved = resolveAliasedTypeSource(projectRoot, path, text, typeName);
|
|
134
|
-
if (!resolved) {
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
sourcePath = resolved;
|
|
138
|
-
}
|
|
86
|
+
throw new Error(`${relPath}: @sg JSDoc must be immediately followed by export interface/type`);
|
|
87
|
+
}
|
|
139
88
|
|
|
140
|
-
|
|
89
|
+
function discoverFromFile(relPath: string, text: string): SchemaRoot[] {
|
|
90
|
+
const roots: SchemaRoot[] = [];
|
|
91
|
+
for (const match of text.matchAll(SG_JSDOC_RE)) {
|
|
92
|
+
const index = match.index ?? 0;
|
|
93
|
+
const blockEnd = index + match[0].length;
|
|
94
|
+
const typeName = parseTypeNameAfterSgBlock(text, blockEnd, relPath);
|
|
95
|
+
roots.push({ typeName, path: relPath, sourcePath: relPath });
|
|
141
96
|
}
|
|
142
|
-
|
|
143
97
|
return roots;
|
|
144
98
|
}
|
|
145
99
|
|
|
146
|
-
/** Find all schema roots under `
|
|
147
|
-
export function discoverSchemaRoots(projectRoot: string,
|
|
148
|
-
const
|
|
149
|
-
const files: string[] = [];
|
|
150
|
-
listTypesManifestFiles(srcPath, projectRoot, files);
|
|
151
|
-
|
|
100
|
+
/** Find all `@sg` schema roots under `SEARCH_DIRS`. */
|
|
101
|
+
export function discoverSchemaRoots(projectRoot: string, _srcDir = "src"): SchemaRoot[] {
|
|
102
|
+
const files = listScannableFiles(projectRoot);
|
|
152
103
|
const roots: SchemaRoot[] = [];
|
|
153
104
|
const typeOwners = new Map<string, string>();
|
|
154
105
|
|
|
155
|
-
for (const relPath of files
|
|
106
|
+
for (const relPath of files) {
|
|
156
107
|
const text = readFileSync(join(projectRoot, relPath), "utf8");
|
|
157
|
-
for (const root of discoverFromFile(
|
|
108
|
+
for (const root of discoverFromFile(relPath, text)) {
|
|
158
109
|
const prev = typeOwners.get(root.typeName);
|
|
159
110
|
if (prev) {
|
|
160
111
|
throw new Error(`${relPath}: duplicate schema root type ${root.typeName} (already declared in ${prev})`);
|
|
@@ -164,10 +115,5 @@ export function discoverSchemaRoots(projectRoot: string, srcDir = "src"): Schema
|
|
|
164
115
|
}
|
|
165
116
|
}
|
|
166
117
|
|
|
167
|
-
const configRoots = roots.filter((r) => r.kind === "config");
|
|
168
|
-
if (configRoots.length > 1) {
|
|
169
|
-
throw new Error(`multiple config schema roots: ${configRoots.map((r) => `${r.typeName} (${r.path})`).join(", ")}`);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
118
|
return roots;
|
|
173
119
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { discoverSchemaRoots, type SchemaRoot
|
|
2
|
-
export { GENERATED_DIR, schemaExportName, schemaJsonBasename
|
|
1
|
+
export { discoverSchemaRoots, type SchemaRoot } from "./discover-schema-roots.ts";
|
|
2
|
+
export { GENERATED_DIR, schemaExportName, schemaJsonBasename } from "./names.ts";
|
|
3
3
|
export { type RunSchemagenOptions, type RunSchemagenResult, runSchemagen } from "./run.ts";
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
/** Directory name for generated schema artifacts (next to `types.ts`). */
|
|
1
|
+
/** Directory name for generated schema artifacts (colocated with `@sg` source files). */
|
|
4
2
|
export const GENERATED_DIR = "__generated__";
|
|
5
3
|
|
|
6
|
-
/**
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
/** JSON basename for a schema root kind (`outputSchema.json`, etc.). */
|
|
10
|
-
export function schemaJsonBasename(kind: SchemaRootKind): string {
|
|
11
|
-
return `${kind}Schema.json`;
|
|
4
|
+
/** JSON basename for a schema root (`RenderJsonInputSchema.json`, etc.). */
|
|
5
|
+
export function schemaJsonBasename(typeName: string): string {
|
|
6
|
+
return `${typeName}Schema.json`;
|
|
12
7
|
}
|
|
13
8
|
|
|
14
9
|
/** Export const name wired on leaves or `program.appConfig`. */
|
|
15
|
-
export function schemaExportName(
|
|
16
|
-
return `${
|
|
10
|
+
export function schemaExportName(typeName: string): string {
|
|
11
|
+
return `${typeName}Schema`;
|
|
17
12
|
}
|
|
18
13
|
|
|
19
14
|
/** Safe import binding for a schema JSON basename. */
|
|
20
|
-
export function schemaJsonImportVar(
|
|
21
|
-
return `${
|
|
15
|
+
export function schemaJsonImportVar(typeName: string): string {
|
|
16
|
+
return `${typeName}SchemaJson`;
|
|
22
17
|
}
|
|
@@ -6,24 +6,20 @@ import { mkdirSync, writeFileSync } from "node:fs";
|
|
|
6
6
|
import { dirname, join, relative } from "node:path";
|
|
7
7
|
import { createGenerator } from "ts-json-schema-generator";
|
|
8
8
|
import { cleanStaleGenerated } from "./cleanup.ts";
|
|
9
|
-
import { discoverSchemaRoots, type SchemaRoot
|
|
9
|
+
import { discoverSchemaRoots, type SchemaRoot } from "./discover-schema-roots.ts";
|
|
10
10
|
import { GENERATED_DIR, schemaExportName, schemaJsonBasename, schemaJsonImportVar } from "./names.ts";
|
|
11
11
|
|
|
12
|
-
const KIND_ORDER: SchemaRootKind[] = ["config", "input", "output"];
|
|
13
|
-
|
|
14
12
|
export interface RunSchemagenOptions {
|
|
15
13
|
/** Project root (default: `process.cwd()`). */
|
|
16
14
|
projectRoot?: string;
|
|
17
|
-
/** Source tree directory relative to project root (default: `src`). */
|
|
15
|
+
/** Source tree directory relative to project root (default: `src`). Unused; kept for API compat. */
|
|
18
16
|
srcDir?: string;
|
|
19
17
|
/** Path to tsconfig relative to project root (default: `tsconfig.json`). */
|
|
20
18
|
tsconfig?: string;
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
export interface RunSchemagenResult {
|
|
24
|
-
|
|
25
|
-
inputRoots: number;
|
|
26
|
-
outputRoots: number;
|
|
22
|
+
schemas: number;
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
function resolveTsconfig(projectRoot: string, tsconfig: string): string {
|
|
@@ -35,6 +31,10 @@ function resolveTsconfig(projectRoot: string, tsconfig: string): string {
|
|
|
35
31
|
}
|
|
36
32
|
}
|
|
37
33
|
|
|
34
|
+
function generatedDirForRoot(projectRoot: string, root: SchemaRoot): string {
|
|
35
|
+
return join(dirname(join(projectRoot, root.path)), GENERATED_DIR);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
38
|
function generateJson(projectRoot: string, tsconfigPath: string, root: SchemaRoot): Record<string, unknown> {
|
|
39
39
|
const typeFile = join(projectRoot, root.sourcePath);
|
|
40
40
|
const generator = createGenerator({
|
|
@@ -44,27 +44,25 @@ function generateJson(projectRoot: string, tsconfigPath: string, root: SchemaRoo
|
|
|
44
44
|
topRef: false,
|
|
45
45
|
skipTypeCheck: false,
|
|
46
46
|
jsDoc: "extended",
|
|
47
|
-
additionalProperties:
|
|
47
|
+
additionalProperties: false,
|
|
48
48
|
});
|
|
49
49
|
const schema = generator.createSchema(root.typeName) as Record<string, unknown>;
|
|
50
|
-
|
|
51
|
-
schema.additionalProperties = false;
|
|
52
|
-
}
|
|
50
|
+
schema.additionalProperties = false;
|
|
53
51
|
return schema;
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
function writeGeneratedIndex(generatedDir: string, roots: SchemaRoot[]): void {
|
|
57
|
-
const sorted = [...roots].sort((left, right) =>
|
|
55
|
+
const sorted = [...roots].sort((left, right) => left.typeName.localeCompare(right.typeName));
|
|
58
56
|
const lines = ["// Auto-generated by argsbarg schemagen — do not edit by hand.", ""];
|
|
59
57
|
for (const root of sorted) {
|
|
60
|
-
lines.push(`import ${schemaJsonImportVar(root.
|
|
58
|
+
lines.push(`import ${schemaJsonImportVar(root.typeName)} from "./${schemaJsonBasename(root.typeName)}";`);
|
|
61
59
|
}
|
|
62
60
|
if (sorted.length > 0) {
|
|
63
61
|
lines.push("");
|
|
64
62
|
}
|
|
65
63
|
for (const root of sorted) {
|
|
66
64
|
lines.push(
|
|
67
|
-
`export const ${schemaExportName(root.
|
|
65
|
+
`export const ${schemaExportName(root.typeName)} = ${schemaJsonImportVar(root.typeName)} as Record<string, unknown>;`,
|
|
68
66
|
);
|
|
69
67
|
lines.push("");
|
|
70
68
|
}
|
|
@@ -78,32 +76,27 @@ export function runSchemagen(options: RunSchemagenOptions = {}): RunSchemagenRes
|
|
|
78
76
|
const tsconfigPath = resolveTsconfig(projectRoot, options.tsconfig ?? "tsconfig.json");
|
|
79
77
|
|
|
80
78
|
const roots = discoverSchemaRoots(projectRoot, srcDir);
|
|
81
|
-
const
|
|
79
|
+
const byGeneratedDir = new Map<string, SchemaRoot[]>();
|
|
82
80
|
|
|
83
81
|
for (const root of roots) {
|
|
84
82
|
const schema = generateJson(projectRoot, tsconfigPath, root);
|
|
85
|
-
const generatedDir =
|
|
83
|
+
const generatedDir = generatedDirForRoot(projectRoot, root);
|
|
86
84
|
mkdirSync(generatedDir, { recursive: true });
|
|
87
|
-
const jsonPath = join(generatedDir, schemaJsonBasename(root.
|
|
85
|
+
const jsonPath = join(generatedDir, schemaJsonBasename(root.typeName));
|
|
88
86
|
writeFileSync(jsonPath, `${JSON.stringify(schema, null, 2)}\n`);
|
|
89
87
|
console.log(`wrote ${relative(projectRoot, jsonPath)} (${root.typeName})`);
|
|
90
88
|
|
|
91
|
-
const list =
|
|
89
|
+
const list = byGeneratedDir.get(generatedDir) ?? [];
|
|
92
90
|
list.push(root);
|
|
93
|
-
|
|
91
|
+
byGeneratedDir.set(generatedDir, list);
|
|
94
92
|
}
|
|
95
93
|
|
|
96
|
-
for (const [
|
|
97
|
-
|
|
98
|
-
writeGeneratedIndex(generatedDir, fileRoots);
|
|
94
|
+
for (const [generatedDir, dirRoots] of byGeneratedDir) {
|
|
95
|
+
writeGeneratedIndex(generatedDir, dirRoots);
|
|
99
96
|
console.log(`wrote ${relative(projectRoot, join(generatedDir, "index.ts"))}`);
|
|
100
97
|
}
|
|
101
98
|
|
|
102
|
-
cleanStaleGenerated(projectRoot, srcDir,
|
|
99
|
+
cleanStaleGenerated(projectRoot, srcDir, byGeneratedDir);
|
|
103
100
|
|
|
104
|
-
return {
|
|
105
|
-
configRoots: roots.filter((r) => r.kind === "config").length,
|
|
106
|
-
inputRoots: roots.filter((r) => r.kind === "input").length,
|
|
107
|
-
outputRoots: roots.filter((r) => r.kind === "output").length,
|
|
108
|
-
};
|
|
101
|
+
return { schemas: roots.length };
|
|
109
102
|
}
|