@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.
@@ -0,0 +1,420 @@
1
+ import { blue, bold, brandColor, dim, gray, white } from "./colors.mjs";
2
+ import { CancelError } from "./error.mjs";
3
+ import SelectRefreshablePrompt from "./select-list.mjs";
4
+ import { stdout } from "./streams.mjs";
5
+ import { cancel, crash, logRaw, newline, shapes, space, status, stripAnsi } from "./index.mjs";
6
+ import { ConfirmPrompt, MultiSelectPrompt, SelectPrompt, TextPrompt, isCancel } from "@clack/core";
7
+ import { createLogUpdate } from "log-update";
8
+
9
+ //#region interactive.ts
10
+ const logUpdate = createLogUpdate(stdout);
11
+ const grayBar = gray(shapes.bar);
12
+ const blCorner = gray(shapes.corners.bl);
13
+ const leftT = gray(shapes.leftT);
14
+ function acceptDefault(promptConfig, renderers, initialValue) {
15
+ const error = promptConfig.validate?.(initialValue);
16
+ if (error) if (promptConfig.throwOnError) throw new Error(error);
17
+ else crash(error);
18
+ logRaw(renderers.submit({ value: initialValue }).join("\n"));
19
+ return initialValue;
20
+ }
21
+ const inputPrompt = async (promptConfig) => {
22
+ const renderers = {
23
+ ...getRenderers(promptConfig),
24
+ ...promptConfig.renderers
25
+ };
26
+ let prompt;
27
+ const dispatchRender = (props, p) => {
28
+ let state = props.state;
29
+ if (state === "initial" && promptConfig.initialErrorMessage) {
30
+ state = "error";
31
+ props.error = promptConfig.initialErrorMessage;
32
+ }
33
+ return renderers[state](props, p).join("\n");
34
+ };
35
+ if (promptConfig.type === "select") {
36
+ const initialValue = String(promptConfig.defaultValue);
37
+ if (promptConfig.acceptDefault) return acceptDefault(promptConfig, renderers, initialValue);
38
+ prompt = new SelectPrompt({
39
+ ...promptConfig,
40
+ options: promptConfig.options.filter((o) => !o.hidden),
41
+ initialValue,
42
+ render() {
43
+ return dispatchRender(this, prompt);
44
+ }
45
+ });
46
+ } else if (promptConfig.type === "confirm") {
47
+ const initialValue = Boolean(promptConfig.defaultValue);
48
+ if (promptConfig.acceptDefault) return acceptDefault(promptConfig, renderers, initialValue);
49
+ prompt = new ConfirmPrompt({
50
+ ...promptConfig,
51
+ initialValue,
52
+ active: promptConfig.activeText || "",
53
+ inactive: promptConfig.inactiveText || "",
54
+ render() {
55
+ return dispatchRender(this, prompt);
56
+ }
57
+ });
58
+ } else if (promptConfig.type == "multiselect") {
59
+ let initialValues;
60
+ if (Array.isArray(promptConfig.defaultValue)) initialValues = promptConfig.defaultValue;
61
+ else if (promptConfig.defaultValue !== void 0) initialValues = [String(promptConfig.defaultValue)];
62
+ if (promptConfig.acceptDefault) return acceptDefault(promptConfig, renderers, initialValues);
63
+ prompt = new MultiSelectPrompt({
64
+ ...promptConfig,
65
+ options: promptConfig.options,
66
+ initialValues,
67
+ render() {
68
+ return dispatchRender(this, prompt);
69
+ }
70
+ });
71
+ } else if (promptConfig.type === "list") {
72
+ const initialValue = String(promptConfig.defaultValue);
73
+ if (promptConfig.acceptDefault) return acceptDefault(promptConfig, renderers, initialValue);
74
+ prompt = new SelectRefreshablePrompt({
75
+ ...promptConfig,
76
+ onRefresh: promptConfig.onRefresh ?? (() => Promise.resolve(promptConfig.options)),
77
+ initialValue,
78
+ render() {
79
+ return dispatchRender(this, prompt);
80
+ }
81
+ });
82
+ } else {
83
+ const initialValue = promptConfig.initialValue ?? String(promptConfig.defaultValue ?? "");
84
+ if (promptConfig.acceptDefault) return acceptDefault(promptConfig, renderers, initialValue);
85
+ prompt = new TextPrompt({
86
+ ...promptConfig,
87
+ initialValue: promptConfig.initialValue,
88
+ defaultValue: String(promptConfig.defaultValue ?? ""),
89
+ render() {
90
+ return dispatchRender(this, prompt);
91
+ }
92
+ });
93
+ }
94
+ const input = await prompt.prompt();
95
+ if (isCancel(input)) if (promptConfig.throwOnError) throw new CancelError("Operation cancelled");
96
+ else {
97
+ cancel("Operation cancelled");
98
+ process.exit(0);
99
+ }
100
+ return input;
101
+ };
102
+ const renderSubmit = (config, value) => {
103
+ const { question, label } = config;
104
+ if (config.type !== "confirm" && !value) return [`${leftT} ${question} ${dim("(skipped)")}`, `${grayBar}`];
105
+ const content = config.type === "confirm" ? `${grayBar} ${brandColor(value)} ${dim(label)}` : `${grayBar} ${brandColor(label)} ${dim(value)}`;
106
+ return [
107
+ `${leftT} ${question}`,
108
+ content,
109
+ `${grayBar}`
110
+ ];
111
+ };
112
+ const getRenderers = (config) => {
113
+ switch (config.type) {
114
+ case "select": return getSelectRenderers(config);
115
+ case "confirm": return getConfirmRenderers(config);
116
+ case "text": return getTextRenderers(config);
117
+ case "multiselect": return getSelectRenderers(config);
118
+ case "list": return getSelectListRenderers(config);
119
+ }
120
+ };
121
+ const getTextRenderers = (config) => {
122
+ const { question } = config;
123
+ const helpText = config.helpText ?? "";
124
+ const format = config.format ?? ((val) => String(val));
125
+ const defaultValue = config.defaultValue?.toString() ?? "";
126
+ const activeRenderer = (props) => {
127
+ const { valueWithCursor } = props;
128
+ return [
129
+ `${blCorner} ${bold(question)} ${dim(helpText)}`,
130
+ `${space(2)}${format(valueWithCursor || dim(defaultValue))}`,
131
+ ``
132
+ ];
133
+ };
134
+ return {
135
+ initial: () => [
136
+ `${blCorner} ${bold(question)} ${dim(helpText)}`,
137
+ `${space(2)}${gray(format(defaultValue))}`,
138
+ ``
139
+ ],
140
+ active: activeRenderer,
141
+ error: ({ value, error }) => [
142
+ `${leftT} ${status.error} ${dim(error)}`,
143
+ `${grayBar}`,
144
+ `${blCorner} ${question} ${dim(helpText)}`,
145
+ `${space(2)}${format(value)}`,
146
+ ``
147
+ ],
148
+ submit: ({ value }) => renderSubmit(config, format(value ?? "")),
149
+ cancel: activeRenderer
150
+ };
151
+ };
152
+ const getSelectRenderers = (config) => {
153
+ const { options, question, helpText: _helpText } = config;
154
+ const helpText = _helpText ?? "";
155
+ const maxItemsPerPage = config.maxItemsPerPage ?? 32;
156
+ const defaultRenderer = ({ cursor = 0, value }) => {
157
+ const renderOption = (opt, i) => {
158
+ const { label: optionLabel, value: optionValue } = opt;
159
+ const active = i === cursor;
160
+ const isInListOfValues = Array.isArray(value) && value.includes(optionValue);
161
+ const color = isInListOfValues || active ? blue : white;
162
+ const text = active ? color.underline(optionLabel) : color(optionLabel);
163
+ const sublabel = opt.sublabel ? color.grey(opt.sublabel) : "";
164
+ const indicator = isInListOfValues || active && !Array.isArray(value) ? color(opt.activeIcon ?? shapes.radioActive) : color(opt.inactiveIcon ?? shapes.radioInactive);
165
+ return `${space(2)}${indicator} ${text} ${sublabel}`;
166
+ };
167
+ const renderOptionCondition = (_, i) => {
168
+ if (options.length <= maxItemsPerPage) return true;
169
+ if (i < cursor) return options.length - i <= maxItemsPerPage;
170
+ return cursor + maxItemsPerPage > i;
171
+ };
172
+ const visibleOptions = options.filter((o) => !o.hidden);
173
+ const activeOption = visibleOptions.at(cursor);
174
+ const lines = [
175
+ `${blCorner} ${bold(question)} ${dim(helpText)}`,
176
+ `${cursor > 0 && options.length > maxItemsPerPage ? `${space(2)}${dim("...")}\n` : ""}${visibleOptions.map(renderOption).filter(renderOptionCondition).join(`\n`)}${cursor + maxItemsPerPage < options.length && options.length > maxItemsPerPage ? `\n${space(2)}${dim("...")}` : ""}`,
177
+ ``
178
+ ];
179
+ if (activeOption?.description) {
180
+ const wordSegmenter = new Intl.Segmenter("en", { granularity: "word" });
181
+ const padding = space(2);
182
+ const availableWidth = process.stdout.columns - stripAnsi(padding).length * 2;
183
+ const description = stripAnsi(activeOption.description);
184
+ const descriptionLines = [];
185
+ let descriptionLineNumber = 0;
186
+ for (const data of wordSegmenter.segment(description)) {
187
+ let line = descriptionLines[descriptionLineNumber] ?? "";
188
+ if (line.length + data.segment.length > availableWidth) {
189
+ descriptionLineNumber++;
190
+ line = "";
191
+ if (data.segment.match(/^\s+$/)) continue;
192
+ }
193
+ descriptionLines[descriptionLineNumber] = line + data.segment;
194
+ }
195
+ lines.push(dim(descriptionLines.map((line) => padding + line + padding).join("\n")), ``);
196
+ }
197
+ return lines;
198
+ };
199
+ return {
200
+ initial: defaultRenderer,
201
+ active: defaultRenderer,
202
+ confirm: defaultRenderer,
203
+ error: (opts, prompt) => {
204
+ return [
205
+ `${leftT} ${status.error} ${dim(opts.error)}`,
206
+ `${grayBar}`,
207
+ ...defaultRenderer(opts, prompt)
208
+ ];
209
+ },
210
+ submit: ({ value }) => {
211
+ if (Array.isArray(value)) return renderSubmit(config, options.filter((o) => value.includes(o.value)).map((o) => o.label).join(", "));
212
+ return renderSubmit(config, options.find((o) => o.value === value)?.label);
213
+ },
214
+ cancel: defaultRenderer
215
+ };
216
+ };
217
+ const getSelectListRenderers = (config) => {
218
+ const { question, helpText: _helpText } = config;
219
+ let options = config.options;
220
+ const helpText = _helpText ?? "";
221
+ const { rows } = stdout;
222
+ const defaultRenderer = ({ cursor, value }, prompt) => {
223
+ if (prompt instanceof SelectRefreshablePrompt) options = prompt.options;
224
+ cursor = cursor ?? 0;
225
+ let smallCursor = 0;
226
+ const renderOption = (opt, i) => {
227
+ const { label: optionLabel, value: optionValueAny } = opt;
228
+ const optionValue = optionValueAny.toString();
229
+ const active = i === smallCursor;
230
+ const isInListOfValues = Array.isArray(value) && value.includes(optionValue);
231
+ const color = isInListOfValues || active ? blue : white;
232
+ const text = active ? color.underline(optionLabel) : color(optionLabel);
233
+ const indicator = isInListOfValues || active && !Array.isArray(value) ? color(shapes.radioActive) : color(shapes.radioInactive);
234
+ const indicatorMargin = 2;
235
+ const detailBulletpointMargin = indicatorMargin + 4;
236
+ return [`${space(indicatorMargin)}${indicator} ${text}`, ...opt.details.map((detail, j) => `${space(detailBulletpointMargin)}${j === opt.details.length - 1 ? gray(shapes.corners.bl) : grayBar} ${detail}`)];
237
+ };
238
+ const pages = [];
239
+ let current = {
240
+ size: 0,
241
+ options: []
242
+ };
243
+ const VERTICAL_MARGIN = 6;
244
+ for (const option of options) {
245
+ const optionHeight = option.details.length + 1;
246
+ if (current.size + optionHeight > rows - VERTICAL_MARGIN) {
247
+ pages.push(current.options);
248
+ current = {
249
+ size: optionHeight,
250
+ options: [option]
251
+ };
252
+ continue;
253
+ }
254
+ current.size += optionHeight;
255
+ current.options.push(option);
256
+ }
257
+ if (current.size !== 0) pages.push(current.options);
258
+ let isFirstPage = true;
259
+ let isLastPage = false;
260
+ let page = [];
261
+ let len = 0;
262
+ for (let i = 0; i < pages.length; i++) {
263
+ const pageIter = pages[i];
264
+ if (cursor >= len && pageIter.length + len > cursor) {
265
+ isFirstPage = i === 0;
266
+ isLastPage = i === pages.length - 1;
267
+ page = pageIter;
268
+ smallCursor = cursor - len;
269
+ break;
270
+ }
271
+ len += pageIter.length;
272
+ }
273
+ return [
274
+ `${blCorner} ${bold(question)} ${dim(helpText)}`,
275
+ ...!isFirstPage ? [`${space(2)}${dim("...")}`] : [],
276
+ ...page.map(renderOption).reduce((prev, now) => [...prev, ...now], []),
277
+ ...!isLastPage ? [`${space(2)}${dim("...")}`] : []
278
+ ];
279
+ };
280
+ return {
281
+ initial: defaultRenderer,
282
+ active: defaultRenderer,
283
+ confirm: defaultRenderer,
284
+ error: (opts, prompt) => {
285
+ return [
286
+ `${leftT} ${status.error} ${dim(opts.error)}`,
287
+ `${grayBar}`,
288
+ ...defaultRenderer(opts, prompt)
289
+ ];
290
+ },
291
+ submit: ({ value }) => {
292
+ if (Array.isArray(value)) return renderSubmit(config, options.filter((o) => value.includes(o.value)).map((o) => o.value).join(", "));
293
+ return renderSubmit(config, options.find((o) => o.value === value)?.value);
294
+ },
295
+ cancel: defaultRenderer
296
+ };
297
+ };
298
+ const getConfirmRenderers = (config) => {
299
+ const { activeText, inactiveText, question, helpText: _helpText } = config;
300
+ const helpText = _helpText ?? "";
301
+ const active = activeText || "Yes";
302
+ const inactive = inactiveText || "No";
303
+ const defaultRenderer = ({ value }) => {
304
+ const yesColor = value ? blue.underline : dim;
305
+ const noColor = value ? dim : blue.underline;
306
+ return [`${blCorner} ${bold(question)} ${dim(helpText)}`, `${space(2)}${yesColor(active)} / ${noColor(inactive)}`];
307
+ };
308
+ return {
309
+ initial: defaultRenderer,
310
+ active: defaultRenderer,
311
+ confirm: defaultRenderer,
312
+ error: defaultRenderer,
313
+ submit: ({ value }) => renderSubmit(config, value ? "yes" : "no"),
314
+ cancel: defaultRenderer
315
+ };
316
+ };
317
+ const spinnerFrames = {
318
+ clockwise: [
319
+ "┤",
320
+ "┘",
321
+ "┴",
322
+ "└",
323
+ "├",
324
+ "┌",
325
+ "┬",
326
+ "┐"
327
+ ],
328
+ vertical: [
329
+ "▁",
330
+ "▃",
331
+ "▄",
332
+ "▅",
333
+ "▆",
334
+ "▇",
335
+ "▆",
336
+ "▅",
337
+ "▄",
338
+ "▃"
339
+ ]
340
+ };
341
+ const ellipsisFrames = [
342
+ "",
343
+ ".",
344
+ "..",
345
+ "...",
346
+ " ..",
347
+ " .",
348
+ ""
349
+ ];
350
+ const spinner = (frames = spinnerFrames.clockwise, color = brandColor) => {
351
+ const frameRate = 120;
352
+ let loop = null;
353
+ let startMsg;
354
+ let currentMsg;
355
+ function clearLoop() {
356
+ if (loop) clearTimeout(loop);
357
+ loop = null;
358
+ }
359
+ return {
360
+ start(msg, helpText) {
361
+ helpText ||= ``;
362
+ currentMsg = msg;
363
+ startMsg = currentMsg;
364
+ if (helpText !== void 0 && helpText.length > 0) startMsg += ` ${dim(helpText)}`;
365
+ if (isInteractive()) {
366
+ let index = 0;
367
+ clearLoop();
368
+ loop = setInterval(() => {
369
+ index++;
370
+ const spinnerFrame = frames[index % frames.length];
371
+ const ellipsisFrame = ellipsisFrames[index % ellipsisFrames.length];
372
+ if (msg) logUpdate(`${color(spinnerFrame)} ${currentMsg} ${ellipsisFrame}`);
373
+ }, frameRate);
374
+ } else logRaw(`${leftT} ${startMsg}`);
375
+ },
376
+ update(msg) {
377
+ currentMsg = msg;
378
+ },
379
+ stop(msg) {
380
+ if (isInteractive()) {
381
+ logUpdate.clear();
382
+ if (msg) {
383
+ logUpdate(`${leftT} ${startMsg}\n${grayBar} ${msg}`);
384
+ logUpdate.done();
385
+ newline();
386
+ }
387
+ clearLoop();
388
+ } else {
389
+ if (msg !== void 0) logRaw(`${grayBar} ${msg}`);
390
+ newline();
391
+ }
392
+ }
393
+ };
394
+ };
395
+ const unwrapFactory = (input) => {
396
+ return typeof input === "function" ? input() : input;
397
+ };
398
+ const spinnerWhile = async (opts) => {
399
+ const s = opts.spinner ?? spinner();
400
+ s.start(unwrapFactory(opts.startMessage));
401
+ try {
402
+ return await unwrapFactory(opts.promise);
403
+ } finally {
404
+ s.stop(unwrapFactory(opts.endMessage));
405
+ }
406
+ };
407
+ /**
408
+ * Test whether the process is "interactive".
409
+ */
410
+ function isInteractive() {
411
+ try {
412
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
413
+ } catch {
414
+ return false;
415
+ }
416
+ }
417
+
418
+ //#endregion
419
+ export { blCorner, getRenderers, grayBar, inputPrompt, isInteractive, leftT, spinner, spinnerFrames, spinnerWhile };
420
+ //# sourceMappingURL=interactive.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interactive.mjs","names":["prompt:\n\t\t| SelectPrompt<Option>\n\t\t| TextPrompt\n\t\t| ConfirmPrompt\n\t\t| MultiSelectPrompt<Option>\n\t\t| SelectRefreshablePrompt","initialValues: string[] | undefined","defaultRenderer: Renderer","descriptionLines: string[]","pages: OptionWithDetails[][]","current: { size: number; options: OptionWithDetails[] }","page: OptionWithDetails[]","loop: ReturnType<typeof setTimeout> | null","startMsg: string","currentMsg: string"],"sources":["../interactive.ts"],"sourcesContent":["import {\n\tConfirmPrompt,\n\tisCancel,\n\tMultiSelectPrompt,\n\tSelectPrompt,\n\tTextPrompt,\n} from \"@clack/core\";\nimport { createLogUpdate } from \"log-update\";\nimport { blue, bold, brandColor, dim, gray, white } from \"./colors\";\nimport { CancelError } from \"./error\";\nimport SelectRefreshablePrompt from \"./select-list\";\nimport { stdout } from \"./streams\";\nimport {\n\tcancel,\n\tcrash,\n\tlogRaw,\n\tnewline,\n\tshapes,\n\tspace,\n\tstatus,\n\tstripAnsi,\n} from \"./index\";\nimport type { OptionWithDetails } from \"./select-list\";\nimport type { Prompt } from \"@clack/core\";\n\n// logUpdate writes text to a TTY (it uses escape sequences to move the cursor\n// and clear lines). This function should not be used when running\n// non-interactively.\nconst logUpdate = createLogUpdate(stdout);\n\nexport type Arg = string | boolean | string[] | undefined | number;\nexport const grayBar = gray(shapes.bar);\nexport const blCorner = gray(shapes.corners.bl);\nexport const leftT = gray(shapes.leftT);\n\nexport type Option = {\n\tlabel: string; // user-visible string\n\tsublabel?: string; // user-visible string\n\tdescription?: string;\n\tvalue: string; // underlying key\n\thidden?: boolean;\n\tactiveIcon?: string;\n\tinactiveIcon?: string;\n};\n\nexport type BasePromptConfig = {\n\t// Displayed to the user while prompting for this input\n\tquestion: string;\n\t// Further clarifies the question\n\thelpText?: string;\n\t// The value to use by default\n\tdefaultValue?: Arg;\n\t// The error message to display if the initial value is invalid\n\tinitialErrorMessage?: string | null;\n\t// Accept the initialValue/defaultValue as if the user pressed ENTER when prompted\n\tacceptDefault?: boolean;\n\t// The status label to be shown after submitting\n\tlabel: string;\n\t// Pretty-prints the value in the interactive prompt\n\tformat?: (value: Arg) => string;\n\t// Returns a user displayed error if the value is invalid\n\tvalidate?: (value: Arg) => string | void;\n\t// Override some/all renderers (can be used for custom renderers before hoisting back into shared code)\n\trenderers?: Partial<ReturnType<typeof getRenderers>>;\n\t// Whether to throw an error if the prompt is crashed or cancelled\n\tthrowOnError?: boolean;\n};\n\nexport type TextPromptConfig = BasePromptConfig & {\n\ttype: \"text\";\n\tinitialValue?: string;\n};\nexport type BaseSelectPromptConfig = BasePromptConfig & {\n\toptions: Option[];\n\tmaxItemsPerPage?: number;\n};\n\nexport type SelectPromptConfig = BaseSelectPromptConfig & {\n\ttype: \"select\";\n};\n\nexport type MultiSelectPromptConfig = BaseSelectPromptConfig & {\n\ttype: \"multiselect\";\n};\n\nexport type ConfirmPromptConfig = BasePromptConfig & {\n\ttype: \"confirm\";\n\tactiveText?: string;\n\tinactiveText?: string;\n};\n\nexport type ListPromptConfig = BasePromptConfig & {\n\ttype: \"list\";\n\toptions: OptionWithDetails[];\n\tonRefresh?: () => Promise<OptionWithDetails[]>;\n};\n\nexport type PromptConfig =\n\t| TextPromptConfig\n\t| ConfirmPromptConfig\n\t| SelectPromptConfig\n\t| MultiSelectPromptConfig\n\t| ListPromptConfig;\n\ntype RenderProps =\n\t| Omit<SelectPrompt<Option>, \"prompt\">\n\t| Omit<MultiSelectPrompt<Option>, \"prompt\">\n\t| Omit<TextPrompt, \"prompt\">\n\t| Omit<ConfirmPrompt, \"prompt\">\n\t| Omit<SelectRefreshablePrompt, \"prompt\">;\n\nfunction acceptDefault<T>(\n\tpromptConfig: PromptConfig,\n\trenderers: Pick<ReturnType<typeof getRenderers>, \"submit\">,\n\tinitialValue: T\n): T {\n\tconst error = promptConfig.validate?.(initialValue as Arg);\n\tif (error) {\n\t\tif (promptConfig.throwOnError) {\n\t\t\tthrow new Error(error);\n\t\t} else {\n\t\t\tcrash(error);\n\t\t}\n\t}\n\n\tconst lines = renderers.submit({ value: initialValue as Arg });\n\tlogRaw(lines.join(\"\\n\"));\n\n\treturn initialValue as T;\n}\n\nexport const inputPrompt = async <T = string>(\n\tpromptConfig: PromptConfig\n): Promise<T> => {\n\tconst renderers = {\n\t\t...getRenderers(promptConfig),\n\t\t...promptConfig.renderers,\n\t};\n\n\tlet prompt:\n\t\t| SelectPrompt<Option>\n\t\t| TextPrompt\n\t\t| ConfirmPrompt\n\t\t| MultiSelectPrompt<Option>\n\t\t| SelectRefreshablePrompt;\n\n\t// Looks up the needed renderer by the current state ('initial', 'submitted', etc.)\n\tconst dispatchRender = (props: RenderProps, p: Prompt): string | void => {\n\t\tlet state = props.state;\n\n\t\tif (state === \"initial\" && promptConfig.initialErrorMessage) {\n\t\t\tstate = \"error\";\n\t\t\tprops.error = promptConfig.initialErrorMessage;\n\t\t}\n\n\t\tconst renderedLines = renderers[state](props, p);\n\t\treturn renderedLines.join(\"\\n\");\n\t};\n\n\tif (promptConfig.type === \"select\") {\n\t\tconst initialValue = String(promptConfig.defaultValue);\n\n\t\tif (promptConfig.acceptDefault) {\n\t\t\treturn acceptDefault<T>(promptConfig, renderers, initialValue as T);\n\t\t}\n\n\t\tprompt = new SelectPrompt({\n\t\t\t...promptConfig,\n\t\t\toptions: promptConfig.options.filter((o) => !o.hidden),\n\t\t\tinitialValue,\n\t\t\trender() {\n\t\t\t\treturn dispatchRender(this, prompt);\n\t\t\t},\n\t\t});\n\t} else if (promptConfig.type === \"confirm\") {\n\t\tconst initialValue = Boolean(promptConfig.defaultValue);\n\n\t\tif (promptConfig.acceptDefault) {\n\t\t\treturn acceptDefault<T>(promptConfig, renderers, initialValue as T);\n\t\t}\n\n\t\tprompt = new ConfirmPrompt({\n\t\t\t...promptConfig,\n\t\t\tinitialValue,\n\t\t\tactive: promptConfig.activeText || \"\",\n\t\t\tinactive: promptConfig.inactiveText || \"\",\n\t\t\trender() {\n\t\t\t\treturn dispatchRender(this, prompt);\n\t\t\t},\n\t\t});\n\t} else if (promptConfig.type == \"multiselect\") {\n\t\tlet initialValues: string[] | undefined;\n\t\tif (Array.isArray(promptConfig.defaultValue)) {\n\t\t\tinitialValues = promptConfig.defaultValue;\n\t\t} else if (promptConfig.defaultValue !== undefined) {\n\t\t\tinitialValues = [String(promptConfig.defaultValue)];\n\t\t}\n\n\t\tif (promptConfig.acceptDefault) {\n\t\t\treturn acceptDefault<T>(promptConfig, renderers, initialValues as T);\n\t\t}\n\n\t\tprompt = new MultiSelectPrompt({\n\t\t\t...promptConfig,\n\t\t\toptions: promptConfig.options,\n\t\t\tinitialValues: initialValues,\n\t\t\trender() {\n\t\t\t\treturn dispatchRender(this, prompt);\n\t\t\t},\n\t\t});\n\t} else if (promptConfig.type === \"list\") {\n\t\tconst initialValue = String(promptConfig.defaultValue);\n\n\t\tif (promptConfig.acceptDefault) {\n\t\t\treturn acceptDefault<T>(promptConfig, renderers, initialValue as T);\n\t\t}\n\n\t\tprompt = new SelectRefreshablePrompt({\n\t\t\t...promptConfig,\n\t\t\tonRefresh:\n\t\t\t\tpromptConfig.onRefresh ?? (() => Promise.resolve(promptConfig.options)),\n\t\t\tinitialValue,\n\t\t\trender() {\n\t\t\t\treturn dispatchRender(this, prompt);\n\t\t\t},\n\t\t});\n\t} else {\n\t\tconst initialValue =\n\t\t\tpromptConfig.initialValue ?? String(promptConfig.defaultValue ?? \"\");\n\n\t\tif (promptConfig.acceptDefault) {\n\t\t\treturn acceptDefault<T>(promptConfig, renderers, initialValue as T);\n\t\t}\n\n\t\tprompt = new TextPrompt({\n\t\t\t...promptConfig,\n\t\t\tinitialValue: promptConfig.initialValue,\n\t\t\tdefaultValue: String(promptConfig.defaultValue ?? \"\"),\n\t\t\trender() {\n\t\t\t\treturn dispatchRender(this, prompt);\n\t\t\t},\n\t\t});\n\t}\n\n\tconst input = (await prompt.prompt()) as T;\n\n\tif (isCancel(input)) {\n\t\tif (promptConfig.throwOnError) {\n\t\t\tthrow new CancelError(\"Operation cancelled\");\n\t\t} else {\n\t\t\tcancel(\"Operation cancelled\");\n\t\t\tprocess.exit(0);\n\t\t}\n\t}\n\n\treturn input;\n};\n\ntype Renderer = (\n\tprops: {\n\t\tstate?: string;\n\t\terror?: string;\n\t\tcursor?: number;\n\t\tvalue: Arg;\n\t},\n\tprompt: Prompt\n) => string[];\n\nconst renderSubmit = (config: PromptConfig, value: string) => {\n\tconst { question, label } = config;\n\n\tif (config.type !== \"confirm\" && !value) {\n\t\treturn [`${leftT} ${question} ${dim(\"(skipped)\")}`, `${grayBar}`];\n\t}\n\n\tconst content =\n\t\tconfig.type === \"confirm\"\n\t\t\t? `${grayBar} ${brandColor(value)} ${dim(label)}`\n\t\t\t: `${grayBar} ${brandColor(label)} ${dim(value)}`;\n\n\treturn [`${leftT} ${question}`, content, `${grayBar}`];\n};\n\nexport const getRenderers = (config: PromptConfig) => {\n\tswitch (config.type) {\n\t\tcase \"select\":\n\t\t\treturn getSelectRenderers(config);\n\t\tcase \"confirm\":\n\t\t\treturn getConfirmRenderers(config);\n\t\tcase \"text\":\n\t\t\treturn getTextRenderers(config);\n\t\tcase \"multiselect\":\n\t\t\treturn getSelectRenderers(config);\n\t\tcase \"list\":\n\t\t\treturn getSelectListRenderers(config);\n\t}\n};\n\nconst getTextRenderers = (config: TextPromptConfig) => {\n\tconst { question } = config;\n\tconst helpText = config.helpText ?? \"\";\n\tconst format = config.format ?? ((val: Arg) => String(val));\n\tconst defaultValue = config.defaultValue?.toString() ?? \"\";\n\tconst activeRenderer = (props: RenderProps) => {\n\t\tconst { valueWithCursor } = props as TextPrompt;\n\t\treturn [\n\t\t\t`${blCorner} ${bold(question)} ${dim(helpText)}`,\n\t\t\t`${space(2)}${format(valueWithCursor || dim(defaultValue))}`,\n\t\t\t``, // extra line for readability\n\t\t];\n\t};\n\n\treturn {\n\t\tinitial: () => [\n\t\t\t`${blCorner} ${bold(question)} ${dim(helpText)}`,\n\t\t\t`${space(2)}${gray(format(defaultValue))}`,\n\t\t\t``, // extra line for readability\n\t\t],\n\t\tactive: activeRenderer,\n\t\terror: ({ value, error }: { value: Arg; error: string }) => [\n\t\t\t`${leftT} ${status.error} ${dim(error)}`,\n\t\t\t`${grayBar}`,\n\t\t\t`${blCorner} ${question} ${dim(helpText)}`,\n\t\t\t`${space(2)}${format(value)}`,\n\t\t\t``, // extra line for readability\n\t\t],\n\t\tsubmit: ({ value }: { value: Arg }) =>\n\t\t\trenderSubmit(config, format(value ?? \"\")),\n\t\tcancel: activeRenderer,\n\t};\n};\n\nconst getSelectRenderers = (\n\tconfig: SelectPromptConfig | MultiSelectPromptConfig\n) => {\n\tconst { options, question, helpText: _helpText } = config;\n\tconst helpText = _helpText ?? \"\";\n\tconst maxItemsPerPage = config.maxItemsPerPage ?? 32;\n\n\tconst defaultRenderer: Renderer = ({ cursor = 0, value }) => {\n\t\tconst renderOption = (opt: Option, i: number) => {\n\t\t\tconst { label: optionLabel, value: optionValue } = opt;\n\t\t\tconst active = i === cursor;\n\t\t\tconst isInListOfValues =\n\t\t\t\tArray.isArray(value) && value.includes(optionValue);\n\t\t\tconst color = isInListOfValues || active ? blue : white;\n\t\t\tconst text = active ? color.underline(optionLabel) : color(optionLabel);\n\t\t\tconst sublabel = opt.sublabel ? color.grey(opt.sublabel) : \"\";\n\n\t\t\tconst indicator =\n\t\t\t\tisInListOfValues || (active && !Array.isArray(value))\n\t\t\t\t\t? color(opt.activeIcon ?? shapes.radioActive)\n\t\t\t\t\t: color(opt.inactiveIcon ?? shapes.radioInactive);\n\n\t\t\treturn `${space(2)}${indicator} ${text} ${sublabel}`;\n\t\t};\n\n\t\tconst renderOptionCondition = (_: unknown, i: number): boolean => {\n\t\t\tif (options.length <= maxItemsPerPage) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (i < cursor) {\n\t\t\t\treturn options.length - i <= maxItemsPerPage;\n\t\t\t}\n\n\t\t\treturn cursor + maxItemsPerPage > i;\n\t\t};\n\n\t\tconst visibleOptions = options.filter((o) => !o.hidden);\n\t\tconst activeOption = visibleOptions.at(cursor);\n\t\tconst lines = [\n\t\t\t`${blCorner} ${bold(question)} ${dim(helpText)}`,\n\t\t\t`${\n\t\t\t\tcursor > 0 && options.length > maxItemsPerPage\n\t\t\t\t\t? `${space(2)}${dim(\"...\")}\\n`\n\t\t\t\t\t: \"\"\n\t\t\t}${visibleOptions\n\t\t\t\t.map(renderOption)\n\t\t\t\t.filter(renderOptionCondition)\n\t\t\t\t.join(`\\n`)}${\n\t\t\t\tcursor + maxItemsPerPage < options.length &&\n\t\t\t\toptions.length > maxItemsPerPage\n\t\t\t\t\t? `\\n${space(2)}${dim(\"...\")}`\n\t\t\t\t\t: \"\"\n\t\t\t}`,\n\t\t\t``, // extra line for readability\n\t\t];\n\n\t\tif (activeOption?.description) {\n\t\t\t// To wrap the text by words instead of characters\n\t\t\tconst wordSegmenter = new Intl.Segmenter(\"en\", { granularity: \"word\" });\n\t\t\tconst padding = space(2);\n\t\t\tconst availableWidth =\n\t\t\t\tprocess.stdout.columns - stripAnsi(padding).length * 2;\n\n\t\t\t// The description cannot have any ANSI code\n\t\t\t// As the segmenter will split the code to several segments\n\t\t\tconst description = stripAnsi(activeOption.description);\n\t\t\tconst descriptionLines: string[] = [];\n\t\t\tlet descriptionLineNumber = 0;\n\n\t\t\tfor (const data of wordSegmenter.segment(description)) {\n\t\t\t\tlet line = descriptionLines[descriptionLineNumber] ?? \"\";\n\n\t\t\t\tconst currentLineWidth = line.length;\n\t\t\t\tconst segmentSize = data.segment.length;\n\n\t\t\t\tif (currentLineWidth + segmentSize > availableWidth) {\n\t\t\t\t\tdescriptionLineNumber++;\n\t\t\t\t\tline = \"\";\n\n\t\t\t\t\t// To avoid starting a new line with a space\n\t\t\t\t\tif (data.segment.match(/^\\s+$/)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tdescriptionLines[descriptionLineNumber] = line + data.segment;\n\t\t\t}\n\n\t\t\tlines.push(\n\t\t\t\tdim(\n\t\t\t\t\tdescriptionLines.map((line) => padding + line + padding).join(\"\\n\")\n\t\t\t\t),\n\t\t\t\t``\n\t\t\t);\n\t\t}\n\n\t\treturn lines;\n\t};\n\n\treturn {\n\t\tinitial: defaultRenderer,\n\t\tactive: defaultRenderer,\n\t\tconfirm: defaultRenderer,\n\t\terror: (opts: { value: Arg; error: string }, prompt: Prompt) => {\n\t\t\treturn [\n\t\t\t\t`${leftT} ${status.error} ${dim(opts.error)}`,\n\t\t\t\t`${grayBar}`,\n\t\t\t\t...defaultRenderer(opts, prompt),\n\t\t\t];\n\t\t},\n\t\tsubmit: ({ value }: { value: Arg }) => {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\treturn renderSubmit(\n\t\t\t\t\tconfig,\n\t\t\t\t\toptions\n\t\t\t\t\t\t.filter((o) => value.includes(o.value))\n\t\t\t\t\t\t.map((o) => o.label)\n\t\t\t\t\t\t.join(\", \")\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn renderSubmit(\n\t\t\t\tconfig,\n\t\t\t\toptions.find((o) => o.value === value)?.label as string\n\t\t\t);\n\t\t},\n\t\tcancel: defaultRenderer,\n\t};\n};\n\nconst getSelectListRenderers = (config: ListPromptConfig) => {\n\tconst { question, helpText: _helpText } = config;\n\tlet options = config.options;\n\tconst helpText = _helpText ?? \"\";\n\tconst { rows } = stdout;\n\tconst defaultRenderer: Renderer = ({ cursor, value }, prompt: Prompt) => {\n\t\tif (prompt instanceof SelectRefreshablePrompt) {\n\t\t\toptions = prompt.options;\n\t\t}\n\n\t\tcursor = cursor ?? 0;\n\t\tlet smallCursor = 0;\n\t\tconst renderOption = (opt: OptionWithDetails, i: number) => {\n\t\t\tconst { label: optionLabel, value: optionValueAny } = opt;\n\t\t\tconst optionValue = optionValueAny.toString() as string;\n\t\t\tconst active = i === smallCursor;\n\t\t\tconst isInListOfValues =\n\t\t\t\tArray.isArray(value) && value.includes(optionValue);\n\t\t\tconst color = isInListOfValues || active ? blue : white;\n\t\t\tconst text = active ? color.underline(optionLabel) : color(optionLabel);\n\n\t\t\tconst indicator =\n\t\t\t\tisInListOfValues || (active && !Array.isArray(value))\n\t\t\t\t\t? color(shapes.radioActive)\n\t\t\t\t\t: color(shapes.radioInactive);\n\n\t\t\tconst indicatorMargin = 2;\n\t\t\tconst detailBulletpointMargin = indicatorMargin + 4;\n\t\t\treturn [\n\t\t\t\t`${space(indicatorMargin)}${indicator} ${text}`,\n\t\t\t\t...opt.details.map(\n\t\t\t\t\t(detail, j) =>\n\t\t\t\t\t\t`${space(detailBulletpointMargin)}${\n\t\t\t\t\t\t\tj === opt.details.length - 1 ? gray(shapes.corners.bl) : grayBar\n\t\t\t\t\t\t} ${detail}`\n\t\t\t\t),\n\t\t\t];\n\t\t};\n\n\t\t// Create the pages, later on we will choose a \"page\" that the user can\n\t\t// navigate on the view\n\t\tconst pages: OptionWithDetails[][] = [];\n\t\tlet current: { size: number; options: OptionWithDetails[] } = {\n\t\t\tsize: 0,\n\t\t\toptions: [],\n\t\t};\n\t\tconst VERTICAL_MARGIN = 6;\n\t\tfor (const option of options) {\n\t\t\t// If current accumulation of options + title row + option details size\n\t\t\t// is bigger than console rows substracted by a bit of vertical margin,\n\t\t\t// add a new page.\n\t\t\tconst optionHeight = option.details.length + 1;\n\t\t\tif (current.size + optionHeight > rows - VERTICAL_MARGIN) {\n\t\t\t\tpages.push(current.options);\n\t\t\t\tcurrent = { size: optionHeight, options: [option] };\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tcurrent.size += optionHeight;\n\t\t\tcurrent.options.push(option);\n\t\t}\n\n\t\t// add the last current as the last page\n\t\tif (current.size !== 0) {\n\t\t\tpages.push(current.options);\n\t\t}\n\n\t\t// choose a page by finding the page that the current cursor is in\n\t\tlet isFirstPage = true;\n\t\tlet isLastPage = false;\n\t\tlet page: OptionWithDetails[] = [];\n\t\tlet len = 0;\n\t\tfor (let i = 0; i < pages.length; i++) {\n\t\t\tconst pageIter = pages[i];\n\t\t\tif (cursor >= len && pageIter.length + len > cursor) {\n\t\t\t\tisFirstPage = i === 0;\n\t\t\t\tisLastPage = i === pages.length - 1;\n\t\t\t\tpage = pageIter;\n\t\t\t\tsmallCursor = cursor - len;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tlen += pageIter.length;\n\t\t}\n\n\t\treturn [\n\t\t\t`${blCorner} ${bold(question)} ${dim(helpText)}`,\n\t\t\t...(!isFirstPage ? [`${space(2)}${dim(\"...\")}`] : []),\n\t\t\t...page.map(renderOption).reduce((prev, now) => [...prev, ...now], []),\n\t\t\t...(!isLastPage ? [`${space(2)}${dim(\"...\")}`] : []),\n\t\t];\n\t};\n\n\treturn {\n\t\tinitial: defaultRenderer,\n\t\tactive: defaultRenderer,\n\t\tconfirm: defaultRenderer,\n\t\terror: (opts: { value: Arg; error: string }, prompt: Prompt) => {\n\t\t\treturn [\n\t\t\t\t`${leftT} ${status.error} ${dim(opts.error)}`,\n\t\t\t\t`${grayBar}`,\n\t\t\t\t...defaultRenderer(opts, prompt),\n\t\t\t];\n\t\t},\n\t\tsubmit: ({ value }: { value: Arg }) => {\n\t\t\tif (Array.isArray(value)) {\n\t\t\t\treturn renderSubmit(\n\t\t\t\t\tconfig,\n\t\t\t\t\toptions\n\t\t\t\t\t\t.filter((o) => value.includes(o.value))\n\t\t\t\t\t\t.map((o) => o.value)\n\t\t\t\t\t\t.join(\", \")\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn renderSubmit(\n\t\t\t\tconfig,\n\t\t\t\toptions.find((o) => o.value === value)?.value as string\n\t\t\t);\n\t\t},\n\t\tcancel: defaultRenderer,\n\t};\n};\n\nconst getConfirmRenderers = (config: ConfirmPromptConfig) => {\n\tconst { activeText, inactiveText, question, helpText: _helpText } = config;\n\tconst helpText = _helpText ?? \"\";\n\n\tconst active = activeText || \"Yes\";\n\tconst inactive = inactiveText || \"No\";\n\n\tconst defaultRenderer: Renderer = ({ value }) => {\n\t\tconst yesColor = value ? blue.underline : dim;\n\t\tconst noColor = value ? dim : blue.underline;\n\t\treturn [\n\t\t\t`${blCorner} ${bold(question)} ${dim(helpText)}`,\n\t\t\t`${space(2)}${yesColor(active)} / ${noColor(inactive)}`,\n\t\t];\n\t};\n\n\treturn {\n\t\tinitial: defaultRenderer,\n\t\tactive: defaultRenderer,\n\t\tconfirm: defaultRenderer,\n\t\terror: defaultRenderer,\n\t\tsubmit: ({ value }: { value: Arg }) =>\n\t\t\trenderSubmit(config, value ? \"yes\" : \"no\"),\n\t\tcancel: defaultRenderer,\n\t};\n};\n\nexport type SpinnerStyle = keyof typeof spinnerFrames;\n\nexport const spinnerFrames = {\n\tclockwise: [\"┤\", \"┘\", \"┴\", \"└\", \"├\", \"┌\", \"┬\", \"┐\"],\n\tvertical: [\"▁\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"▆\", \"▅\", \"▄\", \"▃\"],\n};\n\nconst ellipsisFrames = [\"\", \".\", \"..\", \"...\", \" ..\", \" .\", \"\"];\n\nexport const spinner = (\n\tframes: string[] = spinnerFrames.clockwise,\n\tcolor: typeof brandColor = brandColor\n) => {\n\t// Alternative animations we considered. Keeping around in case we\n\t// introduce different animations for different use cases.\n\t// const frames = [\"▁\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"▆\", \"▅\", \"▄\", \"▃\"];\n\t// const frames = [\"■\", \"□\", \"▪\", \"▫\"];\n\t// const frames = [\"✶\", \"✸\", \"✹\", \"✺\", \"✹\", \"✷\"];\n\t// const frames = [\"◜\", \"◠\", \"◝\", \"◞\", \"◡\", \"◟\"];\n\t// const frames = [\"◐\", \"◓\", \"◑\", \"◒\"];\n\t// const frames = [\"㊂\", \"㊀\", \"㊁\"];\n\n\tconst frameRate = 120;\n\tlet loop: ReturnType<typeof setTimeout> | null = null;\n\tlet startMsg: string;\n\tlet currentMsg: string;\n\n\tfunction clearLoop() {\n\t\tif (loop) {\n\t\t\tclearTimeout(loop);\n\t\t}\n\t\tloop = null;\n\t}\n\n\treturn {\n\t\tstart(msg: string, helpText?: string) {\n\t\t\thelpText ||= ``;\n\t\t\tcurrentMsg = msg;\n\t\t\tstartMsg = currentMsg;\n\t\t\tif (helpText !== undefined && helpText.length > 0) {\n\t\t\t\tstartMsg += ` ${dim(helpText)}`;\n\t\t\t}\n\n\t\t\tif (isInteractive()) {\n\t\t\t\tlet index = 0;\n\n\t\t\t\tclearLoop();\n\t\t\t\tloop = setInterval(() => {\n\t\t\t\t\tindex++;\n\t\t\t\t\tconst spinnerFrame = frames[index % frames.length];\n\t\t\t\t\tconst ellipsisFrame = ellipsisFrames[index % ellipsisFrames.length];\n\n\t\t\t\t\tif (msg) {\n\t\t\t\t\t\tlogUpdate(`${color(spinnerFrame)} ${currentMsg} ${ellipsisFrame}`);\n\t\t\t\t\t}\n\t\t\t\t}, frameRate);\n\t\t\t} else {\n\t\t\t\tlogRaw(`${leftT} ${startMsg}`);\n\t\t\t}\n\t\t},\n\t\tupdate(msg: string) {\n\t\t\tcurrentMsg = msg;\n\t\t},\n\t\tstop(msg?: string) {\n\t\t\tif (isInteractive()) {\n\t\t\t\t// Write the final message and clear the loop\n\t\t\t\tlogUpdate.clear();\n\t\t\t\tif (msg) {\n\t\t\t\t\tlogUpdate(`${leftT} ${startMsg}\\n${grayBar} ${msg}`);\n\t\t\t\t\tlogUpdate.done();\n\t\t\t\t\tnewline();\n\t\t\t\t}\n\t\t\t\tclearLoop();\n\t\t\t} else {\n\t\t\t\tif (msg !== undefined) {\n\t\t\t\t\tlogRaw(`${grayBar} ${msg}`);\n\t\t\t\t}\n\t\t\t\tnewline();\n\t\t\t}\n\t\t},\n\t};\n};\n\ntype FactoryOrValue<T> = T | (() => T);\nconst unwrapFactory = <T>(input: FactoryOrValue<T>): T => {\n\tconst output = typeof input === \"function\" ? (input as () => T)() : input;\n\treturn output;\n};\nexport const spinnerWhile = async <T>(opts: {\n\tpromise: FactoryOrValue<Promise<T>>;\n\tstartMessage: FactoryOrValue<string>;\n\tendMessage?: FactoryOrValue<string>;\n\tspinner?: ReturnType<typeof spinner>;\n}): Promise<T> => {\n\tconst s = opts.spinner ?? spinner();\n\n\ts.start(unwrapFactory(opts.startMessage));\n\n\ttry {\n\t\tconst result = await unwrapFactory(opts.promise);\n\n\t\treturn result;\n\t} finally {\n\t\ts.stop(unwrapFactory(opts.endMessage));\n\t}\n};\n\n/**\n * Test whether the process is \"interactive\".\n */\nexport function isInteractive(): boolean {\n\ttry {\n\t\treturn Boolean(process.stdin.isTTY && process.stdout.isTTY);\n\t} catch {\n\t\treturn false;\n\t}\n}\n"],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ //#region packages.d.ts
2
+ type InstallConfig = {
3
+ startText?: string;
4
+ doneText?: string;
5
+ dev?: boolean;
6
+ force?: boolean;
7
+ isWorkspaceRoot?: boolean;
8
+ };
9
+ /**
10
+ * Install a list of packages to the local project directory and add it to `package.json`
11
+ *
12
+ * @param packageManager - The package manager to use for installation
13
+ * @param packages - An array of package specifiers to be installed
14
+ * @param config.dev - Add packages as `devDependencies`
15
+ * @param config.startText - Spinner start text
16
+ * @param config.doneText - Spinner done text
17
+ * @param config.force - Whether to install with `--force` or not
18
+ */
19
+ declare const installPackages: (packageManager: "npm" | "pnpm" | "yarn" | "bun", packages: string[], config?: InstallConfig) => Promise<void>;
20
+ /**
21
+ * Installs the latest version of wrangler in the project directory if it isn't already.
22
+ */
23
+ declare function installWrangler(packageManager: "npm" | "pnpm" | "yarn" | "bun", isWorkspaceRoot: boolean): Promise<void>;
24
+ //#endregion
25
+ export { installPackages, installWrangler };
26
+ //# sourceMappingURL=packages.d.mts.map
@@ -0,0 +1,124 @@
1
+ import { brandColor, dim } from "./colors.mjs";
2
+ import { runCommand } from "./command.mjs";
3
+ import { parsePackageJSON, readFileSync } from "@cloudflare/workers-utils";
4
+ import path from "node:path";
5
+ import assert from "node:assert";
6
+ import { writeFile } from "node:fs/promises";
7
+
8
+ //#region packages.ts
9
+ /**
10
+ * Install a list of packages to the local project directory and add it to `package.json`
11
+ *
12
+ * @param packageManager - The package manager to use for installation
13
+ * @param packages - An array of package specifiers to be installed
14
+ * @param config.dev - Add packages as `devDependencies`
15
+ * @param config.startText - Spinner start text
16
+ * @param config.doneText - Spinner done text
17
+ * @param config.force - Whether to install with `--force` or not
18
+ */
19
+ const installPackages = async (packageManager, packages, config = {}) => {
20
+ const { force, dev, startText, doneText } = config;
21
+ const isWorkspaceRoot = config.isWorkspaceRoot ?? false;
22
+ if (packages.length === 0) {
23
+ let cmd$1;
24
+ switch (packageManager) {
25
+ case "yarn": break;
26
+ case "npm":
27
+ case "pnpm":
28
+ default:
29
+ cmd$1 = "install";
30
+ break;
31
+ }
32
+ await runCommand([
33
+ packageManager,
34
+ ...cmd$1 ? [cmd$1] : [],
35
+ ...packages,
36
+ ...packageManager === "pnpm" ? ["--no-frozen-lockfile"] : [],
37
+ ...force === true ? ["--force"] : [],
38
+ ...getWorkspaceInstallRootFlag(packageManager, isWorkspaceRoot)
39
+ ], {
40
+ cwd: process.cwd(),
41
+ startText,
42
+ doneText,
43
+ silent: true
44
+ });
45
+ return;
46
+ }
47
+ let saveFlag;
48
+ let cmd;
49
+ switch (packageManager) {
50
+ case "yarn":
51
+ cmd = "add";
52
+ saveFlag = dev ? "-D" : "";
53
+ break;
54
+ case "bun":
55
+ cmd = "add";
56
+ saveFlag = dev ? "-d" : "";
57
+ break;
58
+ case "npm":
59
+ case "pnpm":
60
+ default:
61
+ cmd = "install";
62
+ saveFlag = dev ? "--save-dev" : "";
63
+ break;
64
+ }
65
+ await runCommand([
66
+ packageManager,
67
+ cmd,
68
+ ...saveFlag ? [saveFlag] : [],
69
+ ...packages,
70
+ ...force === true ? ["--force"] : [],
71
+ ...getWorkspaceInstallRootFlag(packageManager, isWorkspaceRoot)
72
+ ], {
73
+ startText,
74
+ doneText,
75
+ silent: true
76
+ });
77
+ if (packageManager === "npm") {
78
+ const pkgJsonPath = path.join(process.cwd(), "package.json");
79
+ const pkgJson = parsePackageJSON(readFileSync(pkgJsonPath), pkgJsonPath);
80
+ const deps = config.dev ? pkgJson.devDependencies : pkgJson.dependencies;
81
+ assert(deps, "dependencies should be defined");
82
+ for (const pkg of packages) {
83
+ const versionMarker = pkg.lastIndexOf("@");
84
+ if (versionMarker > 0) {
85
+ const pkgName = pkg.slice(0, versionMarker);
86
+ const pkgVersion = pkg.slice(versionMarker + 1);
87
+ if (pkgVersion !== "latest") deps[pkgName] = pkgVersion;
88
+ }
89
+ }
90
+ await writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
91
+ }
92
+ };
93
+ /**
94
+ * Returns the potential flag(/s) that need to be added to a package manager's install command when it is
95
+ * run at the root of a workspace.
96
+ *
97
+ * @param packageManager The type of package manager
98
+ * @param isWorkspaceRoot Flag indicating whether the install command is being run at the root of a workspace
99
+ * @returns an array containing the flag(/s) to use, or an empty array if not supported or not running in the workspace root.
100
+ */
101
+ function getWorkspaceInstallRootFlag(packageManager, isWorkspaceRoot) {
102
+ if (!isWorkspaceRoot) return [];
103
+ switch (packageManager) {
104
+ case "pnpm": return ["--workspace-root"];
105
+ case "yarn": return ["-W"];
106
+ case "npm":
107
+ case "bun": return [];
108
+ }
109
+ }
110
+ /**
111
+ * Installs the latest version of wrangler in the project directory if it isn't already.
112
+ */
113
+ async function installWrangler(packageManager, isWorkspaceRoot) {
114
+ await installPackages(packageManager, [`wrangler@latest`], {
115
+ dev: true,
116
+ isWorkspaceRoot,
117
+ startText: `Installing wrangler ${dim("A command line tool for building Cloudflare Workers")}`,
118
+ doneText: `${brandColor("installed")} ${dim(`via \`${packageManager} install wrangler --save-dev\``)}`
119
+ });
120
+ }
121
+
122
+ //#endregion
123
+ export { installPackages, installWrangler };
124
+ //# sourceMappingURL=packages.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packages.mjs","names":["cmd"],"sources":["../packages.ts"],"sourcesContent":["import assert from \"node:assert\";\nimport { writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { parsePackageJSON, readFileSync } from \"@cloudflare/workers-utils\";\nimport { brandColor, dim } from \"./colors\";\nimport { runCommand } from \"./command\";\n\ntype InstallConfig = {\n\tstartText?: string;\n\tdoneText?: string;\n\tdev?: boolean;\n\tforce?: boolean;\n\tisWorkspaceRoot?: boolean;\n};\n\n/**\n * Install a list of packages to the local project directory and add it to `package.json`\n *\n * @param packageManager - The package manager to use for installation\n * @param packages - An array of package specifiers to be installed\n * @param config.dev - Add packages as `devDependencies`\n * @param config.startText - Spinner start text\n * @param config.doneText - Spinner done text\n * @param config.force - Whether to install with `--force` or not\n */\nexport const installPackages = async (\n\tpackageManager: \"npm\" | \"pnpm\" | \"yarn\" | \"bun\",\n\tpackages: string[],\n\tconfig: InstallConfig = {}\n) => {\n\tconst { force, dev, startText, doneText } = config;\n\tconst isWorkspaceRoot = config.isWorkspaceRoot ?? false;\n\n\tif (packages.length === 0) {\n\t\tlet cmd;\n\t\tswitch (packageManager) {\n\t\t\tcase \"yarn\":\n\t\t\t\tbreak;\n\t\t\tcase \"npm\":\n\t\t\tcase \"pnpm\":\n\t\t\tdefault:\n\t\t\t\tcmd = \"install\";\n\t\t\t\tbreak;\n\t\t}\n\n\t\tawait runCommand(\n\t\t\t[\n\t\t\t\tpackageManager,\n\t\t\t\t...(cmd ? [cmd] : []),\n\t\t\t\t...packages,\n\t\t\t\t...(packageManager === \"pnpm\" ? [\"--no-frozen-lockfile\"] : []),\n\t\t\t\t...(force === true ? [\"--force\"] : []),\n\t\t\t\t...getWorkspaceInstallRootFlag(packageManager, isWorkspaceRoot),\n\t\t\t],\n\t\t\t{\n\t\t\t\tcwd: process.cwd(),\n\t\t\t\tstartText,\n\t\t\t\tdoneText,\n\t\t\t\tsilent: true,\n\t\t\t}\n\t\t);\n\t\treturn;\n\t}\n\n\tlet saveFlag;\n\tlet cmd;\n\tswitch (packageManager) {\n\t\tcase \"yarn\":\n\t\t\tcmd = \"add\";\n\t\t\tsaveFlag = dev ? \"-D\" : \"\";\n\t\t\tbreak;\n\t\tcase \"bun\":\n\t\t\tcmd = \"add\";\n\t\t\tsaveFlag = dev ? \"-d\" : \"\";\n\t\t\tbreak;\n\t\tcase \"npm\":\n\t\tcase \"pnpm\":\n\t\tdefault:\n\t\t\tcmd = \"install\";\n\t\t\tsaveFlag = dev ? \"--save-dev\" : \"\";\n\t\t\tbreak;\n\t}\n\tawait runCommand(\n\t\t[\n\t\t\tpackageManager,\n\t\t\tcmd,\n\t\t\t...(saveFlag ? [saveFlag] : []),\n\t\t\t...packages,\n\t\t\t...(force === true ? [\"--force\"] : []),\n\t\t\t...getWorkspaceInstallRootFlag(packageManager, isWorkspaceRoot),\n\t\t],\n\t\t{\n\t\t\tstartText,\n\t\t\tdoneText,\n\t\t\tsilent: true,\n\t\t}\n\t);\n\n\tif (packageManager === \"npm\") {\n\t\t// Npm install will update the package.json with a caret-range rather than the exact version/range we asked for.\n\t\t// We can't use `npm install --save-exact` because that always pins to an exact version, and we want to allow ranges too.\n\t\t// So let's just fix that up now by rewriting the package.json.\n\t\tconst pkgJsonPath = path.join(process.cwd(), \"package.json\");\n\t\tconst pkgJson = parsePackageJSON(readFileSync(pkgJsonPath), pkgJsonPath);\n\t\tconst deps = config.dev ? pkgJson.devDependencies : pkgJson.dependencies;\n\t\tassert(deps, \"dependencies should be defined\");\n\t\tfor (const pkg of packages) {\n\t\t\tconst versionMarker = pkg.lastIndexOf(\"@\");\n\t\t\tif (versionMarker > 0) {\n\t\t\t\t// (if versionMarker was 0 then this would indicate a scoped package with no version)\n\t\t\t\tconst pkgName = pkg.slice(0, versionMarker);\n\t\t\t\tconst pkgVersion = pkg.slice(versionMarker + 1);\n\t\t\t\tif (pkgVersion !== \"latest\") {\n\t\t\t\t\tdeps[pkgName] = pkgVersion;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tawait writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2));\n\t}\n};\n\n/**\n * Returns the potential flag(/s) that need to be added to a package manager's install command when it is\n * run at the root of a workspace.\n *\n * @param packageManager The type of package manager\n * @param isWorkspaceRoot Flag indicating whether the install command is being run at the root of a workspace\n * @returns an array containing the flag(/s) to use, or an empty array if not supported or not running in the workspace root.\n */\nfunction getWorkspaceInstallRootFlag(\n\tpackageManager: \"npm\" | \"pnpm\" | \"yarn\" | \"bun\",\n\tisWorkspaceRoot: boolean\n): string[] {\n\tif (!isWorkspaceRoot) {\n\t\treturn [];\n\t}\n\n\tswitch (packageManager) {\n\t\tcase \"pnpm\":\n\t\t\treturn [\"--workspace-root\"];\n\t\tcase \"yarn\":\n\t\t\treturn [\"-W\"];\n\t\tcase \"npm\":\n\t\tcase \"bun\":\n\t\t\t// npm and bun don't have the workspace check\n\t\t\treturn [];\n\t}\n}\n\n/**\n * Installs the latest version of wrangler in the project directory if it isn't already.\n */\nexport async function installWrangler(\n\tpackageManager: \"npm\" | \"pnpm\" | \"yarn\" | \"bun\",\n\tisWorkspaceRoot: boolean\n) {\n\t// Even if Wrangler is already installed, make sure we install the latest version, as some framework CLIs are pinned to an older version\n\tawait installPackages(packageManager, [`wrangler@latest`], {\n\t\tdev: true,\n\t\tisWorkspaceRoot,\n\t\tstartText: `Installing wrangler ${dim(\n\t\t\t\"A command line tool for building Cloudflare Workers\"\n\t\t)}`,\n\t\tdoneText: `${brandColor(\"installed\")} ${dim(\n\t\t\t`via \\`${packageManager} install wrangler --save-dev\\``\n\t\t)}`,\n\t});\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyBA,MAAa,kBAAkB,OAC9B,gBACA,UACA,SAAwB,EAAE,KACtB;CACJ,MAAM,EAAE,OAAO,KAAK,WAAW,aAAa;CAC5C,MAAM,kBAAkB,OAAO,mBAAmB;AAElD,KAAI,SAAS,WAAW,GAAG;EAC1B,IAAIA;AACJ,UAAQ,gBAAR;GACC,KAAK,OACJ;GACD,KAAK;GACL,KAAK;GACL;AACC,YAAM;AACN;;AAGF,QAAM,WACL;GACC;GACA,GAAIA,QAAM,CAACA,MAAI,GAAG,EAAE;GACpB,GAAG;GACH,GAAI,mBAAmB,SAAS,CAAC,uBAAuB,GAAG,EAAE;GAC7D,GAAI,UAAU,OAAO,CAAC,UAAU,GAAG,EAAE;GACrC,GAAG,4BAA4B,gBAAgB,gBAAgB;GAC/D,EACD;GACC,KAAK,QAAQ,KAAK;GAClB;GACA;GACA,QAAQ;GACR,CACD;AACD;;CAGD,IAAI;CACJ,IAAI;AACJ,SAAQ,gBAAR;EACC,KAAK;AACJ,SAAM;AACN,cAAW,MAAM,OAAO;AACxB;EACD,KAAK;AACJ,SAAM;AACN,cAAW,MAAM,OAAO;AACxB;EACD,KAAK;EACL,KAAK;EACL;AACC,SAAM;AACN,cAAW,MAAM,eAAe;AAChC;;AAEF,OAAM,WACL;EACC;EACA;EACA,GAAI,WAAW,CAAC,SAAS,GAAG,EAAE;EAC9B,GAAG;EACH,GAAI,UAAU,OAAO,CAAC,UAAU,GAAG,EAAE;EACrC,GAAG,4BAA4B,gBAAgB,gBAAgB;EAC/D,EACD;EACC;EACA;EACA,QAAQ;EACR,CACD;AAED,KAAI,mBAAmB,OAAO;EAI7B,MAAM,cAAc,KAAK,KAAK,QAAQ,KAAK,EAAE,eAAe;EAC5D,MAAM,UAAU,iBAAiB,aAAa,YAAY,EAAE,YAAY;EACxE,MAAM,OAAO,OAAO,MAAM,QAAQ,kBAAkB,QAAQ;AAC5D,SAAO,MAAM,iCAAiC;AAC9C,OAAK,MAAM,OAAO,UAAU;GAC3B,MAAM,gBAAgB,IAAI,YAAY,IAAI;AAC1C,OAAI,gBAAgB,GAAG;IAEtB,MAAM,UAAU,IAAI,MAAM,GAAG,cAAc;IAC3C,MAAM,aAAa,IAAI,MAAM,gBAAgB,EAAE;AAC/C,QAAI,eAAe,SAClB,MAAK,WAAW;;;AAInB,QAAM,UAAU,aAAa,KAAK,UAAU,SAAS,MAAM,EAAE,CAAC;;;;;;;;;;;AAYhE,SAAS,4BACR,gBACA,iBACW;AACX,KAAI,CAAC,gBACJ,QAAO,EAAE;AAGV,SAAQ,gBAAR;EACC,KAAK,OACJ,QAAO,CAAC,mBAAmB;EAC5B,KAAK,OACJ,QAAO,CAAC,KAAK;EACd,KAAK;EACL,KAAK,MAEJ,QAAO,EAAE;;;;;;AAOZ,eAAsB,gBACrB,gBACA,iBACC;AAED,OAAM,gBAAgB,gBAAgB,CAAC,kBAAkB,EAAE;EAC1D,KAAK;EACL;EACA,WAAW,uBAAuB,IACjC,sDACA;EACD,UAAU,GAAG,WAAW,YAAY,CAAC,GAAG,IACvC,SAAS,eAAe,gCACxB;EACD,CAAC"}