@cloudflare/cli-shared-helpers 0.0.1

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/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # Cloudflare CLI
2
+
3
+ > Internal utility package for workers-sdk. Not intended for external use — APIs may change without notice.
4
+
5
+ All things related to rendering the CLI for workers-sdk.
@@ -0,0 +1,7 @@
1
+ import { Arg, PromptConfig } from "./interactive.mjs";
2
+
3
+ //#region args.d.ts
4
+ declare const processArgument: <T>(args: Record<string, Arg>, name: string, promptConfig: PromptConfig) => Promise<T>;
5
+ //#endregion
6
+ export { processArgument };
7
+ //# sourceMappingURL=args.d.mts.map
package/dist/args.mjs ADDED
@@ -0,0 +1,17 @@
1
+ import { inputPrompt } from "./interactive.mjs";
2
+
3
+ //#region args.ts
4
+ const processArgument = async (args, name, promptConfig) => {
5
+ const value = args[name];
6
+ const result = await inputPrompt({
7
+ ...promptConfig,
8
+ acceptDefault: promptConfig.acceptDefault ?? value !== void 0,
9
+ defaultValue: value ?? promptConfig.defaultValue
10
+ });
11
+ args[name] = result;
12
+ return result;
13
+ };
14
+
15
+ //#endregion
16
+ export { processArgument };
17
+ //# sourceMappingURL=args.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.mjs","names":[],"sources":["../args.ts"],"sourcesContent":["import { inputPrompt } from \"./interactive\";\nimport type { Arg, PromptConfig } from \"./interactive\";\n\nexport const processArgument = async <T>(\n\targs: Record<string, Arg>,\n\tname: string,\n\tpromptConfig: PromptConfig\n) => {\n\tconst value = args[name];\n\tconst result = await inputPrompt<T>({\n\t\t...promptConfig,\n\t\t// Accept the default value if the arg is already set\n\t\tacceptDefault: promptConfig.acceptDefault ?? value !== undefined,\n\t\tdefaultValue: value ?? promptConfig.defaultValue,\n\t});\n\n\t// Update value in args before returning the result\n\targs[name] = result as Arg;\n\n\treturn result;\n};\n"],"mappings":";;;AAGA,MAAa,kBAAkB,OAC9B,MACA,MACA,iBACI;CACJ,MAAM,QAAQ,KAAK;CACnB,MAAM,SAAS,MAAM,YAAe;EACnC,GAAG;EAEH,eAAe,aAAa,iBAAiB,UAAU;EACvD,cAAc,SAAS,aAAa;EACpC,CAAC;AAGF,MAAK,QAAQ;AAEb,QAAO"}
@@ -0,0 +1,15 @@
1
+ //#region check-macos-version.d.ts
2
+ /**
3
+ * Checks the current macOS version for workerd compatibility.
4
+ *
5
+ * This function is a no-op on non-Darwin platforms and in CI environments.
6
+ *
7
+ * @param options - Configuration object
8
+ * @param options.shouldThrow - If true, throws an error on unsupported versions. If false, logs a warning.
9
+ */
10
+ declare function checkMacOSVersion(options: {
11
+ shouldThrow: boolean;
12
+ }): void;
13
+ //#endregion
14
+ export { checkMacOSVersion };
15
+ //# sourceMappingURL=check-macos-version.d.mts.map
@@ -0,0 +1,65 @@
1
+ import os from "node:os";
2
+ import { UserError } from "@cloudflare/workers-utils";
3
+ import ci from "ci-info";
4
+
5
+ //#region check-macos-version.ts
6
+ /**
7
+ * Minimum macOS version required for workerd compatibility.
8
+ */
9
+ const MINIMUM_MACOS_VERSION = "13.5.0";
10
+ /**
11
+ * Checks the current macOS version for workerd compatibility.
12
+ *
13
+ * This function is a no-op on non-Darwin platforms and in CI environments.
14
+ *
15
+ * @param options - Configuration object
16
+ * @param options.shouldThrow - If true, throws an error on unsupported versions. If false, logs a warning.
17
+ */
18
+ function checkMacOSVersion(options) {
19
+ if (process.platform !== "darwin") return;
20
+ if (ci.isCI) return;
21
+ const macOSVersion = darwinVersionToMacOSVersion(os.release());
22
+ if (macOSVersion && isVersionLessThan(macOSVersion, MINIMUM_MACOS_VERSION)) if (options.shouldThrow) throw new UserError(`Unsupported macOS version: The Cloudflare Workers runtime cannot run on the current version of macOS (${macOSVersion}). The minimum requirement is macOS ${MINIMUM_MACOS_VERSION}+. See https://github.com/cloudflare/workerd?tab=readme-ov-file#running-workerd If you cannot upgrade your version of macOS, you could try running in a DevContainer setup with a supported version of Linux (glibc 2.35+ required).`);
23
+ else console.warn(`⚠️ Warning: Unsupported macOS version detected (${macOSVersion}). The Cloudflare Workers runtime may not work correctly on macOS versions below ${MINIMUM_MACOS_VERSION}. Consider upgrading to macOS ${MINIMUM_MACOS_VERSION}+ or using a DevContainer setup with a supported version of Linux (glibc 2.35+ required).`);
24
+ }
25
+ /**
26
+ * Converts Darwin kernel version to macOS version.
27
+ * Darwin 21.x.x = macOS 12.x (Monterey)
28
+ * Darwin 22.x.x = macOS 13.x (Ventura)
29
+ * Darwin 23.x.x = macOS 14.x (Sonoma)
30
+ * etc.
31
+ */
32
+ function darwinVersionToMacOSVersion(darwinVersion) {
33
+ const match = darwinVersion.match(/^(\d+)\.(\d+)\.(\d+)/);
34
+ if (!match) return null;
35
+ const major = parseInt(match[1], 10);
36
+ if (major >= 20) return `${major - 9}.${parseInt(match[2], 10)}.${parseInt(match[3], 10)}`;
37
+ return null;
38
+ }
39
+ /**
40
+ * Simple semver comparison for major.minor.patch versions.
41
+ * Validates that both versions follow the M.m.p format before comparison.
42
+ */
43
+ function isVersionLessThan(version1, version2) {
44
+ const versionRegex = /^(\d+)\.(\d+)\.(\d+)$/;
45
+ const match1 = version1.match(versionRegex);
46
+ const match2 = version2.match(versionRegex);
47
+ if (!match1 || !match2) throw new Error(`Invalid version format. Expected M.m.p format, got: ${version1}, ${version2}`);
48
+ const [major1, minor1, patch1] = [
49
+ parseInt(match1[1], 10),
50
+ parseInt(match1[2], 10),
51
+ parseInt(match1[3], 10)
52
+ ];
53
+ const [major2, minor2, patch2] = [
54
+ parseInt(match2[1], 10),
55
+ parseInt(match2[2], 10),
56
+ parseInt(match2[3], 10)
57
+ ];
58
+ if (major1 !== major2) return major1 < major2;
59
+ if (minor1 !== minor2) return minor1 < minor2;
60
+ return patch1 < patch2;
61
+ }
62
+
63
+ //#endregion
64
+ export { checkMacOSVersion };
65
+ //# sourceMappingURL=check-macos-version.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-macos-version.mjs","names":[],"sources":["../check-macos-version.ts"],"sourcesContent":["import os from \"node:os\";\nimport { UserError } from \"@cloudflare/workers-utils\";\nimport ci from \"ci-info\";\n\n/**\n * Minimum macOS version required for workerd compatibility.\n */\nconst MINIMUM_MACOS_VERSION = \"13.5.0\";\n\n/**\n * Checks the current macOS version for workerd compatibility.\n *\n * This function is a no-op on non-Darwin platforms and in CI environments.\n *\n * @param options - Configuration object\n * @param options.shouldThrow - If true, throws an error on unsupported versions. If false, logs a warning.\n */\nexport function checkMacOSVersion(options: { shouldThrow: boolean }): void {\n\tif (process.platform !== \"darwin\") {\n\t\treturn;\n\t}\n\n\tif (ci.isCI) {\n\t\treturn;\n\t}\n\n\tconst release = os.release();\n\tconst macOSVersion = darwinVersionToMacOSVersion(release);\n\n\tif (macOSVersion && isVersionLessThan(macOSVersion, MINIMUM_MACOS_VERSION)) {\n\t\tif (options.shouldThrow) {\n\t\t\tthrow new UserError(\n\t\t\t\t`Unsupported macOS version: The Cloudflare Workers runtime cannot run on the current version of macOS (${macOSVersion}). ` +\n\t\t\t\t\t`The minimum requirement is macOS ${MINIMUM_MACOS_VERSION}+. See https://github.com/cloudflare/workerd?tab=readme-ov-file#running-workerd ` +\n\t\t\t\t\t`If you cannot upgrade your version of macOS, you could try running in a DevContainer setup with a supported version of Linux (glibc 2.35+ required).`\n\t\t\t);\n\t\t} else {\n\t\t\t// eslint-disable-next-line no-console\n\t\t\tconsole.warn(\n\t\t\t\t`⚠️ Warning: Unsupported macOS version detected (${macOSVersion}). ` +\n\t\t\t\t\t`The Cloudflare Workers runtime may not work correctly on macOS versions below ${MINIMUM_MACOS_VERSION}. ` +\n\t\t\t\t\t`Consider upgrading to macOS ${MINIMUM_MACOS_VERSION}+ or using a DevContainer setup with a supported version of Linux (glibc 2.35+ required).`\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Converts Darwin kernel version to macOS version.\n * Darwin 21.x.x = macOS 12.x (Monterey)\n * Darwin 22.x.x = macOS 13.x (Ventura)\n * Darwin 23.x.x = macOS 14.x (Sonoma)\n * etc.\n */\nfunction darwinVersionToMacOSVersion(darwinVersion: string): string | null {\n\tconst match = darwinVersion.match(/^(\\d+)\\.(\\d+)\\.(\\d+)/);\n\tif (!match) {\n\t\treturn null;\n\t}\n\n\tconst major = parseInt(match[1], 10);\n\n\tif (major >= 20) {\n\t\tconst macOSMajor = major - 9;\n\t\tconst minor = parseInt(match[2], 10);\n\t\tconst patch = parseInt(match[3], 10);\n\t\treturn `${macOSMajor}.${minor}.${patch}`;\n\t}\n\n\treturn null;\n}\n\n/**\n * Simple semver comparison for major.minor.patch versions.\n * Validates that both versions follow the M.m.p format before comparison.\n */\nfunction isVersionLessThan(version1: string, version2: string): boolean {\n\tconst versionRegex = /^(\\d+)\\.(\\d+)\\.(\\d+)$/;\n\n\tconst match1 = version1.match(versionRegex);\n\tconst match2 = version2.match(versionRegex);\n\n\tif (!match1 || !match2) {\n\t\tthrow new Error(\n\t\t\t`Invalid version format. Expected M.m.p format, got: ${version1}, ${version2}`\n\t\t);\n\t}\n\n\tconst [major1, minor1, patch1] = [\n\t\tparseInt(match1[1], 10),\n\t\tparseInt(match1[2], 10),\n\t\tparseInt(match1[3], 10),\n\t];\n\tconst [major2, minor2, patch2] = [\n\t\tparseInt(match2[1], 10),\n\t\tparseInt(match2[2], 10),\n\t\tparseInt(match2[3], 10),\n\t];\n\n\tif (major1 !== major2) {\n\t\treturn major1 < major2;\n\t}\n\tif (minor1 !== minor2) {\n\t\treturn minor1 < minor2;\n\t}\n\treturn patch1 < patch2;\n}\n"],"mappings":";;;;;;;;AAOA,MAAM,wBAAwB;;;;;;;;;AAU9B,SAAgB,kBAAkB,SAAyC;AAC1E,KAAI,QAAQ,aAAa,SACxB;AAGD,KAAI,GAAG,KACN;CAID,MAAM,eAAe,4BADL,GAAG,SAAS,CAC6B;AAEzD,KAAI,gBAAgB,kBAAkB,cAAc,sBAAsB,CACzE,KAAI,QAAQ,YACX,OAAM,IAAI,UACT,yGAAyG,aAAa,sCACjF,sBAAsB,sOAE3D;KAGD,SAAQ,KACP,oDAAoD,aAAa,mFACiB,sBAAsB,gCACxE,sBAAsB,2FACtD;;;;;;;;;AAYJ,SAAS,4BAA4B,eAAsC;CAC1E,MAAM,QAAQ,cAAc,MAAM,uBAAuB;AACzD,KAAI,CAAC,MACJ,QAAO;CAGR,MAAM,QAAQ,SAAS,MAAM,IAAI,GAAG;AAEpC,KAAI,SAAS,GAIZ,QAAO,GAHY,QAAQ,EAGN,GAFP,SAAS,MAAM,IAAI,GAAG,CAEN,GADhB,SAAS,MAAM,IAAI,GAAG;AAIrC,QAAO;;;;;;AAOR,SAAS,kBAAkB,UAAkB,UAA2B;CACvE,MAAM,eAAe;CAErB,MAAM,SAAS,SAAS,MAAM,aAAa;CAC3C,MAAM,SAAS,SAAS,MAAM,aAAa;AAE3C,KAAI,CAAC,UAAU,CAAC,OACf,OAAM,IAAI,MACT,uDAAuD,SAAS,IAAI,WACpE;CAGF,MAAM,CAAC,QAAQ,QAAQ,UAAU;EAChC,SAAS,OAAO,IAAI,GAAG;EACvB,SAAS,OAAO,IAAI,GAAG;EACvB,SAAS,OAAO,IAAI,GAAG;EACvB;CACD,MAAM,CAAC,QAAQ,QAAQ,UAAU;EAChC,SAAS,OAAO,IAAI,GAAG;EACvB,SAAS,OAAO,IAAI,GAAG;EACvB,SAAS,OAAO,IAAI,GAAG;EACvB;AAED,KAAI,WAAW,OACd,QAAO,SAAS;AAEjB,KAAI,WAAW,OACd,QAAO,SAAS;AAEjB,QAAO,SAAS"}
@@ -0,0 +1,16 @@
1
+ import * as chalk0 from "chalk";
2
+
3
+ //#region colors.d.ts
4
+ declare const white: chalk0.ChalkInstance, gray: chalk0.ChalkInstance, dim: chalk0.ChalkInstance, hidden: chalk0.ChalkInstance, bold: chalk0.ChalkInstance, cyanBright: chalk0.ChalkInstance, bgCyan: chalk0.ChalkInstance;
5
+ declare const brandColor: chalk0.ChalkInstance;
6
+ declare const blue: chalk0.ChalkInstance;
7
+ declare const bgBlue: chalk0.ChalkInstance;
8
+ declare const red: chalk0.ChalkInstance;
9
+ declare const bgRed: chalk0.ChalkInstance;
10
+ declare const green: chalk0.ChalkInstance;
11
+ declare const bgGreen: chalk0.ChalkInstance;
12
+ declare const yellow: chalk0.ChalkInstance;
13
+ declare const bgYellow: chalk0.ChalkInstance;
14
+ //#endregion
15
+ export { bgBlue, bgCyan, bgGreen, bgRed, bgYellow, blue, bold, brandColor, cyanBright, dim, gray, green, hidden, red, white, yellow };
16
+ //# sourceMappingURL=colors.d.mts.map
@@ -0,0 +1,18 @@
1
+ import chalk from "chalk";
2
+
3
+ //#region colors.ts
4
+ const { white, gray, dim, hidden, bold, cyanBright, bgCyan } = chalk;
5
+ const brandColor = chalk.hex("#BD5B08");
6
+ const black = chalk.hex("#111");
7
+ const blue = chalk.hex("#0E838F");
8
+ const bgBlue = black.bgHex("#0E838F");
9
+ const red = chalk.hex("#AB2526");
10
+ const bgRed = black.bgHex("#AB2526");
11
+ const green = chalk.hex("#218529");
12
+ const bgGreen = black.bgHex("#218529");
13
+ const yellow = chalk.hex("#7F7322");
14
+ const bgYellow = black.bgHex("#7F7322");
15
+
16
+ //#endregion
17
+ export { bgBlue, bgCyan, bgGreen, bgRed, bgYellow, blue, bold, brandColor, cyanBright, dim, gray, green, hidden, red, white, yellow };
18
+ //# sourceMappingURL=colors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.mjs","names":[],"sources":["../colors.ts"],"sourcesContent":["import chalk from \"chalk\";\n\nconst { white, gray, dim, hidden, bold, cyanBright, bgCyan } = chalk;\n\nconst brandColor = chalk.hex(\"#BD5B08\");\n\nconst black = chalk.hex(\"#111\");\nconst blue = chalk.hex(\"#0E838F\");\nconst bgBlue = black.bgHex(\"#0E838F\");\nconst red = chalk.hex(\"#AB2526\");\nconst bgRed = black.bgHex(\"#AB2526\");\nconst green = chalk.hex(\"#218529\");\nconst bgGreen = black.bgHex(\"#218529\");\nconst yellow = chalk.hex(\"#7F7322\");\nconst bgYellow = black.bgHex(\"#7F7322\");\n\nexport {\n\tblue,\n\tbgBlue,\n\tgreen,\n\tbgGreen,\n\tred,\n\tbgRed,\n\tyellow,\n\tbgYellow,\n\tcyanBright,\n\tbgCyan,\n\tbrandColor,\n\tdim,\n\twhite,\n\tgray,\n\thidden,\n\tbold,\n};\n"],"mappings":";;;AAEA,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,MAAM,YAAY,WAAW;AAE/D,MAAM,aAAa,MAAM,IAAI,UAAU;AAEvC,MAAM,QAAQ,MAAM,IAAI,OAAO;AAC/B,MAAM,OAAO,MAAM,IAAI,UAAU;AACjC,MAAM,SAAS,MAAM,MAAM,UAAU;AACrC,MAAM,MAAM,MAAM,IAAI,UAAU;AAChC,MAAM,QAAQ,MAAM,MAAM,UAAU;AACpC,MAAM,QAAQ,MAAM,IAAI,UAAU;AAClC,MAAM,UAAU,MAAM,MAAM,UAAU;AACtC,MAAM,SAAS,MAAM,IAAI,UAAU;AACnC,MAAM,WAAW,MAAM,MAAM,UAAU"}
@@ -0,0 +1,46 @@
1
+ //#region command.d.ts
2
+ /**
3
+ * Command is a string array, like ['git', 'commit', '-m', '"Initial commit"']
4
+ */
5
+ type Command = string[];
6
+ type RunOptions = {
7
+ startText?: string;
8
+ doneText?: string | ((output: string) => string);
9
+ silent?: boolean;
10
+ captureOutput?: boolean;
11
+ useSpinner?: boolean;
12
+ env?: NodeJS.ProcessEnv;
13
+ cwd?: string;
14
+ /** If defined this function is called to all you to transform the output from the command into a new string. */
15
+ transformOutput?: (output: string) => string;
16
+ };
17
+ /**
18
+ * An awaitable wrapper around `spawn` that optionally displays progress to user and processes/captures the command's output
19
+ *
20
+ * @param command - The command to run as an array of strings
21
+ * @param opts.silent - Should the command's stdout and stderr be dispalyed to the user
22
+ * @param opts.captureOutput - Should the output of the command the returned as a string.
23
+ * @param opts.env - An object of environment variables to be injected when running the command
24
+ * @param opts.cwd - The directory in which the command should be run
25
+ * @param opts.useSpinner - Should a spinner be shown when running the command
26
+ * @param opts.startText - Spinner start text
27
+ * @param opts.endText - Spinner end text
28
+ * @param opts.transformOutput - A transformer to be run on command output before returning
29
+ *
30
+ * @returns Output collected from the stdout of the command, if `captureOutput` was set to true. Otherwise `null`.
31
+ */
32
+ declare const runCommand: (command: Command, opts?: RunOptions) => Promise<string>;
33
+ /**
34
+ * Formats an array of command line arguments to be displayed to the user
35
+ * in a platform safe way. Args used in conjunction with `runCommand` are safe
36
+ * since we use `cross-spawn` to handle multi-platform support.
37
+ *
38
+ * However, when we output commands to the user, we have to make sure that they
39
+ * are compatible with the platform they are using.
40
+ *
41
+ * @param args - The arguments to format to the user
42
+ */
43
+ declare function quoteShellArgs(args: string[]): string;
44
+ //#endregion
45
+ export { RunOptions, quoteShellArgs, runCommand };
46
+ //# sourceMappingURL=command.d.mts.map
@@ -0,0 +1,114 @@
1
+ import { CancelError } from "./error.mjs";
2
+ import { stripAnsi } from "./index.mjs";
3
+ import { isInteractive, spinner } from "./interactive.mjs";
4
+ import { spawn } from "cross-spawn";
5
+
6
+ //#region command.ts
7
+ /**
8
+ * An awaitable wrapper around `spawn` that optionally displays progress to user and processes/captures the command's output
9
+ *
10
+ * @param command - The command to run as an array of strings
11
+ * @param opts.silent - Should the command's stdout and stderr be dispalyed to the user
12
+ * @param opts.captureOutput - Should the output of the command the returned as a string.
13
+ * @param opts.env - An object of environment variables to be injected when running the command
14
+ * @param opts.cwd - The directory in which the command should be run
15
+ * @param opts.useSpinner - Should a spinner be shown when running the command
16
+ * @param opts.startText - Spinner start text
17
+ * @param opts.endText - Spinner end text
18
+ * @param opts.transformOutput - A transformer to be run on command output before returning
19
+ *
20
+ * @returns Output collected from the stdout of the command, if `captureOutput` was set to true. Otherwise `null`.
21
+ */
22
+ const runCommand = async (command, opts = {}) => {
23
+ return printAsyncStatus({
24
+ useSpinner: opts.useSpinner ?? opts.silent,
25
+ startText: opts.startText || quoteShellArgs(command),
26
+ doneText: opts.doneText,
27
+ promise() {
28
+ const [executable, ...args] = command;
29
+ const abortController = new AbortController();
30
+ const cmd = spawn(executable, [...args], {
31
+ stdio: opts.silent ? "pipe" : "inherit",
32
+ env: {
33
+ ...process.env,
34
+ ...opts.env
35
+ },
36
+ cwd: opts.cwd,
37
+ signal: abortController.signal
38
+ });
39
+ let output = ``;
40
+ if (opts.captureOutput ?? opts.silent) {
41
+ cmd.stdout?.on("data", (data) => {
42
+ output += data;
43
+ });
44
+ cmd.stderr?.on("data", (data) => {
45
+ output += data;
46
+ });
47
+ }
48
+ let cleanup = null;
49
+ return new Promise((resolvePromise, reject) => {
50
+ const cancel = (signal) => {
51
+ reject(new CancelError(`Command cancelled`, signal));
52
+ abortController.abort(signal ? `${signal} received` : null);
53
+ };
54
+ process.on("SIGTERM", cancel).on("SIGINT", cancel);
55
+ cleanup = () => {
56
+ process.off("SIGTERM", cancel).off("SIGINT", cancel);
57
+ };
58
+ cmd.on("close", (code) => {
59
+ try {
60
+ if (code !== 0) throw new Error(output, { cause: code });
61
+ resolvePromise((opts.transformOutput ?? ((result) => result))(stripAnsi(output)));
62
+ } catch (e) {
63
+ reject(new Error(output, { cause: e }));
64
+ }
65
+ });
66
+ cmd.on("error", (error) => {
67
+ reject(error);
68
+ });
69
+ }).finally(() => {
70
+ cleanup?.();
71
+ });
72
+ }
73
+ });
74
+ };
75
+ const printAsyncStatus = async ({ promise,...opts }) => {
76
+ let s;
77
+ if (opts.useSpinner && isInteractive()) s = spinner();
78
+ s?.start(opts?.startText);
79
+ if (typeof promise === "function") promise = promise();
80
+ try {
81
+ const output = await promise;
82
+ const doneText = typeof opts.doneText === "function" ? opts.doneText(output) : opts.doneText;
83
+ s?.stop(doneText);
84
+ } catch (err) {
85
+ s?.stop(err.message);
86
+ } finally {
87
+ s?.stop();
88
+ }
89
+ return promise;
90
+ };
91
+ /**
92
+ * Formats an array of command line arguments to be displayed to the user
93
+ * in a platform safe way. Args used in conjunction with `runCommand` are safe
94
+ * since we use `cross-spawn` to handle multi-platform support.
95
+ *
96
+ * However, when we output commands to the user, we have to make sure that they
97
+ * are compatible with the platform they are using.
98
+ *
99
+ * @param args - The arguments to format to the user
100
+ */
101
+ function quoteShellArgs(args) {
102
+ if (process.platform === "win32") {
103
+ const specialCharsMatcher = /[&<>[\]|{}^=;!'+,`~\s]/;
104
+ return args.map((arg) => arg.match(specialCharsMatcher) ? `"${arg.replaceAll(`"`, `""`)}"` : arg).join(" ");
105
+ } else return args.map((s) => {
106
+ if (/["\s]/.test(s) && !/'/.test(s)) return "'" + s.replace(/(['\\])/g, "\\$1") + "'";
107
+ if (/["'\s]/.test(s)) return "\"" + s.replace(/(["\\$`!])/g, "\\$1") + "\"";
108
+ return s;
109
+ }).join(" ");
110
+ }
111
+
112
+ //#endregion
113
+ export { quoteShellArgs, runCommand };
114
+ //# sourceMappingURL=command.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.mjs","names":["cleanup: (() => void) | null","s: ReturnType<typeof spinner> | undefined"],"sources":["../command.ts"],"sourcesContent":["import { spawn } from \"cross-spawn\";\nimport { CancelError } from \"./error\";\nimport { isInteractive, spinner } from \"./interactive\";\nimport { stripAnsi } from \".\";\n\n/**\n * Command is a string array, like ['git', 'commit', '-m', '\"Initial commit\"']\n */\ntype Command = string[];\n\nexport type RunOptions = {\n\tstartText?: string;\n\tdoneText?: string | ((output: string) => string);\n\tsilent?: boolean;\n\tcaptureOutput?: boolean;\n\tuseSpinner?: boolean;\n\tenv?: NodeJS.ProcessEnv;\n\tcwd?: string;\n\t/** If defined this function is called to all you to transform the output from the command into a new string. */\n\ttransformOutput?: (output: string) => string;\n};\n\ntype PrintOptions<T> = {\n\tpromise: Promise<T> | (() => Promise<T>);\n\tuseSpinner?: boolean;\n\tstartText: string;\n\tdoneText?: string | ((output: T) => string);\n};\n\n/**\n * An awaitable wrapper around `spawn` that optionally displays progress to user and processes/captures the command's output\n *\n * @param command - The command to run as an array of strings\n * @param opts.silent - Should the command's stdout and stderr be dispalyed to the user\n * @param opts.captureOutput - Should the output of the command the returned as a string.\n * @param opts.env - An object of environment variables to be injected when running the command\n * @param opts.cwd - The directory in which the command should be run\n * @param opts.useSpinner - Should a spinner be shown when running the command\n * @param opts.startText - Spinner start text\n * @param opts.endText - Spinner end text\n * @param opts.transformOutput - A transformer to be run on command output before returning\n *\n * @returns Output collected from the stdout of the command, if `captureOutput` was set to true. Otherwise `null`.\n */\nexport const runCommand = async (\n\tcommand: Command,\n\topts: RunOptions = {}\n): Promise<string> => {\n\treturn printAsyncStatus({\n\t\tuseSpinner: opts.useSpinner ?? opts.silent,\n\t\tstartText: opts.startText || quoteShellArgs(command),\n\t\tdoneText: opts.doneText,\n\t\tpromise() {\n\t\t\tconst [executable, ...args] = command;\n\t\t\tconst abortController = new AbortController();\n\t\t\tconst cmd = spawn(executable, [...args], {\n\t\t\t\t// TODO: ideally inherit stderr, but npm install uses this for warnings\n\t\t\t\t// stdio: [ioMode, ioMode, \"inherit\"],\n\t\t\t\tstdio: opts.silent ? \"pipe\" : \"inherit\",\n\t\t\t\tenv: {\n\t\t\t\t\t...process.env,\n\t\t\t\t\t...opts.env,\n\t\t\t\t},\n\t\t\t\tcwd: opts.cwd,\n\t\t\t\tsignal: abortController.signal,\n\t\t\t});\n\n\t\t\tlet output = ``;\n\n\t\t\tif (opts.captureOutput ?? opts.silent) {\n\t\t\t\tcmd.stdout?.on(\"data\", (data) => {\n\t\t\t\t\toutput += data;\n\t\t\t\t});\n\t\t\t\tcmd.stderr?.on(\"data\", (data) => {\n\t\t\t\t\toutput += data;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tlet cleanup: (() => void) | null = null;\n\n\t\t\treturn new Promise<string>((resolvePromise, reject) => {\n\t\t\t\tconst cancel = (signal?: NodeJS.Signals) => {\n\t\t\t\t\treject(new CancelError(`Command cancelled`, signal));\n\t\t\t\t\tabortController.abort(signal ? `${signal} received` : null);\n\t\t\t\t};\n\n\t\t\t\tprocess.on(\"SIGTERM\", cancel).on(\"SIGINT\", cancel);\n\n\t\t\t\t// To cleanup the signal listeners when the promise settles\n\t\t\t\tcleanup = () => {\n\t\t\t\t\tprocess.off(\"SIGTERM\", cancel).off(\"SIGINT\", cancel);\n\t\t\t\t};\n\n\t\t\t\tcmd.on(\"close\", (code) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (code !== 0) {\n\t\t\t\t\t\t\tthrow new Error(output, { cause: code });\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Process any captured output\n\t\t\t\t\t\tconst transformOutput =\n\t\t\t\t\t\t\topts.transformOutput ?? ((result: string) => result);\n\t\t\t\t\t\tconst processedOutput = transformOutput(stripAnsi(output));\n\n\t\t\t\t\t\t// Send the captured (and processed) output back to the caller\n\t\t\t\t\t\tresolvePromise(processedOutput);\n\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t// Something went wrong.\n\t\t\t\t\t\t// Perhaps the command or the transform failed.\n\t\t\t\t\t\treject(new Error(output, { cause: e }));\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tcmd.on(\"error\", (error) => {\n\t\t\t\t\treject(error);\n\t\t\t\t});\n\t\t\t}).finally(() => {\n\t\t\t\tcleanup?.();\n\t\t\t});\n\t\t},\n\t});\n};\n\nconst printAsyncStatus = async <T>({\n\tpromise,\n\t...opts\n}: PrintOptions<T>): Promise<T> => {\n\tlet s: ReturnType<typeof spinner> | undefined;\n\n\tif (opts.useSpinner && isInteractive()) {\n\t\ts = spinner();\n\t}\n\n\ts?.start(opts?.startText);\n\n\tif (typeof promise === \"function\") {\n\t\tpromise = promise();\n\t}\n\n\ttry {\n\t\tconst output = await promise;\n\n\t\tconst doneText =\n\t\t\ttypeof opts.doneText === \"function\"\n\t\t\t\t? opts.doneText(output)\n\t\t\t\t: opts.doneText;\n\t\ts?.stop(doneText);\n\t} catch (err) {\n\t\ts?.stop((err as Error).message);\n\t} finally {\n\t\ts?.stop();\n\t}\n\n\treturn promise;\n};\n\n/**\n * Formats an array of command line arguments to be displayed to the user\n * in a platform safe way. Args used in conjunction with `runCommand` are safe\n * since we use `cross-spawn` to handle multi-platform support.\n *\n * However, when we output commands to the user, we have to make sure that they\n * are compatible with the platform they are using.\n *\n * @param args - The arguments to format to the user\n */\nexport function quoteShellArgs(args: string[]): string {\n\tif (process.platform === \"win32\") {\n\t\t// Simple Windows command prompt quoting if there are special characters.\n\t\tconst specialCharsMatcher = /[&<>[\\]|{}^=;!'+,`~\\s]/;\n\t\treturn args\n\t\t\t.map((arg) =>\n\t\t\t\targ.match(specialCharsMatcher) ? `\"${arg.replaceAll(`\"`, `\"\"`)}\"` : arg\n\t\t\t)\n\t\t\t.join(\" \");\n\t} else {\n\t\treturn args\n\t\t\t.map((s) => {\n\t\t\t\tif (/[\"\\s]/.test(s) && !/'/.test(s)) {\n\t\t\t\t\treturn \"'\" + s.replace(/(['\\\\])/g, \"\\\\$1\") + \"'\";\n\t\t\t\t}\n\t\t\t\tif (/[\"'\\s]/.test(s)) {\n\t\t\t\t\treturn '\"' + s.replace(/([\"\\\\$`!])/g, \"\\\\$1\") + '\"';\n\t\t\t\t}\n\t\t\t\treturn s;\n\t\t\t})\n\t\t\t.join(\" \");\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA4CA,MAAa,aAAa,OACzB,SACA,OAAmB,EAAE,KACA;AACrB,QAAO,iBAAiB;EACvB,YAAY,KAAK,cAAc,KAAK;EACpC,WAAW,KAAK,aAAa,eAAe,QAAQ;EACpD,UAAU,KAAK;EACf,UAAU;GACT,MAAM,CAAC,YAAY,GAAG,QAAQ;GAC9B,MAAM,kBAAkB,IAAI,iBAAiB;GAC7C,MAAM,MAAM,MAAM,YAAY,CAAC,GAAG,KAAK,EAAE;IAGxC,OAAO,KAAK,SAAS,SAAS;IAC9B,KAAK;KACJ,GAAG,QAAQ;KACX,GAAG,KAAK;KACR;IACD,KAAK,KAAK;IACV,QAAQ,gBAAgB;IACxB,CAAC;GAEF,IAAI,SAAS;AAEb,OAAI,KAAK,iBAAiB,KAAK,QAAQ;AACtC,QAAI,QAAQ,GAAG,SAAS,SAAS;AAChC,eAAU;MACT;AACF,QAAI,QAAQ,GAAG,SAAS,SAAS;AAChC,eAAU;MACT;;GAGH,IAAIA,UAA+B;AAEnC,UAAO,IAAI,SAAiB,gBAAgB,WAAW;IACtD,MAAM,UAAU,WAA4B;AAC3C,YAAO,IAAI,YAAY,qBAAqB,OAAO,CAAC;AACpD,qBAAgB,MAAM,SAAS,GAAG,OAAO,aAAa,KAAK;;AAG5D,YAAQ,GAAG,WAAW,OAAO,CAAC,GAAG,UAAU,OAAO;AAGlD,oBAAgB;AACf,aAAQ,IAAI,WAAW,OAAO,CAAC,IAAI,UAAU,OAAO;;AAGrD,QAAI,GAAG,UAAU,SAAS;AACzB,SAAI;AACH,UAAI,SAAS,EACZ,OAAM,IAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,CAAC;AASzC,sBAJC,KAAK,qBAAqB,WAAmB,SACN,UAAU,OAAO,CAAC,CAG3B;cACvB,GAAG;AAGX,aAAO,IAAI,MAAM,QAAQ,EAAE,OAAO,GAAG,CAAC,CAAC;;MAEvC;AAEF,QAAI,GAAG,UAAU,UAAU;AAC1B,YAAO,MAAM;MACZ;KACD,CAAC,cAAc;AAChB,eAAW;KACV;;EAEH,CAAC;;AAGH,MAAM,mBAAmB,OAAU,EAClC,QACA,GAAG,WAC+B;CAClC,IAAIC;AAEJ,KAAI,KAAK,cAAc,eAAe,CACrC,KAAI,SAAS;AAGd,IAAG,MAAM,MAAM,UAAU;AAEzB,KAAI,OAAO,YAAY,WACtB,WAAU,SAAS;AAGpB,KAAI;EACH,MAAM,SAAS,MAAM;EAErB,MAAM,WACL,OAAO,KAAK,aAAa,aACtB,KAAK,SAAS,OAAO,GACrB,KAAK;AACT,KAAG,KAAK,SAAS;UACT,KAAK;AACb,KAAG,KAAM,IAAc,QAAQ;WACtB;AACT,KAAG,MAAM;;AAGV,QAAO;;;;;;;;;;;;AAaR,SAAgB,eAAe,MAAwB;AACtD,KAAI,QAAQ,aAAa,SAAS;EAEjC,MAAM,sBAAsB;AAC5B,SAAO,KACL,KAAK,QACL,IAAI,MAAM,oBAAoB,GAAG,IAAI,IAAI,WAAW,KAAK,KAAK,CAAC,KAAK,IACpE,CACA,KAAK,IAAI;OAEX,QAAO,KACL,KAAK,MAAM;AACX,MAAI,QAAQ,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAClC,QAAO,MAAM,EAAE,QAAQ,YAAY,OAAO,GAAG;AAE9C,MAAI,SAAS,KAAK,EAAE,CACnB,QAAO,OAAM,EAAE,QAAQ,eAAe,OAAO,GAAG;AAEjD,SAAO;GACN,CACD,KAAK,IAAI"}
@@ -0,0 +1,7 @@
1
+ //#region cursor.d.ts
2
+ declare function showCursor(show: boolean, stream?: NodeJS.WriteStream & {
3
+ fd: 2;
4
+ }): void;
5
+ //#endregion
6
+ export { showCursor };
7
+ //# sourceMappingURL=cursor.d.mts.map
@@ -0,0 +1,12 @@
1
+ import process from "node:process";
2
+
3
+ //#region cursor.ts
4
+ function showCursor(show, stream = process.stderr) {
5
+ if (!stream.isTTY) return;
6
+ if (show) stream.write("\x1B[?25h");
7
+ else stream.write("\x1B[?25l");
8
+ }
9
+
10
+ //#endregion
11
+ export { showCursor };
12
+ //# sourceMappingURL=cursor.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.mjs","names":[],"sources":["../cursor.ts"],"sourcesContent":["import process from \"node:process\";\n\nexport function showCursor(show: boolean, stream = process.stderr) {\n\tif (!stream.isTTY) {\n\t\treturn;\n\t}\n\n\tif (show) {\n\t\tstream.write(\"\\x1b[?25h\");\n\t} else {\n\t\tstream.write(\"\\x1b[?25l\");\n\t}\n}\n"],"mappings":";;;AAEA,SAAgB,WAAW,MAAe,SAAS,QAAQ,QAAQ;AAClE,KAAI,CAAC,OAAO,MACX;AAGD,KAAI,KACH,QAAO,MAAM,YAAY;KAEzB,QAAO,MAAM,YAAY"}
@@ -0,0 +1,11 @@
1
+ //#region error.d.ts
2
+ /**
3
+ * Error thrown when an operation is cancelled
4
+ */
5
+ declare class CancelError extends Error {
6
+ readonly signal?: NodeJS.Signals | undefined;
7
+ constructor(message?: string, signal?: NodeJS.Signals | undefined);
8
+ }
9
+ //#endregion
10
+ export { CancelError };
11
+ //# sourceMappingURL=error.d.mts.map
package/dist/error.mjs ADDED
@@ -0,0 +1,14 @@
1
+ //#region error.ts
2
+ /**
3
+ * Error thrown when an operation is cancelled
4
+ */
5
+ var CancelError = class extends Error {
6
+ constructor(message, signal) {
7
+ super(message);
8
+ this.signal = signal;
9
+ }
10
+ };
11
+
12
+ //#endregion
13
+ export { CancelError };
14
+ //# sourceMappingURL=error.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.mjs","names":["signal?: NodeJS.Signals"],"sources":["../error.ts"],"sourcesContent":["/**\n * Error thrown when an operation is cancelled\n */\nexport class CancelError extends Error {\n\tconstructor(\n\t\tmessage?: string,\n\t\treadonly signal?: NodeJS.Signals\n\t) {\n\t\tsuper(message);\n\t}\n}\n"],"mappings":";;;;AAGA,IAAa,cAAb,cAAiC,MAAM;CACtC,YACC,SACA,AAASA,QACR;AACD,QAAM,QAAQ;EAFL"}
@@ -0,0 +1,24 @@
1
+ //#region gitignore.d.ts
2
+ /**
3
+ * Appends to a file that follows a .gitignore-like structure any missing
4
+ * Wrangler-related entries (`.wrangler`, `.dev.vars*`, `.env*`, and their
5
+ * negated example patterns).
6
+ *
7
+ * Creates the file if it does not already exist.
8
+ *
9
+ * @param filePath Absolute or relative path to the ignore file to update.
10
+ */
11
+ declare function maybeAppendWranglerToGitIgnoreLikeFile(filePath: string): void;
12
+ /**
13
+ * Appends any missing Wrangler-related entries to the project's `.gitignore` file.
14
+ *
15
+ * Bails out only when *neither* a `.gitignore` file nor a `.git` directory exists,
16
+ * which indicates the project is likely not targeting/using git. If either one is
17
+ * present the entries are appended (creating `.gitignore` if needed).
18
+ *
19
+ * @param projectPath Root directory of the project.
20
+ */
21
+ declare function maybeAppendWranglerToGitIgnore(projectPath: string): void;
22
+ //#endregion
23
+ export { maybeAppendWranglerToGitIgnore, maybeAppendWranglerToGitIgnoreLikeFile };
24
+ //# sourceMappingURL=gitignore.d.mts.map
@@ -0,0 +1,75 @@
1
+ import { brandColor, dim } from "./colors.mjs";
2
+ import { spinner } from "./interactive.mjs";
3
+ import { readFileSync } from "@cloudflare/workers-utils";
4
+ import { appendFileSync, existsSync, statSync, writeFileSync } from "node:fs";
5
+ import { basename } from "node:path";
6
+
7
+ //#region gitignore.ts
8
+ /**
9
+ * Appends to a file that follows a .gitignore-like structure any missing
10
+ * Wrangler-related entries (`.wrangler`, `.dev.vars*`, `.env*`, and their
11
+ * negated example patterns).
12
+ *
13
+ * Creates the file if it does not already exist.
14
+ *
15
+ * @param filePath Absolute or relative path to the ignore file to update.
16
+ */
17
+ function maybeAppendWranglerToGitIgnoreLikeFile(filePath) {
18
+ const filePreExisted = existsSync(filePath);
19
+ if (!filePreExisted) writeFileSync(filePath, "");
20
+ const existingGitIgnoreContent = readFileSync(filePath);
21
+ const wranglerGitIgnoreFilesToAdd = [];
22
+ const hasDotWrangler = existingGitIgnoreContent.match(/^\/?\.wrangler(\/|\s|$)/m);
23
+ if (!hasDotWrangler) wranglerGitIgnoreFilesToAdd.push(".wrangler");
24
+ if (!existingGitIgnoreContent.match(/^\/?\.dev\.vars\*(\s|$)/m)) wranglerGitIgnoreFilesToAdd.push(".dev.vars*");
25
+ if (!existingGitIgnoreContent.match(/^!\/?\.dev\.vars\.example(\s|$)/m)) wranglerGitIgnoreFilesToAdd.push("!.dev.vars.example");
26
+ if (!existingGitIgnoreContent.match(/^\/?\.env\*?(\..*?)?(\s|$)/m)) wranglerGitIgnoreFilesToAdd.push(".env*");
27
+ if (!existingGitIgnoreContent.match(/^!\/?\.env\.example(\s|$)/m)) wranglerGitIgnoreFilesToAdd.push("!.env.example");
28
+ if (wranglerGitIgnoreFilesToAdd.length === 0) return;
29
+ const s = spinner();
30
+ s.start(`Adding Wrangler files to the ${basename(filePath)} file`);
31
+ const linesToAppend = [...filePreExisted ? ["", ...!existingGitIgnoreContent.match(/\n\s*$/) ? [""] : []] : []];
32
+ if (!hasDotWrangler && wranglerGitIgnoreFilesToAdd.length > 1) linesToAppend.push("# wrangler files");
33
+ wranglerGitIgnoreFilesToAdd.forEach((line) => linesToAppend.push(line));
34
+ linesToAppend.push("");
35
+ appendFileSync(filePath, linesToAppend.join("\n"));
36
+ const fileName = basename(filePath);
37
+ s.stop(`${brandColor(filePreExisted ? "updated" : "created")} ${dim(`${fileName} file`)}`);
38
+ }
39
+ /**
40
+ * Appends any missing Wrangler-related entries to the project's `.gitignore` file.
41
+ *
42
+ * Bails out only when *neither* a `.gitignore` file nor a `.git` directory exists,
43
+ * which indicates the project is likely not targeting/using git. If either one is
44
+ * present the entries are appended (creating `.gitignore` if needed).
45
+ *
46
+ * @param projectPath Root directory of the project.
47
+ */
48
+ function maybeAppendWranglerToGitIgnore(projectPath) {
49
+ const gitIgnorePath = `${projectPath}/.gitignore`;
50
+ const gitIgnorePreExisted = existsSync(gitIgnorePath);
51
+ const gitDirExists = directoryExists(`${projectPath}/.git`);
52
+ if (!gitIgnorePreExisted && !gitDirExists) return;
53
+ maybeAppendWranglerToGitIgnoreLikeFile(gitIgnorePath);
54
+ }
55
+ /**
56
+ * Checks whether a directory exists at the given path.
57
+ *
58
+ * Returns `false` when the path does not exist (`ENOENT`). Re-throws any
59
+ * other filesystem error.
60
+ *
61
+ * @param path Path to check
62
+ * @returns `true` if a directory exists at `path`, `false` otherwise
63
+ */
64
+ function directoryExists(path$1) {
65
+ try {
66
+ return statSync(path$1).isDirectory();
67
+ } catch (error) {
68
+ if (error.code === "ENOENT") return false;
69
+ throw new Error(error);
70
+ }
71
+ }
72
+
73
+ //#endregion
74
+ export { maybeAppendWranglerToGitIgnore, maybeAppendWranglerToGitIgnoreLikeFile };
75
+ //# sourceMappingURL=gitignore.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.mjs","names":["wranglerGitIgnoreFilesToAdd: string[]","path"],"sources":["../gitignore.ts"],"sourcesContent":["import { appendFileSync, existsSync, statSync, writeFileSync } from \"node:fs\";\nimport { basename } from \"node:path\";\nimport { readFileSync } from \"@cloudflare/workers-utils\";\nimport { brandColor, dim } from \"./colors\";\nimport { spinner } from \"./interactive\";\n\n/**\n * Appends to a file that follows a .gitignore-like structure any missing\n * Wrangler-related entries (`.wrangler`, `.dev.vars*`, `.env*`, and their\n * negated example patterns).\n *\n * Creates the file if it does not already exist.\n *\n * @param filePath Absolute or relative path to the ignore file to update.\n */\nexport function maybeAppendWranglerToGitIgnoreLikeFile(filePath: string): void {\n\tconst filePreExisted = existsSync(filePath);\n\n\tif (!filePreExisted) {\n\t\twriteFileSync(filePath, \"\");\n\t}\n\n\tconst existingGitIgnoreContent = readFileSync(filePath);\n\tconst wranglerGitIgnoreFilesToAdd: string[] = [];\n\n\tconst hasDotWrangler = existingGitIgnoreContent.match(\n\t\t/^\\/?\\.wrangler(\\/|\\s|$)/m\n\t);\n\tif (!hasDotWrangler) {\n\t\twranglerGitIgnoreFilesToAdd.push(\".wrangler\");\n\t}\n\n\tconst hasDotDevDotVars = existingGitIgnoreContent.match(\n\t\t/^\\/?\\.dev\\.vars\\*(\\s|$)/m\n\t);\n\tif (!hasDotDevDotVars) {\n\t\twranglerGitIgnoreFilesToAdd.push(\".dev.vars*\");\n\t}\n\n\tconst hasDotDevVarsExample = existingGitIgnoreContent.match(\n\t\t/^!\\/?\\.dev\\.vars\\.example(\\s|$)/m\n\t);\n\tif (!hasDotDevVarsExample) {\n\t\twranglerGitIgnoreFilesToAdd.push(\"!.dev.vars.example\");\n\t}\n\n\t/**\n\t * We check for the following type of occurrences:\n\t *\n\t * ```\n\t * .env\n\t * .env*\n\t * .env.<local|production|staging|...>\n\t * .env*.<local|production|staging|...>\n\t * ```\n\t *\n\t * Any of these may alone on a line or be followed by a space and a trailing comment:\n\t *\n\t * ```\n\t * .env.<local|production|staging> # some trailing comment\n\t * ```\n\t */\n\tconst hasDotEnv = existingGitIgnoreContent.match(\n\t\t/^\\/?\\.env\\*?(\\..*?)?(\\s|$)/m\n\t);\n\tif (!hasDotEnv) {\n\t\twranglerGitIgnoreFilesToAdd.push(\".env*\");\n\t}\n\n\tconst hasDotEnvExample = existingGitIgnoreContent.match(\n\t\t/^!\\/?\\.env\\.example(\\s|$)/m\n\t);\n\tif (!hasDotEnvExample) {\n\t\twranglerGitIgnoreFilesToAdd.push(\"!.env.example\");\n\t}\n\n\tif (wranglerGitIgnoreFilesToAdd.length === 0) {\n\t\treturn;\n\t}\n\n\tconst s = spinner();\n\ts.start(`Adding Wrangler files to the ${basename(filePath)} file`);\n\n\tconst linesToAppend = [\n\t\t...(filePreExisted\n\t\t\t? [\"\", ...(!existingGitIgnoreContent.match(/\\n\\s*$/) ? [\"\"] : [])]\n\t\t\t: []),\n\t];\n\n\tif (!hasDotWrangler && wranglerGitIgnoreFilesToAdd.length > 1) {\n\t\tlinesToAppend.push(\"# wrangler files\");\n\t}\n\n\twranglerGitIgnoreFilesToAdd.forEach((line) => linesToAppend.push(line));\n\n\tlinesToAppend.push(\"\");\n\n\tappendFileSync(filePath, linesToAppend.join(\"\\n\"));\n\n\tconst fileName = basename(filePath);\n\n\ts.stop(\n\t\t`${brandColor(filePreExisted ? \"updated\" : \"created\")} ${dim(\n\t\t\t`${fileName} file`\n\t\t)}`\n\t);\n}\n\n/**\n * Appends any missing Wrangler-related entries to the project's `.gitignore` file.\n *\n * Bails out only when *neither* a `.gitignore` file nor a `.git` directory exists,\n * which indicates the project is likely not targeting/using git. If either one is\n * present the entries are appended (creating `.gitignore` if needed).\n *\n * @param projectPath Root directory of the project.\n */\nexport function maybeAppendWranglerToGitIgnore(projectPath: string): void {\n\tconst gitIgnorePath = `${projectPath}/.gitignore`;\n\tconst gitIgnorePreExisted = existsSync(gitIgnorePath);\n\tconst gitDirExists = directoryExists(`${projectPath}/.git`);\n\n\tif (!gitIgnorePreExisted && !gitDirExists) {\n\t\t// if there is no .gitignore file and neither a .git directory\n\t\t// then bail as the project is likely not targeting/using git\n\t\treturn;\n\t}\n\n\tmaybeAppendWranglerToGitIgnoreLikeFile(gitIgnorePath);\n}\n\n/**\n * Checks whether a directory exists at the given path.\n *\n * Returns `false` when the path does not exist (`ENOENT`). Re-throws any\n * other filesystem error.\n *\n * @param path Path to check\n * @returns `true` if a directory exists at `path`, `false` otherwise\n */\nfunction directoryExists(path: string): boolean {\n\ttry {\n\t\tconst stat = statSync(path);\n\t\treturn stat.isDirectory();\n\t} catch (error) {\n\t\tif ((error as { code: string }).code === \"ENOENT\") {\n\t\t\treturn false;\n\t\t}\n\t\tthrow new Error(error as string);\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAeA,SAAgB,uCAAuC,UAAwB;CAC9E,MAAM,iBAAiB,WAAW,SAAS;AAE3C,KAAI,CAAC,eACJ,eAAc,UAAU,GAAG;CAG5B,MAAM,2BAA2B,aAAa,SAAS;CACvD,MAAMA,8BAAwC,EAAE;CAEhD,MAAM,iBAAiB,yBAAyB,MAC/C,2BACA;AACD,KAAI,CAAC,eACJ,6BAA4B,KAAK,YAAY;AAM9C,KAAI,CAHqB,yBAAyB,MACjD,2BACA,CAEA,6BAA4B,KAAK,aAAa;AAM/C,KAAI,CAHyB,yBAAyB,MACrD,mCACA,CAEA,6BAA4B,KAAK,qBAAqB;AAsBvD,KAAI,CAHc,yBAAyB,MAC1C,8BACA,CAEA,6BAA4B,KAAK,QAAQ;AAM1C,KAAI,CAHqB,yBAAyB,MACjD,6BACA,CAEA,6BAA4B,KAAK,gBAAgB;AAGlD,KAAI,4BAA4B,WAAW,EAC1C;CAGD,MAAM,IAAI,SAAS;AACnB,GAAE,MAAM,gCAAgC,SAAS,SAAS,CAAC,OAAO;CAElE,MAAM,gBAAgB,CACrB,GAAI,iBACD,CAAC,IAAI,GAAI,CAAC,yBAAyB,MAAM,SAAS,GAAG,CAAC,GAAG,GAAG,EAAE,CAAE,GAChE,EAAE,CACL;AAED,KAAI,CAAC,kBAAkB,4BAA4B,SAAS,EAC3D,eAAc,KAAK,mBAAmB;AAGvC,6BAA4B,SAAS,SAAS,cAAc,KAAK,KAAK,CAAC;AAEvE,eAAc,KAAK,GAAG;AAEtB,gBAAe,UAAU,cAAc,KAAK,KAAK,CAAC;CAElD,MAAM,WAAW,SAAS,SAAS;AAEnC,GAAE,KACD,GAAG,WAAW,iBAAiB,YAAY,UAAU,CAAC,GAAG,IACxD,GAAG,SAAS,OACZ,GACD;;;;;;;;;;;AAYF,SAAgB,+BAA+B,aAA2B;CACzE,MAAM,gBAAgB,GAAG,YAAY;CACrC,MAAM,sBAAsB,WAAW,cAAc;CACrD,MAAM,eAAe,gBAAgB,GAAG,YAAY,OAAO;AAE3D,KAAI,CAAC,uBAAuB,CAAC,aAG5B;AAGD,wCAAuC,cAAc;;;;;;;;;;;AAYtD,SAAS,gBAAgB,QAAuB;AAC/C,KAAI;AAEH,SADa,SAASC,OAAK,CACf,aAAa;UACjB,OAAO;AACf,MAAK,MAA2B,SAAS,SACxC,QAAO;AAER,QAAM,IAAI,MAAM,MAAgB"}
@@ -0,0 +1,97 @@
1
+ import { checkMacOSVersion } from "./check-macos-version.mjs";
2
+ import { showCursor } from "./cursor.mjs";
3
+
4
+ //#region index.d.ts
5
+ declare const shapes: {
6
+ diamond: string;
7
+ dash: string;
8
+ radioInactive: string;
9
+ radioActive: string;
10
+ backActive: string;
11
+ backInactive: string;
12
+ bar: string;
13
+ leftT: string;
14
+ rigthT: string;
15
+ arrows: {
16
+ left: string;
17
+ right: string;
18
+ };
19
+ corners: {
20
+ tl: string;
21
+ bl: string;
22
+ tr: string;
23
+ br: string;
24
+ };
25
+ };
26
+ declare const status: {
27
+ error: string;
28
+ warning: string;
29
+ info: string;
30
+ success: string;
31
+ cancel: string;
32
+ };
33
+ declare const space: (n?: number) => string;
34
+ declare const LOGGER_LEVELS: {
35
+ readonly none: -1;
36
+ readonly error: 0;
37
+ readonly warn: 1;
38
+ readonly info: 2;
39
+ readonly log: 3;
40
+ readonly debug: 4;
41
+ };
42
+ type LoggerLevel = keyof typeof LOGGER_LEVELS;
43
+ declare function setLogLevel(level: LoggerLevel): void;
44
+ declare function getLogLevel(): LoggerLevel;
45
+ declare const logRaw: (msg: string) => void;
46
+ declare const log: (msg: string) => void;
47
+ declare const newline: () => void;
48
+ type FormatOptions = {
49
+ linePrefix?: string;
50
+ firstLinePrefix?: string;
51
+ newlineBefore?: boolean;
52
+ newlineAfter?: boolean;
53
+ formatLine?: (line: string) => string;
54
+ multiline?: boolean;
55
+ };
56
+ declare const format: (msg: string, {
57
+ linePrefix,
58
+ firstLinePrefix,
59
+ newlineBefore,
60
+ newlineAfter,
61
+ formatLine,
62
+ multiline
63
+ }?: FormatOptions) => string;
64
+ declare const updateStatus: (msg: string, printNewLine?: boolean) => void;
65
+ declare const startSection: (heading: string, subheading?: string, printNewLine?: boolean) => void;
66
+ declare const endSection: (heading: string, subheading?: string) => void;
67
+ declare const cancel: (msg: string, {
68
+ shape,
69
+ multiline
70
+ }?: {
71
+ shape?: string | undefined;
72
+ multiline?: boolean | undefined;
73
+ }) => void;
74
+ declare const warn: (msg: string, {
75
+ shape,
76
+ multiline,
77
+ newlineBefore
78
+ }?: {
79
+ shape?: string | undefined;
80
+ multiline?: boolean | undefined;
81
+ newlineBefore?: boolean | undefined;
82
+ }) => void;
83
+ declare const success: (msg: string, {
84
+ shape,
85
+ multiline
86
+ }?: {
87
+ shape?: string | undefined;
88
+ multiline?: boolean | undefined;
89
+ }) => void;
90
+ declare const stripAnsi: (str: string) => string;
91
+ declare const linkRegex: RegExp;
92
+ declare const hyperlink: (url: string, label?: string) => string;
93
+ declare const crash: (msg?: string, extra?: string) => never;
94
+ declare const error: (msg?: string, extra?: string, corner?: string) => void;
95
+ //#endregion
96
+ export { LoggerLevel, cancel, checkMacOSVersion, crash, endSection, error, format, getLogLevel, hyperlink, linkRegex, log, logRaw, newline, setLogLevel, shapes, showCursor, space, startSection, status, stripAnsi, success, updateStatus, warn };
97
+ //# sourceMappingURL=index.d.mts.map