Skip to content

Commit 0579803

Browse files
authored
Wrangler and C3: Move the cli folder of C3 into @cloudflare/cli and make Wrangler and C3 depend on it (#4189)
We want to start using @clack/core and C3-like components in Wrangler, so the logical next step is to make these components available to wrangler and C3. `./helpers/cli.ts` in C3 is now index.ts in @cloudflare/cli, didn't include openInBrowser,C3_DEFAULTS and WRANGLER_DEFAULTS. `./helpers/colors` in C3 is now colors.ts in @cloudflare/cli. `./helpers/interactive.ts` in C3 is now interactive.ts in @cloudflare/cli.
1 parent 46cd2df commit 0579803

39 files changed

+529
-266
lines changed

.changeset/itchy-ducks-listen.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@cloudflare/cli": major
3+
"create-cloudflare": patch
4+
"wrangler": patch
5+
---
6+
7+
Move helper cli files of C3 into @cloudflare/cli and make Wrangler and C3 depend on it

packages/cli/.eslintrc.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = {
2+
root: true,
3+
extends: ["@cloudflare/eslint-config-worker"],
4+
ignorePatterns: ["dist"],
5+
};

packages/cli/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Cloudflare CLI
2+
3+
All things related to rendering the CLI for workers-sdk.

packages/cli/__tests__/cli.test.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { describe, expect, test } from "vitest";
2+
import { space } from "..";
3+
4+
describe("cli", () => {
5+
test("test spaces", () => {
6+
expect(space(300)).toHaveLength(300);
7+
});
8+
});

packages/cli/index.ts

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { exit } from "process";
2+
import { brandColor, dim, gray, white, red, hidden, bgRed } from "./colors";
3+
4+
export const shapes = {
5+
diamond: "◇",
6+
dash: "─",
7+
radioInactive: "○",
8+
radioActive: "●",
9+
10+
bar: "│",
11+
leftT: "├",
12+
rigthT: "┤",
13+
14+
arrows: {
15+
left: "‹",
16+
right: "›",
17+
},
18+
19+
corners: {
20+
tl: "╭",
21+
bl: "╰",
22+
tr: "╮",
23+
br: "╯",
24+
},
25+
};
26+
27+
export const status = {
28+
error: bgRed(` ERROR `),
29+
warning: bgRed(` WARNING `),
30+
info: bgRed(` INFO `),
31+
success: bgRed(` SUCCESS `),
32+
};
33+
34+
// Returns a string containing n non-trimmable spaces
35+
// This is useful for places where clack trims lines of output
36+
// but we need leading spaces
37+
export const space = (n = 1) => {
38+
return hidden("\u200A".repeat(n));
39+
};
40+
41+
// Primitive for printing to stdout. Use this instead of
42+
// console.log or printing to stdout directly
43+
export const logRaw = (msg: string) => {
44+
process.stdout.write(`${msg}\n`);
45+
};
46+
47+
// A simple stylized log for use within a prompt
48+
export const log = (msg: string) => {
49+
const lines = msg.split("\n").map((ln) => `${gray(shapes.bar)} ${white(ln)}`);
50+
51+
logRaw(lines.join("\n"));
52+
};
53+
54+
export const newline = () => {
55+
log("");
56+
};
57+
58+
// Log a simple status update with a style similar to the clack spinner
59+
export const updateStatus = (msg: string) => {
60+
logRaw(`${gray(shapes.leftT)} ${msg}`);
61+
newline();
62+
};
63+
64+
export const startSection = (heading: string, subheading?: string) => {
65+
logRaw(
66+
`${gray(shapes.corners.tl)} ${brandColor(heading)} ${
67+
subheading ? dim(subheading) : ""
68+
}`
69+
);
70+
newline();
71+
};
72+
73+
export const endSection = (heading: string, subheading?: string) => {
74+
logRaw(
75+
`${gray(shapes.corners.bl)} ${brandColor(heading)} ${
76+
subheading ? dim(subheading) : ""
77+
}\n`
78+
);
79+
};
80+
81+
export const cancel = (msg: string) => {
82+
newline();
83+
logRaw(`${gray(shapes.corners.bl)} ${white.bgRed(` X `)} ${dim(msg)}`);
84+
};
85+
86+
export const warn = (msg: string) => {
87+
newline();
88+
logRaw(`${gray(shapes.corners.bl)} ${status.warning} ${dim(msg)}`);
89+
};
90+
91+
// Strip the ansi color characters out of the line when calculating
92+
// line length, otherwise the padding will be thrown off
93+
// Used from https://github.com/natemoo-re/clack/blob/main/packages/prompts/src/index.ts
94+
export const stripAnsi = (str: string) => {
95+
const pattern = [
96+
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
97+
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))",
98+
].join("|");
99+
const regex = RegExp(pattern, "g");
100+
101+
return str.replace(regex, "");
102+
};
103+
104+
export const crash = (msg?: string): never => {
105+
if (msg) {
106+
process.stderr.write(red(msg));
107+
process.stderr.write("\n");
108+
}
109+
exit(1);
110+
};

packages/create-cloudflare/src/helpers/interactive.ts renamed to packages/cli/interactive.ts

+13-14
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { TextPrompt, SelectPrompt, ConfirmPrompt } from "@clack/core";
1+
import { ConfirmPrompt, SelectPrompt, TextPrompt } from "@clack/core";
22
import ansiEscapes from "ansi-escapes";
33
import logUpdate from "log-update";
4-
import { shapes, cancel, space, status, newline } from "./cli";
5-
import { blue, dim, gray, brandColor, bold } from "./colors";
4+
import { blue, bold, brandColor, dim, gray } from "./colors";
5+
import { cancel, newline, shapes, space, status } from "./index";
66
import type { ChalkInstance } from "chalk";
7-
import type { C3Arg } from "types";
87

8+
export type Arg = string | boolean | string[] | undefined;
99
const grayBar = gray(shapes.bar);
1010
const blCorner = gray(shapes.corners.bl);
1111
const leftT = gray(shapes.leftT);
@@ -25,9 +25,9 @@ export type BasePromptConfig = {
2525
// The status label to be shown after submitting
2626
label: string;
2727
// Pretty-prints the value in the interactive prompt
28-
format?: (value: C3Arg) => string;
28+
format?: (value: Arg) => string;
2929
// Returns a user displayed error if the value is invalid
30-
validate?: (value: C3Arg) => string | void;
30+
validate?: (value: Arg) => string | void;
3131
};
3232

3333
export type TextPromptConfig = BasePromptConfig & {
@@ -104,7 +104,7 @@ type Renderer = (props: {
104104
state?: string;
105105
error?: string;
106106
cursor?: number;
107-
value: C3Arg;
107+
value: Arg;
108108
}) => string[];
109109

110110
const renderSubmit = (config: PromptConfig, value: string) => {
@@ -144,28 +144,27 @@ const getTextRenderers = (config: TextPromptConfig) => {
144144
format: _format,
145145
} = config;
146146
const helpText = _helpText ?? "";
147-
const format = _format ?? ((val: C3Arg) => String(val));
147+
const format = _format ?? ((val: Arg) => String(val));
148148

149149
return {
150150
initial: () => [
151151
`${blCorner} ${bold(question)} ${dim(helpText)}`,
152152
`${space(2)}${gray(format(defaultValue))}`,
153153
``, // extra line for readability
154154
],
155-
active: ({ value }: { value: C3Arg }) => [
155+
active: ({ value }: { value: Arg }) => [
156156
`${blCorner} ${bold(question)} ${dim(helpText)}`,
157157
`${space(2)}${format(value || dim(defaultValue))}`,
158158
``, // extra line for readability
159159
],
160-
error: ({ value, error }: { value: C3Arg; error: string }) => [
160+
error: ({ value, error }: { value: Arg; error: string }) => [
161161
`${leftT} ${status.error} ${dim(error)}`,
162162
`${grayBar}`,
163163
`${blCorner} ${question} ${dim(helpText)}`,
164164
`${space(2)}${format(value)}`,
165165
``, // extra line for readability
166166
],
167-
submit: ({ value }: { value: C3Arg }) =>
168-
renderSubmit(config, format(value)),
167+
submit: ({ value }: { value: Arg }) => renderSubmit(config, format(value)),
169168
cancel: handleCancel,
170169
};
171170
};
@@ -198,7 +197,7 @@ const getSelectRenderers = (config: SelectPromptConfig) => {
198197
active: defaultRenderer,
199198
confirm: defaultRenderer,
200199
error: defaultRenderer,
201-
submit: ({ value }: { value: C3Arg }) =>
200+
submit: ({ value }: { value: Arg }) =>
202201
renderSubmit(
203202
config,
204203
options.find((o) => o.value === value)?.label as string
@@ -228,7 +227,7 @@ const getConfirmRenderers = (config: ConfirmPromptConfig) => {
228227
active: defaultRenderer,
229228
confirm: defaultRenderer,
230229
error: defaultRenderer,
231-
submit: ({ value }: { value: C3Arg }) =>
230+
submit: ({ value }: { value: Arg }) =>
232231
renderSubmit(config, value ? "yes" : "no"),
233232
cancel: handleCancel,
234233
};

packages/cli/package.json

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@cloudflare/cli",
3+
"version": "0.1.0",
4+
"description": "An SDK to build workers-sdk CLIs",
5+
"private": true,
6+
"keywords": [
7+
"cloudflare",
8+
"workers",
9+
"cloudflare workers",
10+
"cli"
11+
],
12+
"scripts": {
13+
"check:type": "tsc",
14+
"test": "vitest run",
15+
"test:ci": "vitest run",
16+
"check:lint": "eslint ."
17+
},
18+
"repository": {
19+
"type": "git",
20+
"url": "https://github.com/cloudflare/workers-sdk.git",
21+
"directory": "packages/cli"
22+
},
23+
"license": "MIT OR Apache-2.0",
24+
"author": "[email protected]",
25+
"devDependencies": {
26+
"@clack/core": "^0.3.2",
27+
"@clack/prompts": "^0.6.3",
28+
"@cloudflare/eslint-config-worker": "*",
29+
"@cloudflare/workers-tsconfig": "workspace:*",
30+
"@typescript-eslint/eslint-plugin": "^5.55.0",
31+
"@typescript-eslint/parser": "^5.55.0",
32+
"ansi-escapes": "^6.2.0",
33+
"chalk": "^5.2.0",
34+
"esbuild": "^0.17.12",
35+
"log-update": "^5.0.1",
36+
"pnpm": "^8.6.11",
37+
"undici": "5.20.0"
38+
}
39+
}

packages/cli/tsconfig.json

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"root": true,
3+
"extends": "@cloudflare/workers-tsconfig/tsconfig.json",
4+
"include": ["**/*.ts"],
5+
"exclude": ["node_modules"],
6+
"compilerOptions": {
7+
"module": "CommonJS",
8+
"allowJs": false,
9+
"outDir": "dist",
10+
"types": ["node"],
11+
"resolveJsonModule": true
12+
}
13+
}

packages/cli/turbo.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "http://turbo.build/schema.json",
3+
"extends": ["//"],
4+
5+
"pipeline": {}
6+
}

packages/cli/vite.config.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { defineConfig } from "vitest/config";
2+
3+
export default defineConfig({
4+
test: {
5+
include: ["**/__tests__/**/*.{test,spec}.{ts,js,tsx,jsx}"],
6+
testTimeout: 30000,
7+
setupFiles: "./vite.setup.ts",
8+
},
9+
});

packages/cli/vite.setup.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { vi } from "vitest";
2+
3+
vi.mock("log-update");

packages/create-cloudflare/e2e-tests/helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import {
99
import crypto from "node:crypto";
1010
import { tmpdir } from "os";
1111
import { basename, join } from "path";
12+
import { stripAnsi } from "@cloudflare/cli";
1213
import { spawn } from "cross-spawn";
13-
import { stripAnsi } from "helpers/cli";
1414
import { sleep } from "helpers/common";
1515
import { detectPackageManager } from "helpers/packages";
1616
import { fetch } from "undici";

packages/create-cloudflare/package.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242
"devDependencies": {
4343
"@babel/parser": "^7.21.3",
4444
"@babel/types": "^7.21.4",
45-
"@clack/core": "^0.3.2",
4645
"@clack/prompts": "^0.6.3",
46+
"@cloudflare/cli": "workspace:*",
4747
"@cloudflare/eslint-config-worker": "*",
4848
"@cloudflare/workers-tsconfig": "workspace:*",
4949
"@cloudflare/workers-types": "^4.20230419.0",
@@ -58,7 +58,6 @@
5858
"@types/yargs": "^17.0.22",
5959
"@typescript-eslint/eslint-plugin": "^5.55.0",
6060
"@typescript-eslint/parser": "^5.55.0",
61-
"ansi-escapes": "^6.2.0",
6261
"chalk": "^5.2.0",
6362
"command-exists": "^1.2.9",
6463
"cross-spawn": "^7.0.3",
@@ -67,7 +66,6 @@
6766
"execa": "^7.1.1",
6867
"glob": "^10.3.3",
6968
"haikunator": "^2.1.2",
70-
"log-update": "^5.0.1",
7169
"open": "^8.4.0",
7270
"pnpm": "^8.6.11",
7371
"recast": "^0.22.0",

packages/create-cloudflare/src/cli.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
#!/usr/bin/env node
2+
import { crash, logRaw, startSection } from "@cloudflare/cli";
3+
import { blue, dim } from "@cloudflare/cli/colors";
4+
import {
5+
isInteractive,
6+
spinner,
7+
spinnerFrames,
8+
} from "@cloudflare/cli/interactive";
29
import { parseArgs, processArgument } from "helpers/args";
3-
import { C3_DEFAULTS, crash, logRaw, startSection } from "helpers/cli";
4-
import { blue, dim } from "helpers/colors";
10+
import { C3_DEFAULTS } from "helpers/cli";
511
import { runCommand } from "helpers/command";
6-
import { isInteractive, spinnerFrames, spinner } from "helpers/interactive";
712
import { detectPackageManager } from "helpers/packages";
813
import semver from "semver";
914
import { version } from "../package.json";

packages/create-cloudflare/src/common.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,28 @@
11
import { existsSync, mkdirSync, readdirSync } from "fs";
22
import { basename, dirname, relative, resolve } from "path";
33
import { chdir } from "process";
4-
import { getFrameworkCli } from "frameworks/index";
5-
import { processArgument } from "helpers/args";
64
import {
7-
C3_DEFAULTS,
85
crash,
96
endSection,
107
log,
118
logRaw,
129
newline,
13-
openInBrowser,
1410
shapes,
1511
startSection,
1612
updateStatus,
17-
} from "helpers/cli";
18-
import { dim, blue, gray, bgGreen, brandColor } from "helpers/colors";
13+
} from "@cloudflare/cli";
14+
import { brandColor, dim, gray, bgGreen, blue } from "@cloudflare/cli/colors";
15+
import { inputPrompt, spinner } from "@cloudflare/cli/interactive";
16+
import { getFrameworkCli } from "frameworks/index";
17+
import { processArgument } from "helpers/args";
18+
import { C3_DEFAULTS, openInBrowser } from "helpers/cli";
1919
import {
2020
listAccounts,
2121
printAsyncStatus,
2222
runCommand,
2323
runCommands,
2424
wranglerLogin,
2525
} from "helpers/command";
26-
import { inputPrompt, spinner } from "helpers/interactive";
2726
import { detectPackageManager } from "helpers/packages";
2827
import { poll } from "helpers/poll";
2928
import { version as wranglerVersion } from "wrangler/package.json";

0 commit comments

Comments
 (0)