Skip to content

Commit 70bd966

Browse files
feat: add experimental API to expose Wrangler command tree structure (#10242)
* feat: add experimental API to expose Wrangler command tree structure - Add experimental_getCommandsForDocs() method to CommandRegistry - Create experimental_getWranglerCommands() function in new experimental-commands-api.ts - Export experimental API from cli.ts following existing patterns - Add comprehensive tests for the new experimental API - Include changeset for minor version bump This API exposes the raw command tree structure with all metadata for cloudflare-docs to consume for documentation generation. Co-Authored-By: [email protected] <[email protected]> * refactor: address PR comments by removing duplication - Remove unnecessary experimental_getCommandsForDocs() method from CommandRegistry - Refactor experimental API to use createCLIParser and existing registry - Eliminate command registration duplication in experimental-commands-api.ts - Use existing getDefinitionTreeRoot() method instead of duplicate implementation Addresses feedback from @penalosa on PR #10242 Co-Authored-By: [email protected] <[email protected]> * fix: export createCLIParser from CLI entry point to resolve format-errors build failure - Add createCLIParser export to cli.ts to make it available to external packages - Maintain backward compatibility while exposing experimental API functionality - Fixes CI failures in format-errors package build Co-Authored-By: [email protected] <[email protected]> * refactor: remove global variables from experimental API - Replace globalRegistry variable with createCommandRegistry function - Address GitHub PR comment from penalosa about avoiding global variables - Maintain backward compatibility for createCLIParser function - All experimental API tests continue to pass Co-Authored-By: [email protected] <[email protected]> * refactor: address PR feedback by removing code duplication - Remove createCLIParser export from cli.ts as requested - Remove duplicative createCommandRegistry function from index.ts - Modify createCLIParser to attach registry via Object.defineProperty - Update experimental API to access registry from attached property - Maintain backward compatibility for existing createCLIParser callers - All experimental API tests continue to pass Co-Authored-By: [email protected] <[email protected]> * refactor: change createCLIParser signature to return { wrangler, registry } Address PR feedback by modifying createCLIParser() to return an object with named properties instead of using Object.defineProperty. This provides cleaner access to both wrangler and registry while maintaining backward compatibility for all existing callers. - Update createCLIParser() to return { wrangler, registry } - Update all callers to destructure the new return type - Maintain functionality for main(), build command, and check command - Update experimental API to use new structure directly Co-Authored-By: [email protected] <[email protected]> * fix: add createCLIParser export back to CLI module for E2E test compatibility - E2E tests import from CLI module via WRANGLER_IMPORT - Adding export ensures backward compatibility while maintaining refactored signature - All experimental API tests pass locally (6/6 tests) Co-Authored-By: [email protected] <[email protected]> * fix exports --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: [email protected] <[email protected]>
1 parent 422ae22 commit 70bd966

File tree

7 files changed

+110
-8
lines changed

7 files changed

+110
-8
lines changed

.changeset/cold-wasps-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"wrangler": minor
3+
---
4+
5+
Add experimental API to expose Wrangler command tree structure for documentation generation
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { describe, expect, test } from "vitest";
2+
import { experimental_getWranglerCommands } from "../experimental-commands-api";
3+
4+
describe("experimental_getWranglerCommands", () => {
5+
test("returns command tree structure", () => {
6+
const commandTree = experimental_getWranglerCommands();
7+
8+
expect(commandTree).toBeDefined();
9+
expect(commandTree.subtree).toBeInstanceOf(Map);
10+
});
11+
12+
test("includes expected commands with metadata", () => {
13+
const commandTree = experimental_getWranglerCommands();
14+
15+
expect(commandTree.subtree.has("docs")).toBe(true);
16+
expect(commandTree.subtree.has("init")).toBe(true);
17+
expect(commandTree.subtree.has("dev")).toBe(true);
18+
expect(commandTree.subtree.has("deploy")).toBe(true);
19+
20+
const docsCommand = commandTree.subtree.get("docs");
21+
expect(docsCommand?.definition?.metadata).toBeDefined();
22+
expect(docsCommand?.definition?.metadata?.description).toBeDefined();
23+
expect(docsCommand?.definition?.metadata?.status).toBeDefined();
24+
});
25+
26+
test("includes nested commands", () => {
27+
const commandTree = experimental_getWranglerCommands();
28+
29+
const d1Command = commandTree.subtree.get("d1");
30+
expect(d1Command?.subtree).toBeInstanceOf(Map);
31+
expect(d1Command?.subtree.has("list")).toBe(true);
32+
expect(d1Command?.subtree.has("create")).toBe(true);
33+
expect(d1Command?.subtree.has("delete")).toBe(true);
34+
});
35+
36+
test("includes command arguments and metadata", () => {
37+
const commandTree = experimental_getWranglerCommands();
38+
39+
const initCommand = commandTree.subtree.get("init");
40+
expect(initCommand?.definition?.type).toBe("command");
41+
if (initCommand?.definition?.type === "command") {
42+
expect(initCommand.definition.metadata).toBeDefined();
43+
expect(initCommand.definition.metadata.description).toBeDefined();
44+
expect(initCommand.definition.metadata.status).toBeDefined();
45+
expect(initCommand.definition.metadata.owner).toBeDefined();
46+
}
47+
});
48+
49+
test("includes namespace commands", () => {
50+
const commandTree = experimental_getWranglerCommands();
51+
52+
const kvCommand = commandTree.subtree.get("kv");
53+
expect(kvCommand?.definition?.type).toBe("namespace");
54+
expect(kvCommand?.subtree).toBeInstanceOf(Map);
55+
expect(kvCommand?.subtree.has("namespace")).toBe(true);
56+
expect(kvCommand?.subtree.has("key")).toBe(true);
57+
});
58+
59+
test("preserves command metadata properties", () => {
60+
const commandTree = experimental_getWranglerCommands();
61+
62+
const deployCommand = commandTree.subtree.get("deploy");
63+
if (deployCommand?.definition?.type === "command") {
64+
const metadata = deployCommand.definition.metadata;
65+
expect(metadata.description).toBeDefined();
66+
expect(metadata.status).toBeDefined();
67+
expect(metadata.owner).toBeDefined();
68+
expect(typeof metadata.description).toBe("string");
69+
expect([
70+
"experimental",
71+
"alpha",
72+
"private-beta",
73+
"open-beta",
74+
"stable",
75+
]).toContain(metadata.status);
76+
}
77+
});
78+
});

packages/wrangler/src/build/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ export const buildCommand = createCommand({
1313
provideConfig: false,
1414
},
1515
async handler(buildArgs) {
16-
await createCLIParser([
16+
const { wrangler } = createCLIParser([
1717
"deploy",
1818
"--dry-run",
1919
"--outdir=dist",
2020
...(buildArgs.env ? ["--env", buildArgs.env] : []),
2121
...(buildArgs.config ? ["--config", buildArgs.config] : []),
22-
]).parse();
22+
]);
23+
await wrangler.parse();
2324
},
2425
});

packages/wrangler/src/check/commands.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ async function checkStartupHandler(
5757
}
5858

5959
await spinnerWhile({
60-
promise: async () =>
61-
await createCLIParser(
60+
promise: async () => {
61+
const { wrangler } = createCLIParser(
6262
config.pages_build_output_dir || pages
6363
? [
6464
"pages",
@@ -73,7 +73,9 @@ async function checkStartupHandler(
7373
"--dry-run",
7474
`--outfile=${workerBundle}`,
7575
]
76-
).parse(),
76+
);
77+
await wrangler.parse();
78+
},
7779
startMessage: "Building your Worker",
7880
endMessage: chalk.green("Worker Built! 🎉"),
7981
});

packages/wrangler/src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,5 @@ export {
7373
type RemoteProxySession as Experimental_RemoteProxySession,
7474
convertConfigBindingsToStartWorkerBindings as unstable_convertConfigBindingsToStartWorkerBindings,
7575
} from "./api";
76+
77+
export { experimental_getWranglerCommands } from "./experimental-commands-api";
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createCLIParser } from "./index";
2+
import type { DefinitionTreeNode } from "./core/types";
3+
4+
/**
5+
* EXPERIMENTAL: Get all registered Wrangler commands for documentation generation.
6+
* This API is experimental and may change without notice.
7+
*
8+
* @returns The complete command tree structure with all metadata
9+
*/
10+
export function experimental_getWranglerCommands(): DefinitionTreeNode {
11+
const { registry } = createCLIParser([]);
12+
return registry.getDefinitionTreeRoot();
13+
}

packages/wrangler/src/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,7 +1483,7 @@ export function createCLIParser(argv: string[]) {
14831483

14841484
wrangler.exitProcess(false);
14851485

1486-
return wrangler;
1486+
return { wrangler, registry };
14871487
}
14881488

14891489
export async function main(argv: string[]): Promise<void> {
@@ -1492,7 +1492,7 @@ export async function main(argv: string[]): Promise<void> {
14921492
checkMacOSVersion({ shouldThrow: false });
14931493

14941494
const startTime = Date.now();
1495-
const wrangler = createCLIParser(argv);
1495+
const { wrangler } = createCLIParser(argv);
14961496
let command: string | undefined;
14971497
let metricsArgs: Record<string, unknown> | undefined;
14981498
let dispatcher: ReturnType<typeof getMetricsDispatcher> | undefined;
@@ -1567,7 +1567,8 @@ export async function main(argv: string[]): Promise<void> {
15671567
// We are not able to ask the `wrangler` CLI parser to show help for a subcommand programmatically.
15681568
// The workaround is to re-run the parsing with an additional `--help` flag, which will result in the correct help message being displayed.
15691569
// The `wrangler` object is "frozen"; we cannot reuse that with different args, so we must create a new CLI parser to generate the help message.
1570-
await createCLIParser([...argv, "--help"]).parse();
1570+
const { wrangler: helpWrangler } = createCLIParser([...argv, "--help"]);
1571+
await helpWrangler.parse();
15711572
} else if (
15721573
isAuthenticationError(e) ||
15731574
// Is this a Containers/Cloudchamber-based auth error?

0 commit comments

Comments
 (0)