@shopify/cli-kit 3.93.2 → 3.94.0
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/dist/private/node/analytics.d.ts +2 -0
- package/dist/private/node/analytics.js +10 -1
- package/dist/private/node/analytics.js.map +1 -1
- package/dist/private/node/api/urls.js +19 -5
- package/dist/private/node/api/urls.js.map +1 -1
- package/dist/private/node/content-tokens.js +6 -2
- package/dist/private/node/content-tokens.js.map +1 -1
- package/dist/private/node/session/exchange.js +11 -3
- package/dist/private/node/session/exchange.js.map +1 -1
- package/dist/private/node/session.d.ts +7 -7
- package/dist/private/node/session.js +11 -9
- package/dist/private/node/session.js.map +1 -1
- package/dist/private/node/temp-dir.d.ts +1 -0
- package/dist/private/node/temp-dir.js +8 -0
- package/dist/private/node/temp-dir.js.map +1 -0
- package/dist/private/node/testing/ui.js +4 -3
- package/dist/private/node/testing/ui.js.map +1 -1
- package/dist/private/node/ui/components/AutocompletePrompt.js +5 -4
- package/dist/private/node/ui/components/AutocompletePrompt.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.js +12 -10
- package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.test.js +27 -4
- package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
- package/dist/private/node/ui/components/DangerousConfirmationPrompt.d.ts +2 -0
- package/dist/private/node/ui/components/DangerousConfirmationPrompt.js +12 -7
- package/dist/private/node/ui/components/DangerousConfirmationPrompt.js.map +1 -1
- package/dist/private/node/ui/components/LoadingBar.js +9 -2
- package/dist/private/node/ui/components/LoadingBar.js.map +1 -1
- package/dist/private/node/ui/components/LoadingBar.test.js +49 -88
- package/dist/private/node/ui/components/LoadingBar.test.js.map +1 -1
- package/dist/private/node/ui/components/SelectPrompt.js +4 -4
- package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
- package/dist/private/node/ui/components/SingleTask.js +12 -6
- package/dist/private/node/ui/components/SingleTask.js.map +1 -1
- package/dist/private/node/ui/components/TextPrompt.js +5 -5
- package/dist/private/node/ui/components/TextPrompt.js.map +1 -1
- package/dist/private/node/ui/hooks/use-abort-signal.js +10 -15
- package/dist/private/node/ui/hooks/use-abort-signal.js.map +1 -1
- package/dist/private/node/ui/hooks/use-async-and-unmount.js +11 -5
- package/dist/private/node/ui/hooks/use-async-and-unmount.js.map +1 -1
- package/dist/private/node/ui.d.ts +17 -1
- package/dist/private/node/ui.js +35 -4
- package/dist/private/node/ui.js.map +1 -1
- package/dist/public/common/version.d.ts +1 -1
- package/dist/public/common/version.js +1 -1
- package/dist/public/common/version.js.map +1 -1
- package/dist/public/node/context/local.js +1 -2
- package/dist/public/node/context/local.js.map +1 -1
- package/dist/public/node/error-handler.js +4 -0
- package/dist/public/node/error-handler.js.map +1 -1
- package/dist/public/node/error.js +2 -0
- package/dist/public/node/error.js.map +1 -1
- package/dist/public/node/fs.js +10 -4
- package/dist/public/node/fs.js.map +1 -1
- package/dist/public/node/hooks/postrun.js +9 -10
- package/dist/public/node/hooks/postrun.js.map +1 -1
- package/dist/public/node/metadata.d.ts +1 -1
- package/dist/public/node/metadata.js.map +1 -1
- package/dist/public/node/monorail.d.ts +10 -1
- package/dist/public/node/monorail.js +1 -1
- package/dist/public/node/monorail.js.map +1 -1
- package/dist/public/node/node-package-manager.d.ts +12 -0
- package/dist/public/node/node-package-manager.js +62 -30
- package/dist/public/node/node-package-manager.js.map +1 -1
- package/dist/public/node/path.d.ts +8 -0
- package/dist/public/node/path.js +17 -2
- package/dist/public/node/path.js.map +1 -1
- package/dist/public/node/system.d.ts +6 -0
- package/dist/public/node/system.js +9 -0
- package/dist/public/node/system.js.map +1 -1
- package/dist/public/node/tcp.js +32 -8
- package/dist/public/node/tcp.js.map +1 -1
- package/dist/public/node/ui.d.ts +1 -3
- package/dist/public/node/ui.js +16 -14
- package/dist/public/node/ui.js.map +1 -1
- package/dist/public/node/upgrade.d.ts +7 -2
- package/dist/public/node/upgrade.js +20 -7
- package/dist/public/node/upgrade.js.map +1 -1
- package/dist/public/node/vendor/dev_server/dev-server-2024.js +1 -1
- package/dist/public/node/vendor/dev_server/dev-server-2024.js.map +1 -1
- package/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultMeterProvider.js +1 -2
- package/dist/public/node/vendor/otel-js/service/DefaultOtelService/DefaultMeterProvider.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../../src/private/node/testing/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import {Stdout} from '../ui.js'\nimport {ReactElement} from 'react'\nimport {render as inkRender} from 'ink'\n\nimport {EventEmitter} from 'events'\n\nclass Stderr extends EventEmitter {\n readonly frames: string[] = []\n private _lastFrame?: string\n\n write = (frame: string) => {\n this.frames.push(frame)\n this._lastFrame = frame\n }\n\n lastFrame = () => this._lastFrame\n}\n\nexport class Stdin extends EventEmitter {\n isTTY: boolean\n data: string | null = null\n\n constructor(options: {isTTY?: boolean} = {}) {\n super()\n this.isTTY = options.isTTY ?? true\n }\n\n write = (data: string) => {\n this.data = data\n this.emit('readable')\n }\n\n setEncoding() {}\n setRawMode() {}\n ref() {}\n unref() {}\n read: () => string | null = () => {\n const data = this.data\n\n this.data = null\n\n return data\n }\n}\n\ninterface Instance {\n rerender: (tree: ReactElement) => void\n unmount: () => void\n cleanup: () => void\n stdout: Stdout\n stderr: Stderr\n stdin: Stdin\n frames: string[]\n lastFrame: () => string | undefined\n waitUntilExit: () => TrackedPromise<void>\n}\n\ninterface RenderOptions {\n stdout?: EventEmitter\n stderr?: EventEmitter\n stdin?: EventEmitter\n}\n\nexport const render = (tree: ReactElement, options: RenderOptions = {}): Instance => {\n const stdout = new Stdout({columns: 100})\n const stderr = new Stderr()\n const stdin = new Stdin()\n\n const instance = inkRender(tree, {\n stdout: options.stdout ?? (stdout as any),\n\n stderr: options.stderr ?? (stderr as any),\n\n stdin: options.stdin ?? (stdin as any),\n debug: true,\n exitOnCtrlC: false,\n patchConsole: false,\n })\n\n return {\n rerender: instance.rerender,\n unmount: instance.unmount,\n cleanup: instance.cleanup,\n waitUntilExit: () => trackPromise(instance.waitUntilExit().then(() => {})),\n stdout,\n stderr,\n stdin,\n frames: stdout.frames,\n lastFrame: stdout.lastFrame,\n }\n}\n\n/**\n * Wait for the component to be ready to accept input.\n */\nexport function waitForInputsToBeReady() {\n return new Promise((resolve) => setTimeout(resolve, 10))\n}\n\n/**\n * Wait for the last frame to change to anything.\n */\nexport async function waitForChange(func: () => void, getChangingValue: () => string | number | undefined) {\n const initialValue = getChangingValue()\n\n func()\n\n while (getChangingValue() === initialValue) {\n // Yield via setImmediate so React 19's scheduler (which also uses\n // setImmediate in Node.js) can flush batched renders, then yield\n // via setTimeout(0) to let any follow-up microtasks settle.\n // eslint-disable-next-line no-await-in-loop\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)))\n }\n}\n\nexport async function waitFor(func: () => void, condition: () => boolean) {\n func()\n\n while (!condition()) {\n // eslint-disable-next-line no-await-in-loop\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)))\n }\n}\n\n/**\n * Wait for the last frame to contain specific text.\n */\nexport function waitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n func: () => void = () => {},\n) {\n return waitFor(\n () => func(),\n\n () => renderInstance.lastFrame()!.includes(content),\n )\n}\n\n/**\n * Send input and wait for the last frame to change.\n *\n * Useful when you want to send some input and wait for anything to change in the interface.\n * If you need to wait for a specific change instead, you can use sendInputAndWaitForContent.\n */\nexport async function sendInputAndWaitForChange(renderInstance: ReturnType<typeof render>, ...inputs: string[]) {\n await waitForChange(() => inputs.forEach((input) => renderInstance.stdin.write(input)), renderInstance.lastFrame)\n // Yield via setImmediate → setTimeout(0) so React 19's scheduler can flush\n // effects (e.g. re-register useInput handlers with up-to-date closures)\n // before subsequent input is sent.\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)))\n}\n\n/** Send input and wait a number of milliseconds.\n *\n * Useful if you know some what will happen after input will take a certain amount of time\n * and it will not cause any visible change so you can't use sendInputAndWaitForChange.\n * This function can also be used if you want to test that nothing changes after some input has been sent.\n */\nexport async function sendInputAndWait(\n renderInstance: ReturnType<typeof render>,\n waitTime: number,\n ...inputs: string[]\n) {\n inputs.forEach((input) => renderInstance.stdin.write(input))\n await new Promise((resolve) => setTimeout(resolve, waitTime))\n}\n\n/**\n * Send input and wait for the last frame to contain specific text.\n *\n * Useful when you want to send some input and wait for a specific change to happen.\n * If you need to wait for any change instead, you can use sendInputAndWaitForChange.\n */\nexport async function sendInputAndWaitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n ...inputs: string[]\n) {\n await waitForContent(renderInstance, content, () => inputs.forEach((input) => renderInstance.stdin.write(input)))\n // Yield so React 19's scheduler can flush effects (e.g. re-register useInput\n // handlers with up-to-date closures) before subsequent input is sent.\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)))\n}\n\n/** Function that is useful when you want to check the last frame of a component that unmounted.\n *\n * With Ink 6 / React 19, the output is no longer cleared on unmount,\n * so lastFrame() consistently returns the last rendered content.\n */\nexport function getLastFrameAfterUnmount(renderInstance: ReturnType<typeof render>) {\n return renderInstance.lastFrame()\n}\n\ntype TrackedPromise<T> = Promise<T> & {\n isFulfilled: () => boolean\n isPending: () => boolean\n isRejected: () => boolean\n}\n\nfunction trackPromise<T>(promise: Promise<T>): TrackedPromise<T> {\n let isPending = true\n let isRejected = false\n let isFulfilled = false\n\n const trackedPromise = promise.then(\n function (result) {\n isFulfilled = true\n isPending = false\n return result\n },\n function (error) {\n isRejected = true\n isPending = false\n throw error\n },\n ) as TrackedPromise<T>\n\n trackedPromise.isFulfilled = function () {\n return isFulfilled\n }\n trackedPromise.isPending = function () {\n return isPending\n }\n trackedPromise.isRejected = function () {\n return isRejected\n }\n\n return trackedPromise\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../../../../src/private/node/testing/ui.ts"],"names":[],"mappings":"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","sourcesContent":["import {Stdout, InkLifecycleRoot} from '../ui.js'\nimport React, {ReactElement} from 'react'\nimport {render as inkRender} from 'ink'\n\nimport {EventEmitter} from 'events'\n\nclass Stderr extends EventEmitter {\n readonly frames: string[] = []\n private _lastFrame?: string\n\n write = (frame: string) => {\n this.frames.push(frame)\n this._lastFrame = frame\n }\n\n lastFrame = () => this._lastFrame\n}\n\nexport class Stdin extends EventEmitter {\n isTTY: boolean\n data: string | null = null\n\n constructor(options: {isTTY?: boolean} = {}) {\n super()\n this.isTTY = options.isTTY ?? true\n }\n\n write = (data: string) => {\n this.data = data\n this.emit('readable')\n }\n\n setEncoding() {}\n setRawMode() {}\n ref() {}\n unref() {}\n read: () => string | null = () => {\n const data = this.data\n\n this.data = null\n\n return data\n }\n}\n\ninterface Instance {\n rerender: (tree: ReactElement) => void\n unmount: () => void\n cleanup: () => void\n stdout: Stdout\n stderr: Stderr\n stdin: Stdin\n frames: string[]\n lastFrame: () => string | undefined\n waitUntilExit: () => TrackedPromise<void>\n}\n\ninterface RenderOptions {\n stdout?: EventEmitter\n stderr?: EventEmitter\n stdin?: EventEmitter\n}\n\nexport const render = (tree: ReactElement, options: RenderOptions = {}): Instance => {\n const stdout = new Stdout({columns: 100})\n const stderr = new Stderr()\n const stdin = new Stdin()\n\n const instance = inkRender(React.createElement(InkLifecycleRoot, null, tree), {\n stdout: options.stdout ?? (stdout as any),\n\n stderr: options.stderr ?? (stderr as any),\n\n stdin: options.stdin ?? (stdin as any),\n debug: true,\n exitOnCtrlC: false,\n patchConsole: false,\n })\n\n return {\n rerender: (tree: ReactElement) => instance.rerender(React.createElement(InkLifecycleRoot, null, tree)),\n unmount: instance.unmount,\n cleanup: instance.cleanup,\n waitUntilExit: () => trackPromise(instance.waitUntilExit().then(() => {})),\n stdout,\n stderr,\n stdin,\n frames: stdout.frames,\n lastFrame: stdout.lastFrame,\n }\n}\n\n/**\n * Wait for the component to be ready to accept input.\n */\nexport function waitForInputsToBeReady() {\n return new Promise((resolve) => setTimeout(resolve, 10))\n}\n\n/**\n * Wait for the last frame to change to anything.\n */\nexport async function waitForChange(func: () => void, getChangingValue: () => string | number | undefined) {\n const initialValue = getChangingValue()\n\n func()\n\n while (getChangingValue() === initialValue) {\n // Yield via setImmediate so React 19's scheduler (which also uses\n // setImmediate in Node.js) can flush batched renders, then yield\n // via setTimeout(0) to let any follow-up microtasks settle.\n // eslint-disable-next-line no-await-in-loop\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)))\n }\n}\n\nexport async function waitFor(func: () => void, condition: () => boolean) {\n func()\n\n while (!condition()) {\n // eslint-disable-next-line no-await-in-loop\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)))\n }\n}\n\n/**\n * Wait for the last frame to contain specific text.\n */\nexport function waitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n func: () => void = () => {},\n) {\n return waitFor(\n () => func(),\n\n () => renderInstance.lastFrame()!.includes(content),\n )\n}\n\n/**\n * Send input and wait for the last frame to change.\n *\n * Useful when you want to send some input and wait for anything to change in the interface.\n * If you need to wait for a specific change instead, you can use sendInputAndWaitForContent.\n */\nexport async function sendInputAndWaitForChange(renderInstance: ReturnType<typeof render>, ...inputs: string[]) {\n await waitForChange(() => inputs.forEach((input) => renderInstance.stdin.write(input)), renderInstance.lastFrame)\n // Yield via setImmediate → setTimeout(0) so React 19's scheduler can flush\n // effects (e.g. re-register useInput handlers with up-to-date closures)\n // before subsequent input is sent.\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)))\n}\n\n/** Send input and wait a number of milliseconds.\n *\n * Useful if you know some what will happen after input will take a certain amount of time\n * and it will not cause any visible change so you can't use sendInputAndWaitForChange.\n * This function can also be used if you want to test that nothing changes after some input has been sent.\n */\nexport async function sendInputAndWait(\n renderInstance: ReturnType<typeof render>,\n waitTime: number,\n ...inputs: string[]\n) {\n inputs.forEach((input) => renderInstance.stdin.write(input))\n await new Promise((resolve) => setTimeout(resolve, waitTime))\n}\n\n/**\n * Send input and wait for the last frame to contain specific text.\n *\n * Useful when you want to send some input and wait for a specific change to happen.\n * If you need to wait for any change instead, you can use sendInputAndWaitForChange.\n */\nexport async function sendInputAndWaitForContent(\n renderInstance: ReturnType<typeof render>,\n content: string,\n ...inputs: string[]\n) {\n await waitForContent(renderInstance, content, () => inputs.forEach((input) => renderInstance.stdin.write(input)))\n // Yield so React 19's scheduler can flush effects (e.g. re-register useInput\n // handlers with up-to-date closures) before subsequent input is sent.\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)))\n}\n\n/** Function that is useful when you want to check the last frame of a component that unmounted.\n *\n * With Ink 6 / React 19, the output is no longer cleared on unmount,\n * so lastFrame() consistently returns the last rendered content.\n */\nexport function getLastFrameAfterUnmount(renderInstance: ReturnType<typeof render>) {\n return renderInstance.lastFrame()\n}\n\ntype TrackedPromise<T> = Promise<T> & {\n isFulfilled: () => boolean\n isPending: () => boolean\n isRejected: () => boolean\n}\n\nfunction trackPromise<T>(promise: Promise<T>): TrackedPromise<T> {\n let isPending = true\n let isRejected = false\n let isFulfilled = false\n\n const trackedPromise = promise.then(\n function (result) {\n isFulfilled = true\n isPending = false\n return result\n },\n function (error) {\n isRejected = true\n isPending = false\n throw error\n },\n ) as TrackedPromise<T>\n\n trackedPromise.isFulfilled = function () {\n return isFulfilled\n }\n trackedPromise.isPending = function () {\n return isPending\n }\n trackedPromise.isRejected = function () {\n return isRejected\n }\n\n return trackedPromise\n}\n"]}
|
|
@@ -2,12 +2,13 @@ import { SelectInput } from './SelectInput.js';
|
|
|
2
2
|
import { TextInput } from './TextInput.js';
|
|
3
3
|
import { PromptLayout } from './Prompts/PromptLayout.js';
|
|
4
4
|
import { throttle } from '../../../../public/common/function.js';
|
|
5
|
+
import { useComplete } from '../../ui.js';
|
|
5
6
|
import usePrompt, { PromptState } from '../hooks/use-prompt.js';
|
|
6
7
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
7
|
-
import { Box
|
|
8
|
+
import { Box } from 'ink';
|
|
8
9
|
const MIN_NUMBER_OF_ITEMS_FOR_SEARCH = 5;
|
|
9
10
|
function AutocompletePrompt({ message, choices, infoTable, onSubmit, search, hasMorePages: initialHasMorePages = false, abortSignal, infoMessage, groupOrder, }) {
|
|
10
|
-
const
|
|
11
|
+
const complete = useComplete();
|
|
11
12
|
const [searchTerm, setSearchTerm] = useState('');
|
|
12
13
|
const [searchResults, setSearchResults] = useState(choices);
|
|
13
14
|
const canSearch = choices.length > MIN_NUMBER_OF_ITEMS_FOR_SEARCH;
|
|
@@ -28,10 +29,10 @@ function AutocompletePrompt({ message, choices, infoTable, onSubmit, search, has
|
|
|
28
29
|
useEffect(() => {
|
|
29
30
|
if (promptState === PromptState.Submitted && answer) {
|
|
30
31
|
setSearchTerm('');
|
|
31
|
-
unmountInk();
|
|
32
32
|
onSubmit(answer.value);
|
|
33
|
+
complete();
|
|
33
34
|
}
|
|
34
|
-
}, [answer, onSubmit, promptState,
|
|
35
|
+
}, [answer, onSubmit, promptState, complete]);
|
|
35
36
|
const setLoadingWhenSlow = useRef();
|
|
36
37
|
// we want to set it each time so that searchTermRef always tracks searchTerm,
|
|
37
38
|
// this is NOT the same as writing useRef(searchTerm)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AutocompletePrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/AutocompletePrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SelectInput, SelectInputProps, Item as SelectItem} from './SelectInput.js'\nimport {InfoTableProps} from './Prompts/InfoTable.js'\nimport {TextInput} from './TextInput.js'\nimport {InfoMessageProps} from './Prompts/InfoMessage.js'\nimport {Message, PromptLayout} from './Prompts/PromptLayout.js'\nimport {throttle} from '../../../../public/common/function.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport usePrompt, {PromptState} from '../hooks/use-prompt.js'\n\nimport React, {ReactElement, useCallback, useEffect, useRef, useState} from 'react'\nimport {Box, useApp} from 'ink'\n\nexport interface SearchResults<T> {\n data: SelectItem<T>[]\n meta?: {\n hasNextPage: boolean\n }\n}\n\nexport interface AutocompletePromptProps<T> {\n message: Message\n choices: SelectInputProps<T>['items']\n onSubmit: (value: T) => void\n infoTable?: InfoTableProps['table']\n hasMorePages?: boolean\n search: (term: string) => Promise<SearchResults<T>>\n abortSignal?: AbortSignal\n infoMessage?: InfoMessageProps['message']\n groupOrder?: string[]\n}\n\nconst MIN_NUMBER_OF_ITEMS_FOR_SEARCH = 5\n\nfunction AutocompletePrompt<T>({\n message,\n choices,\n infoTable,\n onSubmit,\n search,\n hasMorePages: initialHasMorePages = false,\n abortSignal,\n infoMessage,\n groupOrder,\n}: React.PropsWithChildren<AutocompletePromptProps<T>>): ReactElement | null {\n const {exit: unmountInk} = useApp()\n const [searchTerm, setSearchTerm] = useState('')\n const [searchResults, setSearchResults] = useState<SelectItem<T>[]>(choices)\n const canSearch = choices.length > MIN_NUMBER_OF_ITEMS_FOR_SEARCH\n const [hasMorePages, setHasMorePages] = useState(initialHasMorePages)\n const {promptState, setPromptState, answer, setAnswer} = usePrompt<SelectItem<T> | undefined>({\n initialAnswer: undefined,\n })\n\n const paginatedSearch = useCallback(\n async (term: string) => {\n const results = await search(term)\n return results\n },\n [search],\n )\n\n const submitAnswer = useCallback(\n (answer: SelectItem<T>) => {\n if (promptState === PromptState.Idle) {\n setAnswer(answer)\n setPromptState(PromptState.Submitted)\n }\n },\n [promptState, setAnswer, setPromptState],\n )\n\n useEffect(() => {\n if (promptState === PromptState.Submitted && answer) {\n setSearchTerm('')\n unmountInk()\n onSubmit(answer.value)\n }\n }, [answer, onSubmit, promptState, unmountInk])\n\n const setLoadingWhenSlow = useRef<NodeJS.Timeout>()\n\n // we want to set it each time so that searchTermRef always tracks searchTerm,\n // this is NOT the same as writing useRef(searchTerm)\n const searchTermRef = useRef('')\n searchTermRef.current = searchTerm\n\n // Keep current values in refs to avoid stale closures\n const choicesRef = useRef(choices)\n choicesRef.current = choices\n const initialHasPagesRef = useRef(initialHasMorePages)\n initialHasPagesRef.current = initialHasMorePages\n\n // useMemo ensures debounceSearch is not recreated on every render\n const debounceSearch = React.useMemo(\n () =>\n throttle(\n (term: string) => {\n setLoadingWhenSlow.current = setTimeout(() => {\n setPromptState(PromptState.Loading)\n }, 100)\n paginatedSearch(term)\n .then((result) => {\n // while we were waiting for the promise to resolve, the user\n // has emptied the search term, so we want to show the default\n // choices instead\n if (searchTermRef.current.length === 0) {\n setSearchResults(choicesRef.current)\n setHasMorePages(initialHasPagesRef.current)\n } else {\n setSearchResults(result.data)\n setHasMorePages(result.meta?.hasNextPage ?? false)\n }\n\n setPromptState(PromptState.Idle)\n })\n .catch(() => {\n setPromptState(PromptState.Error)\n })\n .finally(() => {\n clearTimeout(setLoadingWhenSlow.current)\n })\n },\n 400,\n {leading: true, trailing: true},\n ),\n [paginatedSearch, setPromptState],\n )\n\n return (\n <PromptLayout\n message={message}\n state={promptState}\n infoTable={infoTable}\n infoMessage={infoMessage}\n abortSignal={abortSignal}\n header={\n promptState !== PromptState.Submitted && canSearch ? (\n <Box marginLeft={3}>\n <TextInput\n value={searchTerm}\n onChange={(term) => {\n setSearchTerm(term)\n // Update ref immediately so that the debounceSearch's .then()\n // callback sees the current term. With React 19's automatic\n // batching, the render (which normally updates the ref) is\n // deferred, so without this the ref would be stale when the\n // search Promise resolves.\n searchTermRef.current = term\n\n if (term.length > 0) {\n debounceSearch(term)\n } else {\n debounceSearch.cancel()\n setPromptState(PromptState.Idle)\n setSearchResults(choices)\n }\n }}\n placeholder=\"Type to search...\"\n />\n </Box>\n ) : null\n }\n submittedAnswerLabel={answer?.label}\n input={\n <SelectInput\n items={searchResults}\n initialItems={choices}\n enableShortcuts={false}\n emptyMessage=\"No results found.\"\n highlightedTerm={searchTerm}\n loading={promptState === PromptState.Loading}\n errorMessage={\n promptState === PromptState.Error\n ? 'There has been an error while searching. Please try again later.'\n : undefined\n }\n hasMorePages={hasMorePages}\n morePagesMessage=\"Find what you're looking for by typing its name.\"\n onSubmit={submitAnswer}\n groupOrder={groupOrder}\n />\n }\n />\n )\n}\n\nexport {AutocompletePrompt}\n"]}
|
|
1
|
+
{"version":3,"file":"AutocompletePrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/AutocompletePrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SelectInput, SelectInputProps, Item as SelectItem} from './SelectInput.js'\nimport {InfoTableProps} from './Prompts/InfoTable.js'\nimport {TextInput} from './TextInput.js'\nimport {InfoMessageProps} from './Prompts/InfoMessage.js'\nimport {Message, PromptLayout} from './Prompts/PromptLayout.js'\nimport {throttle} from '../../../../public/common/function.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport {useComplete} from '../../ui.js'\nimport usePrompt, {PromptState} from '../hooks/use-prompt.js'\n\nimport React, {ReactElement, useCallback, useEffect, useRef, useState} from 'react'\nimport {Box} from 'ink'\n\nexport interface SearchResults<T> {\n data: SelectItem<T>[]\n meta?: {\n hasNextPage: boolean\n }\n}\n\nexport interface AutocompletePromptProps<T> {\n message: Message\n choices: SelectInputProps<T>['items']\n onSubmit: (value: T) => void\n infoTable?: InfoTableProps['table']\n hasMorePages?: boolean\n search: (term: string) => Promise<SearchResults<T>>\n abortSignal?: AbortSignal\n infoMessage?: InfoMessageProps['message']\n groupOrder?: string[]\n}\n\nconst MIN_NUMBER_OF_ITEMS_FOR_SEARCH = 5\n\nfunction AutocompletePrompt<T>({\n message,\n choices,\n infoTable,\n onSubmit,\n search,\n hasMorePages: initialHasMorePages = false,\n abortSignal,\n infoMessage,\n groupOrder,\n}: React.PropsWithChildren<AutocompletePromptProps<T>>): ReactElement | null {\n const complete = useComplete()\n const [searchTerm, setSearchTerm] = useState('')\n const [searchResults, setSearchResults] = useState<SelectItem<T>[]>(choices)\n const canSearch = choices.length > MIN_NUMBER_OF_ITEMS_FOR_SEARCH\n const [hasMorePages, setHasMorePages] = useState(initialHasMorePages)\n const {promptState, setPromptState, answer, setAnswer} = usePrompt<SelectItem<T> | undefined>({\n initialAnswer: undefined,\n })\n\n const paginatedSearch = useCallback(\n async (term: string) => {\n const results = await search(term)\n return results\n },\n [search],\n )\n\n const submitAnswer = useCallback(\n (answer: SelectItem<T>) => {\n if (promptState === PromptState.Idle) {\n setAnswer(answer)\n setPromptState(PromptState.Submitted)\n }\n },\n [promptState, setAnswer, setPromptState],\n )\n\n useEffect(() => {\n if (promptState === PromptState.Submitted && answer) {\n setSearchTerm('')\n onSubmit(answer.value)\n complete()\n }\n }, [answer, onSubmit, promptState, complete])\n\n const setLoadingWhenSlow = useRef<NodeJS.Timeout>()\n\n // we want to set it each time so that searchTermRef always tracks searchTerm,\n // this is NOT the same as writing useRef(searchTerm)\n const searchTermRef = useRef('')\n searchTermRef.current = searchTerm\n\n // Keep current values in refs to avoid stale closures\n const choicesRef = useRef(choices)\n choicesRef.current = choices\n const initialHasPagesRef = useRef(initialHasMorePages)\n initialHasPagesRef.current = initialHasMorePages\n\n // useMemo ensures debounceSearch is not recreated on every render\n const debounceSearch = React.useMemo(\n () =>\n throttle(\n (term: string) => {\n setLoadingWhenSlow.current = setTimeout(() => {\n setPromptState(PromptState.Loading)\n }, 100)\n paginatedSearch(term)\n .then((result) => {\n // while we were waiting for the promise to resolve, the user\n // has emptied the search term, so we want to show the default\n // choices instead\n if (searchTermRef.current.length === 0) {\n setSearchResults(choicesRef.current)\n setHasMorePages(initialHasPagesRef.current)\n } else {\n setSearchResults(result.data)\n setHasMorePages(result.meta?.hasNextPage ?? false)\n }\n\n setPromptState(PromptState.Idle)\n })\n .catch(() => {\n setPromptState(PromptState.Error)\n })\n .finally(() => {\n clearTimeout(setLoadingWhenSlow.current)\n })\n },\n 400,\n {leading: true, trailing: true},\n ),\n [paginatedSearch, setPromptState],\n )\n\n return (\n <PromptLayout\n message={message}\n state={promptState}\n infoTable={infoTable}\n infoMessage={infoMessage}\n abortSignal={abortSignal}\n header={\n promptState !== PromptState.Submitted && canSearch ? (\n <Box marginLeft={3}>\n <TextInput\n value={searchTerm}\n onChange={(term) => {\n setSearchTerm(term)\n // Update ref immediately so that the debounceSearch's .then()\n // callback sees the current term. With React 19's automatic\n // batching, the render (which normally updates the ref) is\n // deferred, so without this the ref would be stale when the\n // search Promise resolves.\n searchTermRef.current = term\n\n if (term.length > 0) {\n debounceSearch(term)\n } else {\n debounceSearch.cancel()\n setPromptState(PromptState.Idle)\n setSearchResults(choices)\n }\n }}\n placeholder=\"Type to search...\"\n />\n </Box>\n ) : null\n }\n submittedAnswerLabel={answer?.label}\n input={\n <SelectInput\n items={searchResults}\n initialItems={choices}\n enableShortcuts={false}\n emptyMessage=\"No results found.\"\n highlightedTerm={searchTerm}\n loading={promptState === PromptState.Loading}\n errorMessage={\n promptState === PromptState.Error\n ? 'There has been an error while searching. Please try again later.'\n : undefined\n }\n hasMorePages={hasMorePages}\n morePagesMessage=\"Find what you're looking for by typing its name.\"\n onSubmit={submitAnswer}\n groupOrder={groupOrder}\n />\n }\n />\n )\n}\n\nexport {AutocompletePrompt}\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { useComplete } from '../../ui.js';
|
|
1
2
|
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import { Box, Static, Text
|
|
3
|
+
import { Box, Static, Text } from 'ink';
|
|
3
4
|
import figures from 'figures';
|
|
4
5
|
import stripAnsi from 'strip-ansi';
|
|
5
6
|
import { Writable } from 'stream';
|
|
@@ -58,7 +59,8 @@ function useConcurrentOutputContext(context, callback) {
|
|
|
58
59
|
*/
|
|
59
60
|
const ConcurrentOutput = ({ processes, prefixColumnSize, abortSignal, showTimestamps = true, keepRunningAfterProcessesResolve = false, useAlternativeColorPalette = false, }) => {
|
|
60
61
|
const [processOutput, setProcessOutput] = useState([]);
|
|
61
|
-
const
|
|
62
|
+
const [completionResult, setCompletionResult] = useState(null);
|
|
63
|
+
const complete = useComplete();
|
|
62
64
|
const concurrentColors = useMemo(() => useAlternativeColorPalette
|
|
63
65
|
? ['#b994c3', '#e69e19', '#d17a73', 'cyan', 'magenta', 'blue']
|
|
64
66
|
: ['yellow', 'cyan', 'magenta', 'green', 'blue'], [useAlternativeColorPalette]);
|
|
@@ -120,24 +122,24 @@ const ConcurrentOutput = ({ processes, prefixColumnSize, abortSignal, showTimest
|
|
|
120
122
|
await process.action(stdout, stderr, abortSignal);
|
|
121
123
|
}));
|
|
122
124
|
if (!keepRunningAfterProcessesResolve) {
|
|
123
|
-
|
|
124
|
-
// state updates before the component tree is torn down.
|
|
125
|
-
// Use setImmediate → setTimeout(0) to span two event-loop phases,
|
|
126
|
-
// giving React's scheduler (which uses setImmediate in Node.js)
|
|
127
|
-
// a full cycle to flush before we tear down the component tree.
|
|
128
|
-
setImmediate(() => setTimeout(() => unmountInk(), 0));
|
|
125
|
+
setCompletionResult({});
|
|
129
126
|
}
|
|
130
127
|
// eslint-disable-next-line no-catch-all/no-catch-all
|
|
131
128
|
}
|
|
132
129
|
catch (error) {
|
|
133
130
|
if (!keepRunningAfterProcessesResolve) {
|
|
134
|
-
|
|
131
|
+
setCompletionResult({ error: error });
|
|
135
132
|
}
|
|
136
133
|
}
|
|
137
134
|
};
|
|
138
135
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
139
136
|
runProcesses();
|
|
140
|
-
}, [abortSignal, processes, writableStream,
|
|
137
|
+
}, [abortSignal, processes, writableStream, keepRunningAfterProcessesResolve]);
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (completionResult !== null) {
|
|
140
|
+
complete(completionResult.error);
|
|
141
|
+
}
|
|
142
|
+
}, [completionResult, complete]);
|
|
141
143
|
const { lineVertical } = figures;
|
|
142
144
|
return (React.createElement(Static, { items: processOutput }, (chunk, index) => {
|
|
143
145
|
return (React.createElement(Box, { flexDirection: "column", key: index }, chunk.lines.map((line, index) => (React.createElement(Box, { key: index, flexDirection: "row" },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConcurrentOutput.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.tsx"],"names":[],"mappings":"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","sourcesContent":["import {OutputProcess} from '../../../../public/node/output.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport React, {FunctionComponent, useCallback, useEffect, useMemo, useState} from 'react'\nimport {Box, Static, Text, TextProps, useApp} from 'ink'\nimport figures from 'figures'\nimport stripAnsi from 'strip-ansi'\n\nimport {Writable} from 'stream'\nimport {AsyncLocalStorage} from 'node:async_hooks'\n\nexport interface ConcurrentOutputProps {\n processes: OutputProcess[]\n prefixColumnSize?: number\n abortSignal: AbortSignal\n showTimestamps?: boolean\n keepRunningAfterProcessesResolve?: boolean\n useAlternativeColorPalette?: boolean\n}\n\ninterface Chunk {\n color: TextProps['color']\n prefix: string\n lines: string[]\n}\n\nfunction addLeadingZero(number: number) {\n if (number < 10) {\n return `0${number}`\n } else {\n return number.toString()\n }\n}\n\nfunction currentTime() {\n const currentDateTime = new Date()\n const hours = addLeadingZero(currentDateTime.getHours())\n const minutes = addLeadingZero(currentDateTime.getMinutes())\n const seconds = addLeadingZero(currentDateTime.getSeconds())\n return `${hours}:${minutes}:${seconds}`\n}\n\ninterface ConcurrentOutputContext {\n outputPrefix?: string\n stripAnsi?: boolean\n}\n\nconst outputContextStore = new AsyncLocalStorage<ConcurrentOutputContext>()\n\nfunction useConcurrentOutputContext<T>(context: ConcurrentOutputContext, callback: () => T): T {\n return outputContextStore.run(context, callback)\n}\n\n/**\n * Renders output from concurrent processes to the terminal.\n * Output will be divided in a three column layout\n * with the left column containing the timestamp,\n * the right column containing the output,\n * and the middle column containing the process prefix.\n * Every process will be rendered with a different color, up to 4 colors.\n *\n * For example running `shopify app dev`:\n *\n * ```shell\n * 2022-10-10 13:11:03 | backend | npm\n * 2022-10-10 13:11:03 | backend | WARN ignoring workspace config at ...\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend | > shopify-app-template-node@0.1.0 dev\n * 2022-10-10 13:11:03 | backend | > cross-env NODE_ENV=development nodemon backend/index.js --watch ./backend\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | frontend | > starter-react-frontend-app@0.1.0 dev\n * 2022-10-10 13:11:03 | frontend | > cross-env NODE_ENV=development node vite-server.js\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend | [nodemon] to restart at any time, enter `rs`\n * 2022-10-10 13:11:03 | backend | [nodemon] watching path(s): backend/\n * 2022-10-10 13:11:03 | backend | [nodemon] watching extensions: js,mjs,json\n * 2022-10-10 13:11:03 | backend | [nodemon] starting `node backend/index.js`\n * 2022-10-10 13:11:03 | backend |\n *\n * ```\n */\nconst ConcurrentOutput: FunctionComponent<ConcurrentOutputProps> = ({\n processes,\n prefixColumnSize,\n abortSignal,\n showTimestamps = true,\n keepRunningAfterProcessesResolve = false,\n useAlternativeColorPalette = false,\n}) => {\n const [processOutput, setProcessOutput] = useState<Chunk[]>([])\n const {exit: unmountInk} = useApp()\n const concurrentColors: TextProps['color'][] = useMemo(\n () =>\n useAlternativeColorPalette\n ? ['#b994c3', '#e69e19', '#d17a73', 'cyan', 'magenta', 'blue']\n : ['yellow', 'cyan', 'magenta', 'green', 'blue'],\n [useAlternativeColorPalette],\n )\n\n const calculatedPrefixColumnSize = useMemo(() => {\n const maxColumnSize = 25\n\n // If the prefixColumnSize is not provided, we calculate it based on the longest process prefix\n const columnSize =\n prefixColumnSize ??\n processes.reduce((maxPrefixLength, process) => Math.max(maxPrefixLength, process.prefix.length), 0)\n\n // Apply overall limit to the prefix column size\n return Math.min(columnSize, maxColumnSize)\n }, [processes, prefixColumnSize])\n\n const addPrefix = (prefix: string, prefixes: string[]) => {\n const index = prefixes.indexOf(prefix)\n if (index !== -1) {\n return index\n }\n prefixes.push(prefix)\n return prefixes.length - 1\n }\n\n const lineColor = useCallback(\n (index: number) => {\n const colorIndex = index % concurrentColors.length\n return concurrentColors[colorIndex]\n },\n [concurrentColors],\n )\n\n const writableStream = useCallback(\n (process: OutputProcess, prefixes: string[]) => {\n return new Writable({\n write(chunk, _encoding, next) {\n const context = outputContextStore.getStore()\n const prefix = context?.outputPrefix ?? process.prefix\n const shouldStripAnsi = context?.stripAnsi ?? true\n const log = chunk.toString('utf8').replace(/(\\n)$/, '')\n\n const index = addPrefix(prefix, prefixes)\n\n const lines = shouldStripAnsi ? stripAnsi(log).split(/\\n/) : log.split(/\\n/)\n setProcessOutput((previousProcessOutput) => [\n ...previousProcessOutput,\n {\n color: lineColor(index),\n prefix,\n lines,\n },\n ])\n next()\n },\n })\n },\n [lineColor],\n )\n\n const formatPrefix = (prefix: string) => {\n // Truncate prefix if needed\n if (prefix.length > calculatedPrefixColumnSize) {\n return prefix.substring(0, calculatedPrefixColumnSize)\n }\n\n return `${' '.repeat(calculatedPrefixColumnSize - prefix.length)}${prefix}`\n }\n\n useEffect(() => {\n const runProcesses = async () => {\n const prefixes: string[] = []\n\n try {\n await Promise.all(\n processes.map(async (process) => {\n const stdout = writableStream(process, prefixes)\n const stderr = writableStream(process, prefixes)\n await process.action(stdout, stderr, abortSignal)\n }),\n )\n if (!keepRunningAfterProcessesResolve) {\n // Defer unmount so React 19 can flush batched setProcessOutput\n // state updates before the component tree is torn down.\n // Use setImmediate → setTimeout(0) to span two event-loop phases,\n // giving React's scheduler (which uses setImmediate in Node.js)\n // a full cycle to flush before we tear down the component tree.\n setImmediate(() => setTimeout(() => unmountInk(), 0))\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error: unknown) {\n if (!keepRunningAfterProcessesResolve) {\n setImmediate(() => setTimeout(() => unmountInk(error as Error | undefined), 0))\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n runProcesses()\n }, [abortSignal, processes, writableStream, unmountInk, keepRunningAfterProcessesResolve])\n\n const {lineVertical} = figures\n\n return (\n <Static items={processOutput}>\n {(chunk, index) => {\n return (\n <Box flexDirection=\"column\" key={index}>\n {chunk.lines.map((line, index) => (\n <Box key={index} flexDirection=\"row\">\n <Text>\n {showTimestamps ? (\n <Text>\n {currentTime()} {lineVertical}{' '}\n </Text>\n ) : null}\n <Text color={chunk.color}>{formatPrefix(chunk.prefix)}</Text>\n <Text>\n {' '}\n {lineVertical} {line}\n </Text>\n </Text>\n </Box>\n ))}\n </Box>\n )\n }}\n </Static>\n )\n}\nexport {ConcurrentOutput, ConcurrentOutputContext, useConcurrentOutputContext}\n"]}
|
|
1
|
+
{"version":3,"file":"ConcurrentOutput.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.tsx"],"names":[],"mappings":"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","sourcesContent":["import {OutputProcess} from '../../../../public/node/output.js'\nimport {AbortSignal} from '../../../../public/node/abort.js'\nimport {useComplete} from '../../ui.js'\nimport React, {FunctionComponent, useCallback, useEffect, useMemo, useState} from 'react'\nimport {Box, Static, Text, TextProps} from 'ink'\nimport figures from 'figures'\nimport stripAnsi from 'strip-ansi'\n\nimport {Writable} from 'stream'\nimport {AsyncLocalStorage} from 'node:async_hooks'\n\nexport interface ConcurrentOutputProps {\n processes: OutputProcess[]\n prefixColumnSize?: number\n abortSignal: AbortSignal\n showTimestamps?: boolean\n keepRunningAfterProcessesResolve?: boolean\n useAlternativeColorPalette?: boolean\n}\n\ninterface Chunk {\n color: TextProps['color']\n prefix: string\n lines: string[]\n}\n\nfunction addLeadingZero(number: number) {\n if (number < 10) {\n return `0${number}`\n } else {\n return number.toString()\n }\n}\n\nfunction currentTime() {\n const currentDateTime = new Date()\n const hours = addLeadingZero(currentDateTime.getHours())\n const minutes = addLeadingZero(currentDateTime.getMinutes())\n const seconds = addLeadingZero(currentDateTime.getSeconds())\n return `${hours}:${minutes}:${seconds}`\n}\n\ninterface ConcurrentOutputContext {\n outputPrefix?: string\n stripAnsi?: boolean\n}\n\nconst outputContextStore = new AsyncLocalStorage<ConcurrentOutputContext>()\n\nfunction useConcurrentOutputContext<T>(context: ConcurrentOutputContext, callback: () => T): T {\n return outputContextStore.run(context, callback)\n}\n\n/**\n * Renders output from concurrent processes to the terminal.\n * Output will be divided in a three column layout\n * with the left column containing the timestamp,\n * the right column containing the output,\n * and the middle column containing the process prefix.\n * Every process will be rendered with a different color, up to 4 colors.\n *\n * For example running `shopify app dev`:\n *\n * ```shell\n * 2022-10-10 13:11:03 | backend | npm\n * 2022-10-10 13:11:03 | backend | WARN ignoring workspace config at ...\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend | > shopify-app-template-node@0.1.0 dev\n * 2022-10-10 13:11:03 | backend | > cross-env NODE_ENV=development nodemon backend/index.js --watch ./backend\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | frontend | > starter-react-frontend-app@0.1.0 dev\n * 2022-10-10 13:11:03 | frontend | > cross-env NODE_ENV=development node vite-server.js\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | frontend |\n * 2022-10-10 13:11:03 | backend |\n * 2022-10-10 13:11:03 | backend | [nodemon] to restart at any time, enter `rs`\n * 2022-10-10 13:11:03 | backend | [nodemon] watching path(s): backend/\n * 2022-10-10 13:11:03 | backend | [nodemon] watching extensions: js,mjs,json\n * 2022-10-10 13:11:03 | backend | [nodemon] starting `node backend/index.js`\n * 2022-10-10 13:11:03 | backend |\n *\n * ```\n */\nconst ConcurrentOutput: FunctionComponent<ConcurrentOutputProps> = ({\n processes,\n prefixColumnSize,\n abortSignal,\n showTimestamps = true,\n keepRunningAfterProcessesResolve = false,\n useAlternativeColorPalette = false,\n}) => {\n const [processOutput, setProcessOutput] = useState<Chunk[]>([])\n const [completionResult, setCompletionResult] = useState<{error?: Error} | null>(null)\n const complete = useComplete()\n const concurrentColors: TextProps['color'][] = useMemo(\n () =>\n useAlternativeColorPalette\n ? ['#b994c3', '#e69e19', '#d17a73', 'cyan', 'magenta', 'blue']\n : ['yellow', 'cyan', 'magenta', 'green', 'blue'],\n [useAlternativeColorPalette],\n )\n\n const calculatedPrefixColumnSize = useMemo(() => {\n const maxColumnSize = 25\n\n // If the prefixColumnSize is not provided, we calculate it based on the longest process prefix\n const columnSize =\n prefixColumnSize ??\n processes.reduce((maxPrefixLength, process) => Math.max(maxPrefixLength, process.prefix.length), 0)\n\n // Apply overall limit to the prefix column size\n return Math.min(columnSize, maxColumnSize)\n }, [processes, prefixColumnSize])\n\n const addPrefix = (prefix: string, prefixes: string[]) => {\n const index = prefixes.indexOf(prefix)\n if (index !== -1) {\n return index\n }\n prefixes.push(prefix)\n return prefixes.length - 1\n }\n\n const lineColor = useCallback(\n (index: number) => {\n const colorIndex = index % concurrentColors.length\n return concurrentColors[colorIndex]\n },\n [concurrentColors],\n )\n\n const writableStream = useCallback(\n (process: OutputProcess, prefixes: string[]) => {\n return new Writable({\n write(chunk, _encoding, next) {\n const context = outputContextStore.getStore()\n const prefix = context?.outputPrefix ?? process.prefix\n const shouldStripAnsi = context?.stripAnsi ?? true\n const log = chunk.toString('utf8').replace(/(\\n)$/, '')\n\n const index = addPrefix(prefix, prefixes)\n\n const lines = shouldStripAnsi ? stripAnsi(log).split(/\\n/) : log.split(/\\n/)\n setProcessOutput((previousProcessOutput) => [\n ...previousProcessOutput,\n {\n color: lineColor(index),\n prefix,\n lines,\n },\n ])\n next()\n },\n })\n },\n [lineColor],\n )\n\n const formatPrefix = (prefix: string) => {\n // Truncate prefix if needed\n if (prefix.length > calculatedPrefixColumnSize) {\n return prefix.substring(0, calculatedPrefixColumnSize)\n }\n\n return `${' '.repeat(calculatedPrefixColumnSize - prefix.length)}${prefix}`\n }\n\n useEffect(() => {\n const runProcesses = async () => {\n const prefixes: string[] = []\n\n try {\n await Promise.all(\n processes.map(async (process) => {\n const stdout = writableStream(process, prefixes)\n const stderr = writableStream(process, prefixes)\n await process.action(stdout, stderr, abortSignal)\n }),\n )\n if (!keepRunningAfterProcessesResolve) {\n setCompletionResult({})\n }\n // eslint-disable-next-line no-catch-all/no-catch-all\n } catch (error: unknown) {\n if (!keepRunningAfterProcessesResolve) {\n setCompletionResult({error: error as Error})\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-floating-promises\n runProcesses()\n }, [abortSignal, processes, writableStream, keepRunningAfterProcessesResolve])\n\n useEffect(() => {\n if (completionResult !== null) {\n complete(completionResult.error)\n }\n }, [completionResult, complete])\n\n const {lineVertical} = figures\n\n return (\n <Static items={processOutput}>\n {(chunk, index) => {\n return (\n <Box flexDirection=\"column\" key={index}>\n {chunk.lines.map((line, index) => (\n <Box key={index} flexDirection=\"row\">\n <Text>\n {showTimestamps ? (\n <Text>\n {currentTime()} {lineVertical}{' '}\n </Text>\n ) : null}\n <Text color={chunk.color}>{formatPrefix(chunk.prefix)}</Text>\n <Text>\n {' '}\n {lineVertical} {line}\n </Text>\n </Text>\n </Box>\n ))}\n </Box>\n )\n }}\n </Static>\n )\n}\nexport {ConcurrentOutput, ConcurrentOutputContext, useConcurrentOutputContext}\n"]}
|
|
@@ -20,6 +20,7 @@ describe('ConcurrentOutput', () => {
|
|
|
20
20
|
// Given
|
|
21
21
|
const backendSync = new Synchronizer();
|
|
22
22
|
const frontendSync = new Synchronizer();
|
|
23
|
+
const gate = new Synchronizer();
|
|
23
24
|
const backendProcess = {
|
|
24
25
|
prefix: 'backend',
|
|
25
26
|
action: async (stdout, _stderr, _signal) => {
|
|
@@ -27,6 +28,7 @@ describe('ConcurrentOutput', () => {
|
|
|
27
28
|
stdout.write('second backend message');
|
|
28
29
|
stdout.write('third backend message');
|
|
29
30
|
backendSync.resolve();
|
|
31
|
+
await gate.promise;
|
|
30
32
|
},
|
|
31
33
|
};
|
|
32
34
|
const frontendProcess = {
|
|
@@ -37,6 +39,7 @@ describe('ConcurrentOutput', () => {
|
|
|
37
39
|
stdout.write('second frontend message');
|
|
38
40
|
stdout.write('third frontend message');
|
|
39
41
|
frontendSync.resolve();
|
|
42
|
+
await gate.promise;
|
|
40
43
|
},
|
|
41
44
|
};
|
|
42
45
|
// When
|
|
@@ -54,17 +57,20 @@ describe('ConcurrentOutput', () => {
|
|
|
54
57
|
00:00:00 │ frontend │ third frontend message
|
|
55
58
|
"
|
|
56
59
|
`);
|
|
60
|
+
gate.resolve();
|
|
57
61
|
});
|
|
58
62
|
test('strips ansi codes from the output by default', async () => {
|
|
59
63
|
const output = 'foo';
|
|
60
64
|
// Given
|
|
61
65
|
const processSync = new Synchronizer();
|
|
66
|
+
const gate = new Synchronizer();
|
|
62
67
|
const processes = [
|
|
63
68
|
{
|
|
64
69
|
prefix: '1',
|
|
65
70
|
action: async (stdout, _stderr, _signal) => {
|
|
66
71
|
stdout.write(`\u001b[32m${output}\u001b[39m`);
|
|
67
72
|
processSync.resolve();
|
|
73
|
+
await gate.promise;
|
|
68
74
|
},
|
|
69
75
|
},
|
|
70
76
|
];
|
|
@@ -76,11 +82,13 @@ describe('ConcurrentOutput', () => {
|
|
|
76
82
|
const logColumns = renderInstance.lastFrame().split('│');
|
|
77
83
|
expect(logColumns.length).toBe(3);
|
|
78
84
|
expect(logColumns[2]?.trim()).toEqual(output);
|
|
85
|
+
gate.resolve();
|
|
79
86
|
});
|
|
80
87
|
test('does not strip ansi codes from the output when stripAnsi is false', async () => {
|
|
81
88
|
const output = '\u001b[32mfoo\u001b[39m';
|
|
82
89
|
// Given
|
|
83
90
|
const processSync = new Synchronizer();
|
|
91
|
+
const gate = new Synchronizer();
|
|
84
92
|
const processes = [
|
|
85
93
|
{
|
|
86
94
|
prefix: '1',
|
|
@@ -89,6 +97,7 @@ describe('ConcurrentOutput', () => {
|
|
|
89
97
|
stdout.write(output);
|
|
90
98
|
});
|
|
91
99
|
processSync.resolve();
|
|
100
|
+
await gate.promise;
|
|
92
101
|
},
|
|
93
102
|
},
|
|
94
103
|
];
|
|
@@ -100,10 +109,12 @@ describe('ConcurrentOutput', () => {
|
|
|
100
109
|
const logColumns = renderInstance.lastFrame().split('│');
|
|
101
110
|
expect(logColumns.length).toBe(3);
|
|
102
111
|
expect(logColumns[2]?.trim()).toEqual(output);
|
|
112
|
+
gate.resolve();
|
|
103
113
|
});
|
|
104
114
|
test('renders custom prefixes on log lines', async () => {
|
|
105
115
|
// Given
|
|
106
116
|
const processSync = new Synchronizer();
|
|
117
|
+
const gate = new Synchronizer();
|
|
107
118
|
const extensionName = 'my-extension';
|
|
108
119
|
const processes = [
|
|
109
120
|
{
|
|
@@ -113,6 +124,7 @@ describe('ConcurrentOutput', () => {
|
|
|
113
124
|
stdout.write('foo bar');
|
|
114
125
|
});
|
|
115
126
|
processSync.resolve();
|
|
127
|
+
await gate.promise;
|
|
116
128
|
},
|
|
117
129
|
},
|
|
118
130
|
];
|
|
@@ -126,11 +138,13 @@ describe('ConcurrentOutput', () => {
|
|
|
126
138
|
const logColumns = unstyled(renderInstance.lastFrame()).split('│');
|
|
127
139
|
expect(logColumns.length).toBe(3);
|
|
128
140
|
expect(logColumns[1]?.trim()).toEqual(extensionName);
|
|
141
|
+
gate.resolve();
|
|
129
142
|
});
|
|
130
143
|
test('renders prefix column width based on prefixColumnSize', async () => {
|
|
131
144
|
// Given
|
|
132
145
|
const processSync1 = new Synchronizer();
|
|
133
146
|
const processSync2 = new Synchronizer();
|
|
147
|
+
const gate = new Synchronizer();
|
|
134
148
|
const columnSize = 5;
|
|
135
149
|
const processes = [
|
|
136
150
|
{
|
|
@@ -138,6 +152,7 @@ describe('ConcurrentOutput', () => {
|
|
|
138
152
|
action: async (stdout, _stderr, _signal) => {
|
|
139
153
|
stdout.write('foo');
|
|
140
154
|
processSync1.resolve();
|
|
155
|
+
await gate.promise;
|
|
141
156
|
},
|
|
142
157
|
},
|
|
143
158
|
{
|
|
@@ -145,6 +160,7 @@ describe('ConcurrentOutput', () => {
|
|
|
145
160
|
action: async (stdout, _stderr, _signal) => {
|
|
146
161
|
stdout.write('bar');
|
|
147
162
|
processSync2.resolve();
|
|
163
|
+
await gate.promise;
|
|
148
164
|
},
|
|
149
165
|
},
|
|
150
166
|
];
|
|
@@ -161,21 +177,24 @@ describe('ConcurrentOutput', () => {
|
|
|
161
177
|
// Including spacing
|
|
162
178
|
expect(logColumns[1]?.length).toBe(columnSize + 2);
|
|
163
179
|
});
|
|
180
|
+
gate.resolve();
|
|
164
181
|
});
|
|
165
182
|
test('renders prefix column width based on processes by default', async () => {
|
|
166
183
|
// Given
|
|
167
184
|
const processSync = new Synchronizer();
|
|
185
|
+
const gate = new Synchronizer();
|
|
168
186
|
const processes = [
|
|
169
187
|
{
|
|
170
188
|
prefix: '1',
|
|
171
189
|
action: async (stdout, _stderr, _signal) => {
|
|
172
190
|
stdout.write('foo');
|
|
173
191
|
processSync.resolve();
|
|
192
|
+
await gate.promise;
|
|
174
193
|
},
|
|
175
194
|
},
|
|
176
|
-
{ prefix: '12', action: async () =>
|
|
177
|
-
{ prefix: '123', action: async () =>
|
|
178
|
-
{ prefix: '1234', action: async () =>
|
|
195
|
+
{ prefix: '12', action: async () => gate.promise },
|
|
196
|
+
{ prefix: '123', action: async () => gate.promise },
|
|
197
|
+
{ prefix: '1234', action: async () => gate.promise },
|
|
179
198
|
];
|
|
180
199
|
// When
|
|
181
200
|
const renderInstance = render(React.createElement(ConcurrentOutput, { processes: processes, abortSignal: new AbortController().signal }));
|
|
@@ -186,19 +205,22 @@ describe('ConcurrentOutput', () => {
|
|
|
186
205
|
expect(logColumns.length).toBe(3);
|
|
187
206
|
// 4 is largest prefix, plus spacing
|
|
188
207
|
expect(logColumns[1]?.length).toBe(4 + 2);
|
|
208
|
+
gate.resolve();
|
|
189
209
|
});
|
|
190
210
|
test('does not render prefix column larger than max', async () => {
|
|
191
211
|
// Given
|
|
192
212
|
const processSync = new Synchronizer();
|
|
213
|
+
const gate = new Synchronizer();
|
|
193
214
|
const processes = [
|
|
194
215
|
{
|
|
195
216
|
prefix: '1',
|
|
196
217
|
action: async (stdout, _stderr, _signal) => {
|
|
197
218
|
stdout.write('foo');
|
|
198
219
|
processSync.resolve();
|
|
220
|
+
await gate.promise;
|
|
199
221
|
},
|
|
200
222
|
},
|
|
201
|
-
{ prefix: new Array(26).join('0'), action: async () =>
|
|
223
|
+
{ prefix: new Array(26).join('0'), action: async () => gate.promise },
|
|
202
224
|
];
|
|
203
225
|
// When
|
|
204
226
|
const renderInstance = render(React.createElement(ConcurrentOutput, { processes: processes, abortSignal: new AbortController().signal }));
|
|
@@ -209,6 +231,7 @@ describe('ConcurrentOutput', () => {
|
|
|
209
231
|
expect(logColumns.length).toBe(3);
|
|
210
232
|
// 25 is largest column allowed, plus spacing
|
|
211
233
|
expect(logColumns[1]?.length).toBe(25 + 2);
|
|
234
|
+
gate.resolve();
|
|
212
235
|
});
|
|
213
236
|
test('rejects with the error thrown inside one of the processes', async () => {
|
|
214
237
|
// Given
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConcurrentOutput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ConcurrentOutput, useConcurrentOutputContext} from './ConcurrentOutput.js'\nimport {render, waitForContent} from '../../testing/ui.js'\nimport {AbortController, AbortSignal} from '../../../../public/node/abort.js'\nimport {unstyled} from '../../../../public/node/output.js'\n\nimport React from 'react'\nimport {describe, expect, test} from 'vitest'\n\nimport {Writable} from 'stream'\n\n/**\n * ConcurrentOutput tests are unreliable unless we await a promise that resolves after the process has written to stdout.\n */\nclass Synchronizer {\n resolve: () => void\n promise: Promise<void>\n\n constructor() {\n this.resolve = () => {}\n this.promise = new Promise<void>((resolve, _reject) => {\n this.resolve = resolve\n })\n }\n}\n\ndescribe('ConcurrentOutput', () => {\n test('renders a stream of concurrent outputs from sub-processes', async () => {\n // Given\n const backendSync = new Synchronizer()\n const frontendSync = new Synchronizer()\n\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n backendSync.resolve()\n },\n }\n\n const frontendProcess = {\n prefix: 'frontend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n await backendSync.promise\n\n stdout.write('first frontend message')\n stdout.write('second frontend message')\n stdout.write('third frontend message')\n\n frontendSync.resolve()\n },\n }\n // When\n\n const renderInstance = render(\n <ConcurrentOutput processes={[backendProcess, frontendProcess]} abortSignal={new AbortController().signal} />,\n )\n\n await frontendSync.promise\n // Wait for React 19 to render the process output\n await waitForContent(renderInstance, 'third frontend message')\n\n // Then\n expect(unstyled(renderInstance.lastFrame()!.replace(/\\d/g, '0'))).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n 00:00:00 │ frontend │ first frontend message\n 00:00:00 │ frontend │ second frontend message\n 00:00:00 │ frontend │ third frontend message\n \"\n `)\n })\n\n test('strips ansi codes from the output by default', async () => {\n const output = 'foo'\n\n // Given\n const processSync = new Synchronizer()\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write(`\\u001b[32m${output}\\u001b[39m`)\n processSync.resolve()\n },\n },\n ]\n\n // When\n const renderInstance = render(<ConcurrentOutput processes={processes} abortSignal={new AbortController().signal} />)\n await processSync.promise\n await waitForContent(renderInstance, output)\n\n // Then\n const logColumns = renderInstance.lastFrame()!.split('│')\n expect(logColumns.length).toBe(3)\n expect(logColumns[2]?.trim()).toEqual(output)\n })\n\n test('does not strip ansi codes from the output when stripAnsi is false', async () => {\n const output = '\\u001b[32mfoo\\u001b[39m'\n\n // Given\n const processSync = new Synchronizer()\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n useConcurrentOutputContext({stripAnsi: false}, () => {\n stdout.write(output)\n })\n processSync.resolve()\n },\n },\n ]\n\n // When\n const renderInstance = render(<ConcurrentOutput processes={processes} abortSignal={new AbortController().signal} />)\n await processSync.promise\n await waitForContent(renderInstance, 'foo')\n\n // Then\n const logColumns = renderInstance.lastFrame()!.split('│')\n expect(logColumns.length).toBe(3)\n expect(logColumns[2]?.trim()).toEqual(output)\n })\n\n test('renders custom prefixes on log lines', async () => {\n // Given\n const processSync = new Synchronizer()\n const extensionName = 'my-extension'\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n useConcurrentOutputContext({outputPrefix: extensionName}, () => {\n stdout.write('foo bar')\n })\n processSync.resolve()\n },\n },\n ]\n\n // When\n const renderInstance = render(\n <ConcurrentOutput\n processes={processes}\n // Ensure it's not truncated\n prefixColumnSize={extensionName.length}\n abortSignal={new AbortController().signal}\n />,\n )\n\n await processSync.promise\n await waitForContent(renderInstance, 'foo bar')\n\n // Then\n const logColumns = unstyled(renderInstance.lastFrame()!).split('│')\n expect(logColumns.length).toBe(3)\n expect(logColumns[1]?.trim()).toEqual(extensionName)\n })\n\n test('renders prefix column width based on prefixColumnSize', async () => {\n // Given\n const processSync1 = new Synchronizer()\n const processSync2 = new Synchronizer()\n\n const columnSize = 5\n const processes = [\n {\n prefix: '1234567890',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('foo')\n processSync1.resolve()\n },\n },\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('bar')\n processSync2.resolve()\n },\n },\n ]\n\n // When\n const renderInstance = render(\n <ConcurrentOutput\n processes={processes}\n prefixColumnSize={columnSize}\n abortSignal={new AbortController().signal}\n />,\n )\n await Promise.all([processSync1.promise, processSync2.promise])\n await waitForContent(renderInstance, 'bar')\n\n // Then\n const logLines = unstyled(renderInstance.lastFrame()!).split('\\n').filter(Boolean)\n expect(logLines.length).toBe(2)\n logLines.forEach((line) => {\n const logColumns = line.split('│')\n expect(logColumns.length).toBe(3)\n // Including spacing\n expect(logColumns[1]?.length).toBe(columnSize + 2)\n })\n })\n\n test('renders prefix column width based on processes by default', async () => {\n // Given\n const processSync = new Synchronizer()\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('foo')\n processSync.resolve()\n },\n },\n {prefix: '12', action: async () => {}},\n {prefix: '123', action: async () => {}},\n {prefix: '1234', action: async () => {}},\n ]\n\n // When\n const renderInstance = render(<ConcurrentOutput processes={processes} abortSignal={new AbortController().signal} />)\n await processSync.promise\n await waitForContent(renderInstance, 'foo')\n\n // Then\n const logColumns = unstyled(renderInstance.lastFrame()!).split('│')\n expect(logColumns.length).toBe(3)\n // 4 is largest prefix, plus spacing\n expect(logColumns[1]?.length).toBe(4 + 2)\n })\n\n test('does not render prefix column larger than max', async () => {\n // Given\n const processSync = new Synchronizer()\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('foo')\n processSync.resolve()\n },\n },\n {prefix: new Array(26).join('0'), action: async () => {}},\n ]\n\n // When\n const renderInstance = render(<ConcurrentOutput processes={processes} abortSignal={new AbortController().signal} />)\n await processSync.promise\n await waitForContent(renderInstance, 'foo')\n\n // Then\n const logColumns = unstyled(renderInstance.lastFrame()!).split('│')\n expect(logColumns.length).toBe(3)\n // 25 is largest column allowed, plus spacing\n expect(logColumns[1]?.length).toBe(25 + 2)\n })\n\n test('rejects with the error thrown inside one of the processes', async () => {\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n throw new Error('something went wrong')\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput processes={[backendProcess]} abortSignal={new AbortController().signal} />,\n )\n\n const renderPromise = renderInstance.waitUntilExit()\n\n await expect(renderPromise).rejects.toThrowError('something went wrong')\n expect(renderPromise.isRejected()).toBe(true)\n })\n\n test(\"doesn't reject when an error is thrown inside one of the processes and keepRunningAfterProcessesResolve is true\", async () => {\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n throw new Error('something went wrong')\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortSignal={new AbortController().signal}\n keepRunningAfterProcessesResolve\n />,\n )\n\n await new Promise((resolve) => setTimeout(resolve, 10))\n expect(renderInstance.waitUntilExit().isRejected()).toBe(false)\n })\n\n test('render promise resolves when all processes resolve by default', async () => {\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n },\n }\n\n // When\n const renderInstance = render(\n <ConcurrentOutput processes={[backendProcess]} abortSignal={new AbortController().signal} />,\n )\n\n const renderPromise = renderInstance.waitUntilExit()\n\n await renderPromise\n expect(renderPromise.isFulfilled()).toBe(true)\n })\n\n test(\"render promise doesn't resolve when all processes resolve and keepRunningAfterProcessesResolve is true\", async () => {\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n },\n }\n\n // When\n const renderInstance = render(\n <ConcurrentOutput\n keepRunningAfterProcessesResolve\n processes={[backendProcess]}\n abortSignal={new AbortController().signal}\n />,\n )\n\n await new Promise((resolve) => setTimeout(resolve, 10))\n\n expect(renderInstance.waitUntilExit().isFulfilled()).toBe(false)\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"ConcurrentOutput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {ConcurrentOutput, useConcurrentOutputContext} from './ConcurrentOutput.js'\nimport {render, waitForContent} from '../../testing/ui.js'\nimport {AbortController, AbortSignal} from '../../../../public/node/abort.js'\nimport {unstyled} from '../../../../public/node/output.js'\n\nimport React from 'react'\nimport {describe, expect, test} from 'vitest'\n\nimport {Writable} from 'stream'\n\n/**\n * ConcurrentOutput tests are unreliable unless we await a promise that resolves after the process has written to stdout.\n */\nclass Synchronizer {\n resolve: () => void\n promise: Promise<void>\n\n constructor() {\n this.resolve = () => {}\n this.promise = new Promise<void>((resolve, _reject) => {\n this.resolve = resolve\n })\n }\n}\n\ndescribe('ConcurrentOutput', () => {\n test('renders a stream of concurrent outputs from sub-processes', async () => {\n // Given\n const backendSync = new Synchronizer()\n const frontendSync = new Synchronizer()\n const gate = new Synchronizer()\n\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n backendSync.resolve()\n await gate.promise\n },\n }\n\n const frontendProcess = {\n prefix: 'frontend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n await backendSync.promise\n\n stdout.write('first frontend message')\n stdout.write('second frontend message')\n stdout.write('third frontend message')\n\n frontendSync.resolve()\n await gate.promise\n },\n }\n // When\n\n const renderInstance = render(\n <ConcurrentOutput processes={[backendProcess, frontendProcess]} abortSignal={new AbortController().signal} />,\n )\n\n await frontendSync.promise\n // Wait for React 19 to render the process output\n await waitForContent(renderInstance, 'third frontend message')\n\n // Then\n expect(unstyled(renderInstance.lastFrame()!.replace(/\\d/g, '0'))).toMatchInlineSnapshot(`\n \"00:00:00 │ backend │ first backend message\n 00:00:00 │ backend │ second backend message\n 00:00:00 │ backend │ third backend message\n 00:00:00 │ frontend │ first frontend message\n 00:00:00 │ frontend │ second frontend message\n 00:00:00 │ frontend │ third frontend message\n \"\n `)\n\n gate.resolve()\n })\n\n test('strips ansi codes from the output by default', async () => {\n const output = 'foo'\n\n // Given\n const processSync = new Synchronizer()\n const gate = new Synchronizer()\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write(`\\u001b[32m${output}\\u001b[39m`)\n processSync.resolve()\n await gate.promise\n },\n },\n ]\n\n // When\n const renderInstance = render(<ConcurrentOutput processes={processes} abortSignal={new AbortController().signal} />)\n await processSync.promise\n await waitForContent(renderInstance, output)\n\n // Then\n const logColumns = renderInstance.lastFrame()!.split('│')\n expect(logColumns.length).toBe(3)\n expect(logColumns[2]?.trim()).toEqual(output)\n gate.resolve()\n })\n\n test('does not strip ansi codes from the output when stripAnsi is false', async () => {\n const output = '\\u001b[32mfoo\\u001b[39m'\n\n // Given\n const processSync = new Synchronizer()\n const gate = new Synchronizer()\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n useConcurrentOutputContext({stripAnsi: false}, () => {\n stdout.write(output)\n })\n processSync.resolve()\n await gate.promise\n },\n },\n ]\n\n // When\n const renderInstance = render(<ConcurrentOutput processes={processes} abortSignal={new AbortController().signal} />)\n await processSync.promise\n await waitForContent(renderInstance, 'foo')\n\n // Then\n const logColumns = renderInstance.lastFrame()!.split('│')\n expect(logColumns.length).toBe(3)\n expect(logColumns[2]?.trim()).toEqual(output)\n gate.resolve()\n })\n\n test('renders custom prefixes on log lines', async () => {\n // Given\n const processSync = new Synchronizer()\n const gate = new Synchronizer()\n const extensionName = 'my-extension'\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n useConcurrentOutputContext({outputPrefix: extensionName}, () => {\n stdout.write('foo bar')\n })\n processSync.resolve()\n await gate.promise\n },\n },\n ]\n\n // When\n const renderInstance = render(\n <ConcurrentOutput\n processes={processes}\n // Ensure it's not truncated\n prefixColumnSize={extensionName.length}\n abortSignal={new AbortController().signal}\n />,\n )\n\n await processSync.promise\n await waitForContent(renderInstance, 'foo bar')\n\n // Then\n const logColumns = unstyled(renderInstance.lastFrame()!).split('│')\n expect(logColumns.length).toBe(3)\n expect(logColumns[1]?.trim()).toEqual(extensionName)\n gate.resolve()\n })\n\n test('renders prefix column width based on prefixColumnSize', async () => {\n // Given\n const processSync1 = new Synchronizer()\n const processSync2 = new Synchronizer()\n const gate = new Synchronizer()\n\n const columnSize = 5\n const processes = [\n {\n prefix: '1234567890',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('foo')\n processSync1.resolve()\n await gate.promise\n },\n },\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('bar')\n processSync2.resolve()\n await gate.promise\n },\n },\n ]\n\n // When\n const renderInstance = render(\n <ConcurrentOutput\n processes={processes}\n prefixColumnSize={columnSize}\n abortSignal={new AbortController().signal}\n />,\n )\n await Promise.all([processSync1.promise, processSync2.promise])\n await waitForContent(renderInstance, 'bar')\n\n // Then\n const logLines = unstyled(renderInstance.lastFrame()!).split('\\n').filter(Boolean)\n expect(logLines.length).toBe(2)\n logLines.forEach((line) => {\n const logColumns = line.split('│')\n expect(logColumns.length).toBe(3)\n // Including spacing\n expect(logColumns[1]?.length).toBe(columnSize + 2)\n })\n gate.resolve()\n })\n\n test('renders prefix column width based on processes by default', async () => {\n // Given\n const processSync = new Synchronizer()\n const gate = new Synchronizer()\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('foo')\n processSync.resolve()\n await gate.promise\n },\n },\n {prefix: '12', action: async () => gate.promise},\n {prefix: '123', action: async () => gate.promise},\n {prefix: '1234', action: async () => gate.promise},\n ]\n\n // When\n const renderInstance = render(<ConcurrentOutput processes={processes} abortSignal={new AbortController().signal} />)\n await processSync.promise\n await waitForContent(renderInstance, 'foo')\n\n // Then\n const logColumns = unstyled(renderInstance.lastFrame()!).split('│')\n expect(logColumns.length).toBe(3)\n // 4 is largest prefix, plus spacing\n expect(logColumns[1]?.length).toBe(4 + 2)\n gate.resolve()\n })\n\n test('does not render prefix column larger than max', async () => {\n // Given\n const processSync = new Synchronizer()\n const gate = new Synchronizer()\n const processes = [\n {\n prefix: '1',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('foo')\n processSync.resolve()\n await gate.promise\n },\n },\n {prefix: new Array(26).join('0'), action: async () => gate.promise},\n ]\n\n // When\n const renderInstance = render(<ConcurrentOutput processes={processes} abortSignal={new AbortController().signal} />)\n await processSync.promise\n await waitForContent(renderInstance, 'foo')\n\n // Then\n const logColumns = unstyled(renderInstance.lastFrame()!).split('│')\n expect(logColumns.length).toBe(3)\n // 25 is largest column allowed, plus spacing\n expect(logColumns[1]?.length).toBe(25 + 2)\n gate.resolve()\n })\n\n test('rejects with the error thrown inside one of the processes', async () => {\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n throw new Error('something went wrong')\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput processes={[backendProcess]} abortSignal={new AbortController().signal} />,\n )\n\n const renderPromise = renderInstance.waitUntilExit()\n\n await expect(renderPromise).rejects.toThrowError('something went wrong')\n expect(renderPromise.isRejected()).toBe(true)\n })\n\n test(\"doesn't reject when an error is thrown inside one of the processes and keepRunningAfterProcessesResolve is true\", async () => {\n // Given\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n\n throw new Error('something went wrong')\n },\n }\n\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess]}\n abortSignal={new AbortController().signal}\n keepRunningAfterProcessesResolve\n />,\n )\n\n await new Promise((resolve) => setTimeout(resolve, 10))\n expect(renderInstance.waitUntilExit().isRejected()).toBe(false)\n })\n\n test('render promise resolves when all processes resolve by default', async () => {\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n },\n }\n\n // When\n const renderInstance = render(\n <ConcurrentOutput processes={[backendProcess]} abortSignal={new AbortController().signal} />,\n )\n\n const renderPromise = renderInstance.waitUntilExit()\n\n await renderPromise\n expect(renderPromise.isFulfilled()).toBe(true)\n })\n\n test(\"render promise doesn't resolve when all processes resolve and keepRunningAfterProcessesResolve is true\", async () => {\n const backendProcess = {\n prefix: 'backend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n stdout.write('first backend message')\n stdout.write('second backend message')\n stdout.write('third backend message')\n },\n }\n\n // When\n const renderInstance = render(\n <ConcurrentOutput\n keepRunningAfterProcessesResolve\n processes={[backendProcess]}\n abortSignal={new AbortController().signal}\n />,\n )\n\n await new Promise((resolve) => setTimeout(resolve, 10))\n\n expect(renderInstance.waitUntilExit().isFulfilled()).toBe(false)\n })\n})\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { InlineToken, TokenItem } from './TokenizedText.js';
|
|
1
2
|
import { InfoTableProps } from './Prompts/InfoTable.js';
|
|
2
3
|
import { AbortSignal } from '../../../../public/node/abort.js';
|
|
3
4
|
import { FunctionComponent } from 'react';
|
|
@@ -5,6 +6,7 @@ export interface DangerousConfirmationPromptProps {
|
|
|
5
6
|
message: string;
|
|
6
7
|
confirmation: string;
|
|
7
8
|
infoTable?: InfoTableProps['table'];
|
|
9
|
+
warningItem?: TokenItem<InlineToken>;
|
|
8
10
|
onSubmit: (value: boolean) => void;
|
|
9
11
|
abortSignal?: AbortSignal;
|
|
10
12
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { TextInput } from './TextInput.js';
|
|
2
2
|
import { TokenizedText } from './TokenizedText.js';
|
|
3
3
|
import { InfoTable } from './Prompts/InfoTable.js';
|
|
4
|
-
import { handleCtrlC } from '../../ui.js';
|
|
4
|
+
import { handleCtrlC, useComplete } from '../../ui.js';
|
|
5
5
|
import useLayout from '../hooks/use-layout.js';
|
|
6
6
|
import { messageWithPunctuation } from '../utilities.js';
|
|
7
7
|
import useAbortSignal from '../hooks/use-abort-signal.js';
|
|
8
8
|
import usePrompt, { PromptState } from '../hooks/use-prompt.js';
|
|
9
9
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
10
|
-
import { Box,
|
|
10
|
+
import { Box, useInput, Text } from 'ink';
|
|
11
11
|
import figures from 'figures';
|
|
12
|
-
const DangerousConfirmationPrompt = ({ message, confirmation, infoTable, onSubmit, abortSignal, }) => {
|
|
12
|
+
const DangerousConfirmationPrompt = ({ message, confirmation, infoTable, warningItem, onSubmit, abortSignal, }) => {
|
|
13
13
|
const validateAnswer = useCallback((value) => {
|
|
14
14
|
return value === confirmation ? undefined : ['Value must be exactly', { userInput: confirmation }];
|
|
15
15
|
}, [confirmation]);
|
|
@@ -17,7 +17,7 @@ const DangerousConfirmationPrompt = ({ message, confirmation, infoTable, onSubmi
|
|
|
17
17
|
const { promptState, setPromptState, answer, setAnswer } = usePrompt({
|
|
18
18
|
initialAnswer: '',
|
|
19
19
|
});
|
|
20
|
-
const
|
|
20
|
+
const complete = useComplete();
|
|
21
21
|
const [error, setError] = useState(undefined);
|
|
22
22
|
const color = promptState === PromptState.Error ? 'red' : 'cyan';
|
|
23
23
|
const underline = new Array(oneThird - 3).fill('▔');
|
|
@@ -42,13 +42,13 @@ const DangerousConfirmationPrompt = ({ message, confirmation, infoTable, onSubmi
|
|
|
42
42
|
useEffect(() => {
|
|
43
43
|
if (promptState === PromptState.Submitted) {
|
|
44
44
|
onSubmit(true);
|
|
45
|
-
|
|
45
|
+
complete();
|
|
46
46
|
}
|
|
47
47
|
else if (promptState === PromptState.Cancelled) {
|
|
48
48
|
onSubmit(false);
|
|
49
|
-
|
|
49
|
+
complete();
|
|
50
50
|
}
|
|
51
|
-
}, [onSubmit, promptState,
|
|
51
|
+
}, [onSubmit, promptState, complete]);
|
|
52
52
|
const completed = promptState === PromptState.Submitted || promptState === PromptState.Cancelled;
|
|
53
53
|
return isAborted ? null : (React.createElement(Box, { flexDirection: "column", marginBottom: 1, width: twoThirds },
|
|
54
54
|
React.createElement(Box, null,
|
|
@@ -59,6 +59,11 @@ const DangerousConfirmationPrompt = ({ message, confirmation, infoTable, onSubmi
|
|
|
59
59
|
React.createElement(Box, { flexDirection: "column", gap: 1, marginTop: 1, marginLeft: 3 },
|
|
60
60
|
infoTable ? (React.createElement(Box, { paddingLeft: 2, borderStyle: "bold", borderLeft: true, borderRight: false, borderTop: false, borderBottom: false, flexDirection: "column", gap: 1 },
|
|
61
61
|
React.createElement(InfoTable, { table: infoTable }))) : null,
|
|
62
|
+
warningItem ? (React.createElement(Box, { flexDirection: "column" },
|
|
63
|
+
React.createElement(Text, { color: "red" },
|
|
64
|
+
figures.warning,
|
|
65
|
+
" WARNING"),
|
|
66
|
+
React.createElement(TokenizedText, { item: warningItem }))) : null,
|
|
62
67
|
React.createElement(Box, null,
|
|
63
68
|
React.createElement(TokenizedText, { item: ['Type', { userInput: confirmation }, 'to confirm, or press Escape to cancel.'] }))),
|
|
64
69
|
React.createElement(Box, { flexDirection: "column", width: oneThird },
|