@shopify/cli-kit 3.45.0-pre.3 → 3.45.0-pre.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/private/node/ui/components/Alert.test.js +7 -4
- package/dist/private/node/ui/components/Alert.test.js.map +1 -1
- package/dist/private/node/ui/components/Banner.js +32 -5
- package/dist/private/node/ui/components/Banner.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.js +5 -3
- package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
- package/dist/private/node/ui/components/ConcurrentOutput.test.js +8 -8
- package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
- package/dist/private/node/ui/components/FatalError.test.js +2 -2
- package/dist/private/node/ui/components/FatalError.test.js.map +1 -1
- package/dist/private/node/ui/components/Link.js +19 -7
- package/dist/private/node/ui/components/Link.js.map +1 -1
- package/dist/private/node/ui/components/SelectPrompt.js +1 -1
- package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
- package/dist/private/node/ui/components/Tasks.d.ts +2 -1
- package/dist/private/node/ui/components/Tasks.js +6 -2
- package/dist/private/node/ui/components/Tasks.js.map +1 -1
- package/dist/private/node/ui/components/Tasks.test.js +16 -0
- package/dist/private/node/ui/components/Tasks.test.js.map +1 -1
- package/dist/private/node/ui/components/TextPrompt.d.ts +1 -0
- package/dist/private/node/ui/components/TextPrompt.js +3 -3
- package/dist/private/node/ui/components/TextPrompt.js.map +1 -1
- package/dist/private/node/ui/contexts/LinksContext.d.ts +12 -0
- package/dist/private/node/ui/contexts/LinksContext.js +3 -0
- package/dist/private/node/ui/contexts/LinksContext.js.map +1 -0
- 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/ruby.js +10 -7
- package/dist/public/node/ruby.js.map +1 -1
- package/dist/public/node/tcp.d.ts +6 -0
- package/dist/public/node/tcp.js +8 -0
- package/dist/public/node/tcp.js.map +1 -1
- package/dist/public/node/ui.d.ts +26 -18
- package/dist/public/node/ui.js +31 -20
- package/dist/public/node/ui.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ To work with themes, the CLI needs to be installed globally with:
|
|
|
36
36
|
|
|
37
37
|
- `npm install -g @shopify/cli @shopify/theme`
|
|
38
38
|
|
|
39
|
-
You can also use do it through Homebrew on macOS: `brew install shopify-cli`
|
|
39
|
+
You can also use do it through Homebrew on macOS: `brew tap shopify/shopify && brew install shopify-cli`
|
|
40
40
|
|
|
41
41
|
Learn more in the docs: [Shopify CLI for themes](https://shopify.dev/docs/themes/tools/cli)
|
|
42
42
|
|
|
@@ -48,7 +48,7 @@ describe('Alert', async () => {
|
|
|
48
48
|
],
|
|
49
49
|
link: {
|
|
50
50
|
label: 'Link',
|
|
51
|
-
url: 'https://
|
|
51
|
+
url: 'https://www.google.com/search?q=jh56t9l34kpo35tw8s28hn7s9s2xvzla01d8cn6j7yq&rlz=1C1GCEU_enUS832US832&oq=jh56t9l34kpo35tw8s28hn7s9s2xvzla01d8cn6j7yq&aqs=chrome.0.35i39l2j0l4j46j69i60.2711j0j7&sourceid=chrome&ie=UTF-8',
|
|
52
52
|
},
|
|
53
53
|
customSections: [
|
|
54
54
|
{
|
|
@@ -84,10 +84,9 @@ describe('Alert', async () => {
|
|
|
84
84
|
│ │
|
|
85
85
|
│ Reference │
|
|
86
86
|
│ • Run \`npm shopify help\` │
|
|
87
|
-
│ • Press 'return' to open the really amazing and clean dev docs
|
|
88
|
-
│ https://shopify.dev ) │
|
|
87
|
+
│ • Press 'return' to open the really amazing and clean dev docs [1] │
|
|
89
88
|
│ │
|
|
90
|
-
│ Link
|
|
89
|
+
│ Link [2] │
|
|
91
90
|
│ │
|
|
92
91
|
│ Custom section │
|
|
93
92
|
│ • Item 1 │
|
|
@@ -100,6 +99,10 @@ describe('Alert', async () => {
|
|
|
100
99
|
│ • Item 3 │
|
|
101
100
|
│ │
|
|
102
101
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
102
|
+
[1] https://shopify.dev
|
|
103
|
+
[2] https://www.google.com/search?q=jh56t9l34kpo35tw8s28hn7s9s2xvzla01d8cn6j7yq&rlz=1C1GCEU_enUS832U
|
|
104
|
+
S832&oq=jh56t9l34kpo35tw8s28hn7s9s2xvzla01d8cn6j7yq&aqs=chrome.0.35i39l2j0l4j46j69i60.2711j0j7&sourc
|
|
105
|
+
eid=chrome&ie=UTF-8
|
|
103
106
|
"
|
|
104
107
|
`);
|
|
105
108
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Alert.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Alert.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,YAAY,CAAA;AAChC,OAAO,EAAC,QAAQ,EAAC,MAAM,mCAAmC,CAAA;AAC1D,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAE1C,QAAQ,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE;IAC3B,IAAI,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,OAAO,GAAG;YACd,QAAQ,EAAE,CAAC,EAAC,SAAS,EAAE,QAAQ,EAAC,EAAE,iCAAiC,CAAC;YACpE,IAAI,EAAE,CAAC,qCAAqC,EAAE,EAAC,QAAQ,EAAE,MAAM,EAAC,EAAE,SAAS,CAAC;YAC5E,SAAS,EAAE;gBACT;oBACE,KAAK;oBACL;wBACE,OAAO,EAAE,oBAAoB;qBAC9B;iBACF;gBACD;oBACE,8BAA8B;oBAC9B;wBACE,OAAO,EAAE,aAAa;qBACvB;iBACF;gBACD;oBACE,wBAAwB;oBACxB;wBACE,OAAO,EAAE,wBAAwB;qBAClC;iBACF;aACF;YACD,SAAS,EAAE;gBACT;oBACE,KAAK;oBACL;wBACE,OAAO,EAAE,kBAAkB;qBAC5B;iBACF;gBACD;oBACE,iCAAiC;oBACjC,qDAAqD;oBACrD;wBACE,IAAI,EAAE;4BACJ,KAAK,EAAE,UAAU;4BACjB,GAAG,EAAE,qBAAqB;yBAC3B;qBACF;iBACF;aACF;YACD,IAAI,EAAE;gBACJ,KAAK,EAAE,MAAM;gBACb,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"Alert.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Alert.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {Alert} from './Alert.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\nimport {render} from 'ink-testing-library'\n\ndescribe('Alert', async () => {\n test('renders correctly with all the options', async () => {\n const options = {\n headline: [{userInput: 'my-app'}, 'initialized and ready to build.'],\n body: ['You can find the build files in the', {filePath: 'dist'}, 'folder.'],\n nextSteps: [\n [\n 'Run',\n {\n command: 'cd santorini-goods',\n },\n ],\n [\n 'To preview your project, run',\n {\n command: 'npm app dev',\n },\n ],\n [\n 'To add extensions, run',\n {\n command: 'npm generate extension',\n },\n ],\n ],\n reference: [\n [\n 'Run',\n {\n command: 'npm shopify help',\n },\n ],\n [\n // testing link wrapping behavior\n \"Press 'return' to open the really amazing and clean\",\n {\n link: {\n label: 'dev docs',\n url: 'https://shopify.dev',\n },\n },\n ],\n ],\n link: {\n label: 'Link',\n url: 'https://www.google.com/search?q=jh56t9l34kpo35tw8s28hn7s9s2xvzla01d8cn6j7yq&rlz=1C1GCEU_enUS832US832&oq=jh56t9l34kpo35tw8s28hn7s9s2xvzla01d8cn6j7yq&aqs=chrome.0.35i39l2j0l4j46j69i60.2711j0j7&sourceid=chrome&ie=UTF-8',\n },\n customSections: [\n {\n title: 'Custom section',\n body: {\n list: {\n items: ['Item 1', 'Item 2', 'Item 3'],\n },\n },\n },\n {\n title: 'Custom section 2',\n body: {\n list: {\n items: ['Item 1', 'Item 2', 'Item 3'],\n },\n },\n },\n ],\n }\n\n const {lastFrame} = render(<Alert type=\"info\" {...options} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"╭─ info ───────────────────────────────────────────────────────────────────────╮\n │ │\n │ my-app initialized and ready to build. │\n │ │\n │ You can find the build files in the dist folder. │\n │ │\n │ Next steps │\n │ • Run \\`cd santorini-goods\\` │\n │ • To preview your project, run \\`npm app dev\\` │\n │ • To add extensions, run \\`npm generate extension\\` │\n │ │\n │ Reference │\n │ • Run \\`npm shopify help\\` │\n │ • Press 'return' to open the really amazing and clean dev docs [1] │\n │ │\n │ Link [2] │\n │ │\n │ Custom section │\n │ • Item 1 │\n │ • Item 2 │\n │ • Item 3 │\n │ │\n │ Custom section 2 │\n │ • Item 1 │\n │ • Item 2 │\n │ • Item 3 │\n │ │\n ╰──────────────────────────────────────────────────────────────────────────────╯\n [1] https://shopify.dev\n [2] https://www.google.com/search?q=jh56t9l34kpo35tw8s28hn7s9s2xvzla01d8cn6j7yq&rlz=1C1GCEU_enUS832U\n S832&oq=jh56t9l34kpo35tw8s28hn7s9s2xvzla01d8cn6j7yq&aqs=chrome.0.35i39l2j0l4j46j69i60.2711j0j7&sourc\n eid=chrome&ie=UTF-8\n \"\n `)\n })\n\n test('allows passing just a body', async () => {\n const options = {\n body: 'Title',\n }\n\n const {lastFrame} = render(<Alert type=\"info\" {...options} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"╭─ info ───────────────────────────────────────────────────────────────────────╮\n │ │\n │ Title │\n │ │\n ╰──────────────────────────────────────────────────────────────────────────────╯\n \"\n `)\n })\n\n test('has the headline in bold', async () => {\n const options = {\n headline: 'Title',\n }\n\n const {lastFrame} = render(<Alert type=\"info\" {...options} />)\n\n expect(lastFrame()).toMatchInlineSnapshot(`\n \"\u001b[2m╭─\u001b[22m info \u001b[2m───────────────────────────────────────────────────────────────────────╮\u001b[22m\n \u001b[2m│\u001b[22m \u001b[2m│\u001b[22m\n \u001b[2m│\u001b[22m \u001b[1mTitle\u001b[22m \u001b[2m│\u001b[22m\n \u001b[2m│\u001b[22m \u001b[2m│\u001b[22m\n \u001b[2m╰──────────────────────────────────────────────────────────────────────────────╯\u001b[22m\n \"\n `)\n })\n})\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import useLayout from '../hooks/use-layout.js';
|
|
2
|
+
import { LinksContext } from '../contexts/LinksContext.js';
|
|
2
3
|
import { Box, Text } from 'ink';
|
|
3
|
-
import React from 'react';
|
|
4
|
+
import React, { useContext, useRef } from 'react';
|
|
4
5
|
function typeToColor(type) {
|
|
5
6
|
return {
|
|
6
7
|
success: 'green',
|
|
@@ -10,12 +11,38 @@ function typeToColor(type) {
|
|
|
10
11
|
external_error: 'red',
|
|
11
12
|
}[type];
|
|
12
13
|
}
|
|
14
|
+
const Footnotes = () => {
|
|
15
|
+
const linksContext = useContext(LinksContext);
|
|
16
|
+
if (linksContext === null || linksContext.links.current === null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
const links = linksContext.links.current;
|
|
20
|
+
const linkIds = Object.keys(links);
|
|
21
|
+
return linkIds.length > 0 ? (React.createElement(Box, { marginBottom: 1, marginTop: -1, flexDirection: "column" }, linkIds.map((id) => (React.createElement(Text, { key: id }, `[${id}] ${links[id]?.url}`))))) : null;
|
|
22
|
+
};
|
|
13
23
|
const BoxWithBorder = ({ type, children }) => {
|
|
14
24
|
const { twoThirds } = useLayout();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
const links = useRef({});
|
|
26
|
+
return (React.createElement(LinksContext.Provider, { value: {
|
|
27
|
+
links,
|
|
28
|
+
addLink: (label, url) => {
|
|
29
|
+
const id = Object.keys(links.current).find((id) => links.current[id].url === url);
|
|
30
|
+
if (id) {
|
|
31
|
+
return id;
|
|
32
|
+
}
|
|
33
|
+
const newId = (Object.keys(links.current).length + 1).toString();
|
|
34
|
+
links.current = {
|
|
35
|
+
...links.current,
|
|
36
|
+
[newId]: { label, url },
|
|
37
|
+
};
|
|
38
|
+
return newId;
|
|
39
|
+
},
|
|
40
|
+
} },
|
|
41
|
+
React.createElement(Box, { width: twoThirds, paddingY: 1, paddingX: 2, marginBottom: 1, borderStyle: "round", flexDirection: "column", borderColor: typeToColor(type) },
|
|
42
|
+
React.createElement(Box, { marginTop: -2, marginBottom: 1, marginLeft: -1 },
|
|
43
|
+
React.createElement(Text, null, ` ${type.replace(/_/g, ' ')} `)),
|
|
44
|
+
children),
|
|
45
|
+
React.createElement(Footnotes, null)));
|
|
19
46
|
};
|
|
20
47
|
const BoxWithTopBottomLines = ({ type, children }) => {
|
|
21
48
|
const { twoThirds } = useLayout();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Banner.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Banner.tsx"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,wBAAwB,CAAA;AAC9C,OAAO,EAAC,GAAG,EAAE,IAAI,EAAC,MAAM,KAAK,CAAA;AAC7B,OAAO,
|
|
1
|
+
{"version":3,"file":"Banner.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Banner.tsx"],"names":[],"mappings":"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","sourcesContent":["import useLayout from '../hooks/use-layout.js'\nimport {Link, LinksContext} from '../contexts/LinksContext.js'\nimport {Box, Text} from 'ink'\nimport React, {FunctionComponent, useContext, useRef} from 'react'\n\nexport type BannerType = 'success' | 'error' | 'warning' | 'info' | 'external_error'\n\ninterface BannerProps {\n type: BannerType\n}\n\nfunction typeToColor(type: BannerProps['type']) {\n return {\n success: 'green',\n error: 'red',\n warning: 'yellow',\n info: 'dim',\n external_error: 'red',\n }[type]\n}\n\nconst Footnotes = () => {\n const linksContext = useContext(LinksContext)\n\n if (linksContext === null || linksContext.links.current === null) {\n return null\n }\n\n const links = linksContext.links.current\n const linkIds = Object.keys(links)\n\n return linkIds.length > 0 ? (\n <Box marginBottom={1} marginTop={-1} flexDirection=\"column\">\n {linkIds.map((id) => (\n <Text key={id}>{`[${id}] ${links[id]?.url}`}</Text>\n ))}\n </Box>\n ) : null\n}\n\nconst BoxWithBorder: FunctionComponent<BannerProps> = ({type, children}) => {\n const {twoThirds} = useLayout()\n const links = useRef<{[key: string]: Link}>({})\n\n return (\n <LinksContext.Provider\n value={{\n links,\n addLink: (label, url) => {\n const id: string | undefined = Object.keys(links.current).find((id) => links.current[id]!.url === url)\n if (id) {\n return id\n }\n const newId = (Object.keys(links.current).length + 1).toString()\n links.current = {\n ...links.current,\n [newId]: {label, url},\n }\n return newId\n },\n }}\n >\n <Box\n width={twoThirds}\n paddingY={1}\n paddingX={2}\n marginBottom={1}\n borderStyle=\"round\"\n flexDirection=\"column\"\n borderColor={typeToColor(type)}\n >\n <Box marginTop={-2} marginBottom={1} marginLeft={-1}>\n <Text>{` ${type.replace(/_/g, ' ')} `}</Text>\n </Box>\n {children}\n </Box>\n <Footnotes />\n </LinksContext.Provider>\n )\n}\n\nconst BoxWithTopBottomLines: FunctionComponent<BannerProps> = ({type, children}) => {\n const {twoThirds} = useLayout()\n // 2 initial dashes + 2 spaces surrounding the type\n let topLineAfterTypeLength = twoThirds - 2 - type.length - 2\n if (topLineAfterTypeLength < 0) topLineAfterTypeLength = 0\n\n return (\n <Box flexDirection=\"column\" marginBottom={1}>\n <Box marginBottom={1}>\n <Text>\n <Text color={typeToColor(type)}>{'─'.repeat(2)}</Text>\n <Text>{` ${type.replace(/_/g, ' ')} `}</Text>\n <Text color={typeToColor(type)}>{'─'.repeat(topLineAfterTypeLength)}</Text>\n </Text>\n </Box>\n\n {children}\n\n <Box marginTop={1}>\n <Text color={typeToColor(type)}>{'─'.repeat(twoThirds)}</Text>\n </Box>\n </Box>\n )\n}\n\nconst Banner: FunctionComponent<BannerProps> = ({children, ...props}) => {\n if (props.type === 'external_error') {\n return React.createElement(BoxWithTopBottomLines, props, children)\n } else {\n return React.createElement(BoxWithBorder, props, children)\n }\n}\n\nexport {Banner}\n"]}
|
|
@@ -81,10 +81,10 @@ const ConcurrentOutput = ({ processes, abortController, showTimestamps = true, o
|
|
|
81
81
|
showTimestamps ? (React.createElement(Box, null,
|
|
82
82
|
React.createElement(Box, { marginRight: 1 },
|
|
83
83
|
React.createElement(Text, { color: chunk.color }, new Date().toISOString().replace(/T/, ' ').replace(/\..+/, ''))),
|
|
84
|
-
React.createElement(Text, { bold: true, color: chunk.color },
|
|
84
|
+
React.createElement(Text, { bold: true, color: chunk.color }, figures.lineVertical))) : null,
|
|
85
85
|
React.createElement(Box, { width: prefixColumnSize, marginX: 1 },
|
|
86
86
|
React.createElement(Text, { color: chunk.color }, chunk.prefix)),
|
|
87
|
-
React.createElement(Text, { bold: true, color: chunk.color },
|
|
87
|
+
React.createElement(Text, { bold: true, color: chunk.color }, figures.lineVertical),
|
|
88
88
|
React.createElement(Box, { flexGrow: 1, paddingLeft: 1 },
|
|
89
89
|
React.createElement(Text, { color: chunk.color }, line)))))));
|
|
90
90
|
}),
|
|
@@ -93,7 +93,9 @@ const ConcurrentOutput = ({ processes, abortController, showTimestamps = true, o
|
|
|
93
93
|
figures.pointerSmall,
|
|
94
94
|
" Press ",
|
|
95
95
|
React.createElement(Text, { bold: true }, shortcut.key),
|
|
96
|
-
"
|
|
96
|
+
" ",
|
|
97
|
+
figures.lineVertical,
|
|
98
|
+
" ",
|
|
97
99
|
shortcut.action)))),
|
|
98
100
|
footer.subTitle ? (React.createElement(Box, { marginTop: 1 },
|
|
99
101
|
React.createElement(Text, null, footer.subTitle))) : null)) : null));
|
|
@@ -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 useAsyncAndUnmount from '../hooks/use-async-and-unmount.js'\nimport {AbortController} from '../../../../public/node/abort.js'\nimport {handleCtrlC} from '../../ui.js'\nimport React, {FunctionComponent, useState} from 'react'\nimport {Box, Key, Static, Text, useInput} from 'ink'\nimport stripAnsi from 'strip-ansi'\nimport treeKill from 'tree-kill'\nimport figures from 'figures'\nimport {Writable} from 'stream'\n\nexport type WritableStream = (process: OutputProcess, index: number) => Writable\n\ninterface Shortcut {\n key: string\n action: string\n}\nexport interface ConcurrentOutputProps {\n processes: OutputProcess[]\n abortController: AbortController\n showTimestamps?: boolean\n onInput?: (input: string, key: Key, exit: () => void) => void\n footer?: {\n shortcuts: Shortcut[]\n subTitle?: string\n }\n}\ninterface Chunk {\n color: string\n prefix: string\n lines: string[]\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 abortController,\n showTimestamps = true,\n onInput,\n footer,\n}) => {\n const [processOutput, setProcessOutput] = useState<Chunk[]>([])\n const concurrentColors = ['yellow', 'cyan', 'magenta', 'green', 'blue']\n const prefixColumnSize = Math.max(...processes.map((process) => process.prefix.length))\n\n function lineColor(index: number) {\n const colorIndex = index < concurrentColors.length ? index : index % concurrentColors.length\n return concurrentColors[colorIndex]!\n }\n\n const writableStream = (process: OutputProcess, index: number) => {\n return new Writable({\n write(chunk, _encoding, next) {\n const lines = stripAnsi(chunk.toString('utf8').replace(/(\\n)$/, '')).split(/\\n/)\n\n setProcessOutput((previousProcessOutput) => [\n ...previousProcessOutput,\n {\n color: lineColor(index),\n prefix: process.prefix,\n lines,\n },\n ])\n\n next()\n },\n })\n }\n\n const runProcesses = () => {\n return Promise.all(\n processes.map(async (process, index) => {\n const stdout = writableStream(process, index)\n const stderr = writableStream(process, index)\n\n await process.action(stdout, stderr, abortController.signal)\n }),\n )\n }\n\n useInput(\n (input, key) => {\n handleCtrlC(input, key)\n\n onInput!(input, key, () => treeKill(process.pid, 'SIGINT'))\n },\n {isActive: typeof onInput !== 'undefined'},\n )\n\n useAsyncAndUnmount(runProcesses, {onRejected: () => abortController.abort()})\n\n return (\n <>\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 {showTimestamps ? (\n <Box>\n <Box marginRight={1}>\n <Text color={chunk.color}>\n {new Date().toISOString().replace(/T/, ' ').replace(/\\..+/, '')}\n </Text>\n </Box>\n\n <Text bold color={chunk.color}>\n |\n </Text>\n </Box>\n ) : null}\n\n <Box width={prefixColumnSize} marginX={1}>\n <Text color={chunk.color}>{chunk.prefix}</Text>\n </Box>\n\n <Text bold color={chunk.color}>\n |\n </Text>\n\n <Box flexGrow={1} paddingLeft={1}>\n <Text color={chunk.color}>{line}</Text>\n </Box>\n </Box>\n ))}\n </Box>\n )\n }}\n </Static>\n {footer ? (\n <Box marginY={1} flexDirection=\"column\" flexGrow={1}>\n <Box flexDirection=\"column\">\n {footer.shortcuts.map((shortcut, index) => (\n <Text key={index}>\n {figures.pointerSmall} Press <Text bold>{shortcut.key}</Text> | {shortcut.action}\n </Text>\n ))}\n </Box>\n {footer.subTitle ? (\n <Box marginTop={1}>\n <Text>{footer.subTitle}</Text>\n </Box>\n ) : null}\n </Box>\n ) : null}\n </>\n )\n}\n\nexport {ConcurrentOutput}\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 useAsyncAndUnmount from '../hooks/use-async-and-unmount.js'\nimport {AbortController} from '../../../../public/node/abort.js'\nimport {handleCtrlC} from '../../ui.js'\nimport React, {FunctionComponent, useState} from 'react'\nimport {Box, Key, Static, Text, useInput} from 'ink'\nimport stripAnsi from 'strip-ansi'\nimport treeKill from 'tree-kill'\nimport figures from 'figures'\nimport {Writable} from 'stream'\n\nexport type WritableStream = (process: OutputProcess, index: number) => Writable\n\ninterface Shortcut {\n key: string\n action: string\n}\nexport interface ConcurrentOutputProps {\n processes: OutputProcess[]\n abortController: AbortController\n showTimestamps?: boolean\n onInput?: (input: string, key: Key, exit: () => void) => void\n footer?: {\n shortcuts: Shortcut[]\n subTitle?: string\n }\n}\ninterface Chunk {\n color: string\n prefix: string\n lines: string[]\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 abortController,\n showTimestamps = true,\n onInput,\n footer,\n}) => {\n const [processOutput, setProcessOutput] = useState<Chunk[]>([])\n const concurrentColors = ['yellow', 'cyan', 'magenta', 'green', 'blue']\n const prefixColumnSize = Math.max(...processes.map((process) => process.prefix.length))\n\n function lineColor(index: number) {\n const colorIndex = index < concurrentColors.length ? index : index % concurrentColors.length\n return concurrentColors[colorIndex]!\n }\n\n const writableStream = (process: OutputProcess, index: number) => {\n return new Writable({\n write(chunk, _encoding, next) {\n const lines = stripAnsi(chunk.toString('utf8').replace(/(\\n)$/, '')).split(/\\n/)\n\n setProcessOutput((previousProcessOutput) => [\n ...previousProcessOutput,\n {\n color: lineColor(index),\n prefix: process.prefix,\n lines,\n },\n ])\n\n next()\n },\n })\n }\n\n const runProcesses = () => {\n return Promise.all(\n processes.map(async (process, index) => {\n const stdout = writableStream(process, index)\n const stderr = writableStream(process, index)\n\n await process.action(stdout, stderr, abortController.signal)\n }),\n )\n }\n\n useInput(\n (input, key) => {\n handleCtrlC(input, key)\n\n onInput!(input, key, () => treeKill(process.pid, 'SIGINT'))\n },\n {isActive: typeof onInput !== 'undefined'},\n )\n\n useAsyncAndUnmount(runProcesses, {onRejected: () => abortController.abort()})\n\n return (\n <>\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 {showTimestamps ? (\n <Box>\n <Box marginRight={1}>\n <Text color={chunk.color}>\n {new Date().toISOString().replace(/T/, ' ').replace(/\\..+/, '')}\n </Text>\n </Box>\n\n <Text bold color={chunk.color}>\n {figures.lineVertical}\n </Text>\n </Box>\n ) : null}\n\n <Box width={prefixColumnSize} marginX={1}>\n <Text color={chunk.color}>{chunk.prefix}</Text>\n </Box>\n\n <Text bold color={chunk.color}>\n {figures.lineVertical}\n </Text>\n\n <Box flexGrow={1} paddingLeft={1}>\n <Text color={chunk.color}>{line}</Text>\n </Box>\n </Box>\n ))}\n </Box>\n )\n }}\n </Static>\n {footer ? (\n <Box marginY={1} flexDirection=\"column\" flexGrow={1}>\n <Box flexDirection=\"column\">\n {footer.shortcuts.map((shortcut, index) => (\n <Text key={index}>\n {figures.pointerSmall} Press <Text bold>{shortcut.key}</Text> {figures.lineVertical} {shortcut.action}\n </Text>\n ))}\n </Box>\n {footer.subTitle ? (\n <Box marginTop={1}>\n <Text>{footer.subTitle}</Text>\n </Box>\n ) : null}\n </Box>\n ) : null}\n </>\n )\n}\n\nexport {ConcurrentOutput}\n"]}
|
|
@@ -53,15 +53,15 @@ describe('ConcurrentOutput', () => {
|
|
|
53
53
|
await frontendPromise;
|
|
54
54
|
// Then
|
|
55
55
|
expect(unstyled(getLastFrameAfterUnmount(renderInstance).replace(/\d/g, '0'))).toMatchInlineSnapshot(`
|
|
56
|
-
"0000-00-00 00:00:00
|
|
57
|
-
0000-00-00 00:00:00
|
|
58
|
-
0000-00-00 00:00:00
|
|
59
|
-
0000-00-00 00:00:00
|
|
60
|
-
0000-00-00 00:00:00
|
|
61
|
-
0000-00-00 00:00:00
|
|
56
|
+
"0000-00-00 00:00:00 │ backend │ first backend message
|
|
57
|
+
0000-00-00 00:00:00 │ backend │ second backend message
|
|
58
|
+
0000-00-00 00:00:00 │ backend │ third backend message
|
|
59
|
+
0000-00-00 00:00:00 │ frontend │ first frontend message
|
|
60
|
+
0000-00-00 00:00:00 │ frontend │ second frontend message
|
|
61
|
+
0000-00-00 00:00:00 │ frontend │ third frontend message
|
|
62
62
|
|
|
63
|
-
› Press p
|
|
64
|
-
› Press q
|
|
63
|
+
› Press p │ open your browser
|
|
64
|
+
› Press q │ quit
|
|
65
65
|
|
|
66
66
|
Preview URL: https://shopify.com
|
|
67
67
|
"
|
|
@@ -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} from './ConcurrentOutput.js'\nimport {getLastFrameAfterUnmount, waitForInputsToBeReady} from '../../testing/ui.js'\nimport {AbortController, AbortSignal} from '../../../../public/node/abort.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\nimport {render} from 'ink-testing-library'\nimport {Writable} from 'stream'\n\ndescribe('ConcurrentOutput', () => {\n test('renders a stream of concurrent outputs from sub-processes', async () => {\n // Given\n let backendPromiseResolve: () => void\n let frontendPromiseResolve: () => void\n\n const backendPromise = new Promise<void>(function (resolve, _reject) {\n backendPromiseResolve = resolve\n })\n\n const frontendPromise = new Promise<void>(function (resolve, _reject) {\n frontendPromiseResolve = resolve\n })\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 backendPromiseResolve()\n },\n }\n\n const frontendProcess = {\n prefix: 'frontend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n await backendPromise\n\n stdout.write('first frontend message')\n stdout.write('second frontend message')\n stdout.write('third frontend message')\n\n frontendPromiseResolve()\n },\n }\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess, frontendProcess]}\n abortController={new AbortController()}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'open your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n // wait for all output to be rendered\n await frontendPromise\n\n // Then\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!.replace(/\\d/g, '0'))).toMatchInlineSnapshot(`\n \"0000-00-00 00:00:00
|
|
1
|
+
{"version":3,"file":"ConcurrentOutput.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/ConcurrentOutput.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAC,wBAAwB,EAAE,sBAAsB,EAAC,MAAM,qBAAqB,CAAA;AACpF,OAAO,EAAC,eAAe,EAAc,MAAM,kCAAkC,CAAA;AAC7E,OAAO,EAAC,QAAQ,EAAC,MAAM,mCAAmC,CAAA;AAC1D,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAC,MAAM,QAAQ,CAAA;AACjD,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAG1C,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QAC3E,QAAQ;QACR,IAAI,qBAAiC,CAAA;QACrC,IAAI,sBAAkC,CAAA;QAEtC,MAAM,cAAc,GAAG,IAAI,OAAO,CAAO,UAAU,OAAO,EAAE,OAAO;YACjE,qBAAqB,GAAG,OAAO,CAAA;QACjC,CAAC,CAAC,CAAA;QAEF,MAAM,eAAe,GAAG,IAAI,OAAO,CAAO,UAAU,OAAO,EAAE,OAAO;YAClE,sBAAsB,GAAG,OAAO,CAAA;QAClC,CAAC,CAAC,CAAA;QAEF,MAAM,cAAc,GAAG;YACrB,MAAM,EAAE,SAAS;YACjB,MAAM,EAAE,KAAK,EAAE,MAAgB,EAAE,OAAiB,EAAE,OAAoB,EAAE,EAAE;gBAC1E,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAA;gBACrC,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;gBACtC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAA;gBAErC,qBAAqB,EAAE,CAAA;YACzB,CAAC;SACF,CAAA;QAED,MAAM,eAAe,GAAG;YACtB,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,KAAK,EAAE,MAAgB,EAAE,OAAiB,EAAE,OAAoB,EAAE,EAAE;gBAC1E,MAAM,cAAc,CAAA;gBAEpB,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;gBACtC,MAAM,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAA;gBACvC,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAA;gBAEtC,sBAAsB,EAAE,CAAA;YAC1B,CAAC;SACF,CAAA;QACD,OAAO;QAEP,MAAM,cAAc,GAAG,MAAM,CAC3B,oBAAC,gBAAgB,IACf,SAAS,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC,EAC5C,eAAe,EAAE,IAAI,eAAe,EAAE,EACtC,MAAM,EAAE;gBACN,SAAS,EAAE;oBACT;wBACE,GAAG,EAAE,GAAG;wBACR,MAAM,EAAE,mBAAmB;qBAC5B;oBACD;wBACE,GAAG,EAAE,GAAG;wBACR,MAAM,EAAE,MAAM;qBACf;iBACF;gBACD,QAAQ,EAAE,kCAAkC;aAC7C,GACD,CACH,CAAA;QAED,qCAAqC;QACrC,MAAM,eAAe,CAAA;QAErB,OAAO;QACP,MAAM,CAAC,QAAQ,CAAC,wBAAwB,CAAC,cAAc,CAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;KAarG,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,kBAAkB,GAAG,IAAI,OAAO,CAAO,UAAU,QAAQ,EAAE,OAAO,IAAG,CAAC,CAAC,CAAA;QAE7E,MAAM,kBAAkB,GAAG;YACzB,MAAM,EAAE,sBAAsB;YAC9B,MAAM,EAAE,KAAK,IAAI,EAAE;gBACjB,MAAM,kBAAkB,CAAA;YAC1B,CAAC;SACF,CAAA;QAED,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAEvB,MAAM,cAAc,GAAG,MAAM,CAC3B,oBAAC,gBAAgB,IACf,SAAS,EAAE,CAAC,kBAAkB,CAAC,EAC/B,eAAe,EAAE,IAAI,eAAe,EAAE,EACtC,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,GAC5C,CACH,CAAA;QAED,MAAM,sBAAsB,EAAE,CAAA;QAC9B,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAExC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QACxC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {ConcurrentOutput} from './ConcurrentOutput.js'\nimport {getLastFrameAfterUnmount, waitForInputsToBeReady} from '../../testing/ui.js'\nimport {AbortController, AbortSignal} from '../../../../public/node/abort.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport React from 'react'\nimport {describe, expect, test, vi} from 'vitest'\nimport {render} from 'ink-testing-library'\nimport {Writable} from 'stream'\n\ndescribe('ConcurrentOutput', () => {\n test('renders a stream of concurrent outputs from sub-processes', async () => {\n // Given\n let backendPromiseResolve: () => void\n let frontendPromiseResolve: () => void\n\n const backendPromise = new Promise<void>(function (resolve, _reject) {\n backendPromiseResolve = resolve\n })\n\n const frontendPromise = new Promise<void>(function (resolve, _reject) {\n frontendPromiseResolve = resolve\n })\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 backendPromiseResolve()\n },\n }\n\n const frontendProcess = {\n prefix: 'frontend',\n action: async (stdout: Writable, _stderr: Writable, _signal: AbortSignal) => {\n await backendPromise\n\n stdout.write('first frontend message')\n stdout.write('second frontend message')\n stdout.write('third frontend message')\n\n frontendPromiseResolve()\n },\n }\n // When\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[backendProcess, frontendProcess]}\n abortController={new AbortController()}\n footer={{\n shortcuts: [\n {\n key: 'p',\n action: 'open your browser',\n },\n {\n key: 'q',\n action: 'quit',\n },\n ],\n subTitle: `Preview URL: https://shopify.com`,\n }}\n />,\n )\n\n // wait for all output to be rendered\n await frontendPromise\n\n // Then\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!.replace(/\\d/g, '0'))).toMatchInlineSnapshot(`\n \"0000-00-00 00:00:00 │ backend │ first backend message\n 0000-00-00 00:00:00 │ backend │ second backend message\n 0000-00-00 00:00:00 │ backend │ third backend message\n 0000-00-00 00:00:00 │ frontend │ first frontend message\n 0000-00-00 00:00:00 │ frontend │ second frontend message\n 0000-00-00 00:00:00 │ frontend │ third frontend message\n\n › Press p │ open your browser\n › Press q │ quit\n\n Preview URL: https://shopify.com\n \"\n `)\n })\n\n test('accepts a onInput function that fires when a key is pressed', async () => {\n const neverEndingPromise = new Promise<void>(function (_resolve, _reject) {})\n\n const neverEndingProcess = {\n prefix: 'never-ending-process',\n action: async () => {\n await neverEndingPromise\n },\n }\n\n const onInput = vi.fn()\n\n const renderInstance = render(\n <ConcurrentOutput\n processes={[neverEndingProcess]}\n abortController={new AbortController()}\n onInput={(input, key) => onInput(input, key)}\n />,\n )\n\n await waitForInputsToBeReady()\n expect(onInput).toHaveBeenCalledTimes(0)\n\n renderInstance.stdin.write('a')\n expect(onInput).toHaveBeenCalledTimes(1)\n expect(onInput.mock.calls[0]![0]).toBe('a')\n })\n})\n"]}
|
|
@@ -81,8 +81,7 @@ describe('FatalError', async () => {
|
|
|
81
81
|
│ Unexpected error │
|
|
82
82
|
│ │
|
|
83
83
|
│ Next steps │
|
|
84
|
-
│ • Have you created a Shopify Partners organization
|
|
85
|
-
│ https://partners.shopify.com/signup )? │
|
|
84
|
+
│ • Have you created a Shopify Partners organization [1]? │
|
|
86
85
|
│ • Have you confirmed your accounts from the emails you received? │
|
|
87
86
|
│ • Need to connect to a different App or organization? Run the command │
|
|
88
87
|
│ again with \`--reset\` │
|
|
@@ -94,6 +93,7 @@ describe('FatalError', async () => {
|
|
|
94
93
|
│ at _load (internal/modules/cjs/loader.js:878) │
|
|
95
94
|
│ │
|
|
96
95
|
╰──────────────────────────────────────────────────────────────────────────────╯
|
|
96
|
+
[1] https://partners.shopify.com/signup
|
|
97
97
|
"
|
|
98
98
|
`);
|
|
99
99
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FatalError.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/FatalError.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAC,QAAQ,EAAC,MAAM,mCAAmC,CAAA;AAC1D,OAAO,EAAC,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAC,MAAM,kCAAkC,CAAA;AACpF,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAE1C,QAAQ,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;IAChC,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;QAChD,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,GAAI,CAAC,CAAA;QAExD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;KASpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,kBAAkB,CAAC,CAAA;QAC9C,KAAK,CAAC,KAAK,GAAG;;;;;;KAMb,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,GAAI,CAAC,CAAA;QAExD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;KAapD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,kBAAkB,CAAC,CAAA;QAC9C,KAAK,CAAC,KAAK,GAAG;;;;;;KAMb,CAAA;QAED,KAAK,CAAC,SAAS,GAAG;YAChB;gBACE,UAAU;gBACV;oBACE,IAAI,EAAE;wBACJ,KAAK,EAAE,yCAAyC;wBAChD,GAAG,EAAE,qCAAqC;qBAC3C;iBACF;gBACD;oBACE,IAAI,EAAE,GAAG;iBACV;aACF;YACD,gEAAgE;YAChE;gBACE,gFAAgF;gBAChF;oBACE,OAAO,EAAE,SAAS;iBACnB;aACF;SACF,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,GAAI,CAAC,CAAA;QAExD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;KAoBpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,kBAAkB,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,CAAA;QAExE,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,GAAI,CAAC,CAAA;QAExD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;KASpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {FatalError} from './FatalError.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {AbortError, BugError, ExternalError} from '../../../../public/node/error.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\nimport {render} from 'ink-testing-library'\n\ndescribe('FatalError', async () => {\n test('renders correctly with a just a message and tryMessage', async () => {\n const error = new AbortError('test', 'try this')\n const {lastFrame} = render(<FatalError error={error} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"╭─ error ──────────────────────────────────────────────────────────────────────╮\n │ │\n │ test │\n │ │\n │ try this │\n │ │\n ╰──────────────────────────────────────────────────────────────────────────────╯\n \"\n `)\n })\n\n test('renders correctly with a message and a stack', async () => {\n const error = new BugError('Unexpected error')\n error.stack = `\n Error: Unexpected error\n at Module._compile (internal/modules/cjs/loader.js:1137:30)\n at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)\n at Module.load (internal/modules/cjs/loader.js:985:32)\n at Function.Module._load (internal/modules/cjs/loader.js:878:14)\n `\n\n const {lastFrame} = render(<FatalError error={error} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"╭─ error ──────────────────────────────────────────────────────────────────────╮\n │ │\n │ Unexpected error │\n │ │\n │ To investigate the issue, examine this stack trace: │\n │ at _compile (internal/modules/cjs/loader.js:1137) │\n │ at js (internal/modules/cjs/loader.js:1157) │\n │ at load (internal/modules/cjs/loader.js:985) │\n │ at _load (internal/modules/cjs/loader.js:878) │\n │ │\n ╰──────────────────────────────────────────────────────────────────────────────╯\n \"\n `)\n })\n\n test('renders correctly with a message, a stack, and next steps', async () => {\n const error = new BugError('Unexpected error')\n error.stack = `\n Error: Unexpected error\n at Module._compile (internal/modules/cjs/loader.js:1137:30)\n at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)\n at Module.load (internal/modules/cjs/loader.js:985:32)\n at Function.Module._load (internal/modules/cjs/loader.js:878:14)\n `\n\n error.nextSteps = [\n [\n 'Have you',\n {\n link: {\n label: 'created a Shopify Partners organization',\n url: 'https://partners.shopify.com/signup',\n },\n },\n {\n char: '?',\n },\n ],\n 'Have you confirmed your accounts from the emails you received?',\n [\n 'Need to connect to a different App or organization? Run the command again with',\n {\n command: '--reset',\n },\n ],\n ]\n\n const {lastFrame} = render(<FatalError error={error} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"╭─ error ──────────────────────────────────────────────────────────────────────╮\n │ │\n │ Unexpected error │\n │ │\n │ Next steps │\n │ • Have you created a Shopify Partners organization
|
|
1
|
+
{"version":3,"file":"FatalError.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/FatalError.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAC,QAAQ,EAAC,MAAM,mCAAmC,CAAA;AAC1D,OAAO,EAAC,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAC,MAAM,kCAAkC,CAAA;AACpF,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAE1C,QAAQ,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE;IAChC,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;QAChD,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,GAAI,CAAC,CAAA;QAExD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;KASpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,kBAAkB,CAAC,CAAA;QAC9C,KAAK,CAAC,KAAK,GAAG;;;;;;KAMb,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,GAAI,CAAC,CAAA;QAExD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;KAapD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,KAAK,GAAG,IAAI,QAAQ,CAAC,kBAAkB,CAAC,CAAA;QAC9C,KAAK,CAAC,KAAK,GAAG;;;;;;KAMb,CAAA;QAED,KAAK,CAAC,SAAS,GAAG;YAChB;gBACE,UAAU;gBACV;oBACE,IAAI,EAAE;wBACJ,KAAK,EAAE,yCAAyC;wBAChD,GAAG,EAAE,qCAAqC;qBAC3C;iBACF;gBACD;oBACE,IAAI,EAAE,GAAG;iBACV;aACF;YACD,gEAAgE;YAChE;gBACE,gFAAgF;gBAChF;oBACE,OAAO,EAAE,SAAS;iBACnB;aACF;SACF,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,GAAI,CAAC,CAAA;QAExD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;KAoBpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,kBAAkB,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,CAAA;QAExE,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,UAAU,IAAC,KAAK,EAAE,KAAK,GAAI,CAAC,CAAA;QAExD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;;;;KASpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {FatalError} from './FatalError.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {AbortError, BugError, ExternalError} from '../../../../public/node/error.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\nimport {render} from 'ink-testing-library'\n\ndescribe('FatalError', async () => {\n test('renders correctly with a just a message and tryMessage', async () => {\n const error = new AbortError('test', 'try this')\n const {lastFrame} = render(<FatalError error={error} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"╭─ error ──────────────────────────────────────────────────────────────────────╮\n │ │\n │ test │\n │ │\n │ try this │\n │ │\n ╰──────────────────────────────────────────────────────────────────────────────╯\n \"\n `)\n })\n\n test('renders correctly with a message and a stack', async () => {\n const error = new BugError('Unexpected error')\n error.stack = `\n Error: Unexpected error\n at Module._compile (internal/modules/cjs/loader.js:1137:30)\n at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)\n at Module.load (internal/modules/cjs/loader.js:985:32)\n at Function.Module._load (internal/modules/cjs/loader.js:878:14)\n `\n\n const {lastFrame} = render(<FatalError error={error} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"╭─ error ──────────────────────────────────────────────────────────────────────╮\n │ │\n │ Unexpected error │\n │ │\n │ To investigate the issue, examine this stack trace: │\n │ at _compile (internal/modules/cjs/loader.js:1137) │\n │ at js (internal/modules/cjs/loader.js:1157) │\n │ at load (internal/modules/cjs/loader.js:985) │\n │ at _load (internal/modules/cjs/loader.js:878) │\n │ │\n ╰──────────────────────────────────────────────────────────────────────────────╯\n \"\n `)\n })\n\n test('renders correctly with a message, a stack, and next steps', async () => {\n const error = new BugError('Unexpected error')\n error.stack = `\n Error: Unexpected error\n at Module._compile (internal/modules/cjs/loader.js:1137:30)\n at Object.Module._extensions..js (internal/modules/cjs/loader.js:1157:10)\n at Module.load (internal/modules/cjs/loader.js:985:32)\n at Function.Module._load (internal/modules/cjs/loader.js:878:14)\n `\n\n error.nextSteps = [\n [\n 'Have you',\n {\n link: {\n label: 'created a Shopify Partners organization',\n url: 'https://partners.shopify.com/signup',\n },\n },\n {\n char: '?',\n },\n ],\n 'Have you confirmed your accounts from the emails you received?',\n [\n 'Need to connect to a different App or organization? Run the command again with',\n {\n command: '--reset',\n },\n ],\n ]\n\n const {lastFrame} = render(<FatalError error={error} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"╭─ error ──────────────────────────────────────────────────────────────────────╮\n │ │\n │ Unexpected error │\n │ │\n │ Next steps │\n │ • Have you created a Shopify Partners organization [1]? │\n │ • Have you confirmed your accounts from the emails you received? │\n │ • Need to connect to a different App or organization? Run the command │\n │ again with \\`--reset\\` │\n │ │\n │ To investigate the issue, examine this stack trace: │\n │ at _compile (internal/modules/cjs/loader.js:1137) │\n │ at js (internal/modules/cjs/loader.js:1157) │\n │ at load (internal/modules/cjs/loader.js:985) │\n │ at _load (internal/modules/cjs/loader.js:878) │\n │ │\n ╰──────────────────────────────────────────────────────────────────────────────╯\n [1] https://partners.shopify.com/signup\n \"\n `)\n })\n\n test('renders correctly an external error', async () => {\n const error = new ExternalError('Unexpected error', 'yarn', ['install'])\n\n const {lastFrame} = render(<FatalError error={error} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"── external error ──────────────────────────────────────────────────────────────\n\n Error coming from \\`yarn install\\`\n\n Unexpected error\n\n ────────────────────────────────────────────────────────────────────────────────\n \"\n `)\n })\n})\n"]}
|
|
@@ -1,15 +1,27 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { LinksContext } from '../contexts/LinksContext.js';
|
|
2
2
|
import { Text } from 'ink';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import React, { useContext } from 'react';
|
|
4
|
+
import ansiEscapes from 'ansi-escapes';
|
|
5
|
+
import supportsHyperlinks from 'supports-hyperlinks';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
function link(label, url, linksContext) {
|
|
8
|
+
if (!supportsHyperlinks.stdout) {
|
|
9
|
+
if (linksContext === null) {
|
|
10
|
+
return label ? `${label} ${chalk.dim(`( ${url} )`)}` : url;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
const linkId = linksContext.addLink(label, url);
|
|
14
|
+
return label ? `${label} [${linkId}]` : `[${linkId}]]`;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return ansiEscapes.link(label ?? url, url);
|
|
7
18
|
}
|
|
8
19
|
/**
|
|
9
20
|
* `Link` displays a clickable link when supported by the terminal.
|
|
10
21
|
*/
|
|
11
|
-
const Link = ({
|
|
12
|
-
|
|
22
|
+
const Link = ({ label, url }) => {
|
|
23
|
+
const linksContext = useContext(LinksContext);
|
|
24
|
+
return React.createElement(Text, null, link(label, url, linksContext));
|
|
13
25
|
};
|
|
14
26
|
export { Link };
|
|
15
27
|
//# sourceMappingURL=Link.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Link.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Link.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"Link.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Link.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAoC,MAAM,6BAA6B,CAAA;AAC3F,OAAO,EAAC,IAAI,EAAC,MAAM,KAAK,CAAA;AACxB,OAAO,KAAK,EAAE,EAAoB,UAAU,EAAC,MAAM,OAAO,CAAA;AAC1D,OAAO,WAAW,MAAM,cAAc,CAAA;AACtC,OAAO,kBAAkB,MAAM,qBAAqB,CAAA;AACpD,OAAO,KAAK,MAAM,OAAO,CAAA;AAOzB,SAAS,IAAI,CAAC,KAAyB,EAAE,GAAW,EAAE,YAAsC;IAC1F,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE;QAC9B,IAAI,YAAY,KAAK,IAAI,EAAE;YACzB,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;SAC3D;aAAM;YACL,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;YAC/C,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,MAAM,IAAI,CAAA;SACvD;KACF;IAED,OAAO,WAAW,CAAC,IAAI,CAAC,KAAK,IAAI,GAAG,EAAE,GAAG,CAAC,CAAA;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,IAAI,GAAiC,CAAC,EAAC,KAAK,EAAE,GAAG,EAAC,EAAe,EAAE;IACvE,MAAM,YAAY,GAAG,UAAU,CAAC,YAAY,CAAC,CAAA;IAE7C,OAAO,oBAAC,IAAI,QAAE,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,CAAQ,CAAA;AACtD,CAAC,CAAA;AAED,OAAO,EAAC,IAAI,EAAC,CAAA","sourcesContent":["import {LinksContext, ContextValue as LinksContextValue} from '../contexts/LinksContext.js'\nimport {Text} from 'ink'\nimport React, {FunctionComponent, useContext} from 'react'\nimport ansiEscapes from 'ansi-escapes'\nimport supportsHyperlinks from 'supports-hyperlinks'\nimport chalk from 'chalk'\n\ninterface LinkProps {\n url: string\n label?: string\n}\n\nfunction link(label: string | undefined, url: string, linksContext: LinksContextValue | null) {\n if (!supportsHyperlinks.stdout) {\n if (linksContext === null) {\n return label ? `${label} ${chalk.dim(`( ${url} )`)}` : url\n } else {\n const linkId = linksContext.addLink(label, url)\n return label ? `${label} [${linkId}]` : `[${linkId}]]`\n }\n }\n\n return ansiEscapes.link(label ?? url, url)\n}\n\n/**\n * `Link` displays a clickable link when supported by the terminal.\n */\nconst Link: FunctionComponent<LinkProps> = ({label, url}): JSX.Element => {\n const linksContext = useContext(LinksContext)\n\n return <Text>{link(label, url, linksContext)}</Text>\n}\n\nexport {Link}\n"]}
|
|
@@ -49,7 +49,7 @@ function SelectPrompt({ message, choices, infoTable, onSubmit, defaultValue, sub
|
|
|
49
49
|
return () => {
|
|
50
50
|
stdout.off('resize', onResize);
|
|
51
51
|
};
|
|
52
|
-
}, [wrapperHeight, selectInputHeight, choices.length, stdout
|
|
52
|
+
}, [wrapperHeight, selectInputHeight, choices.length, numberOfGroups, stdout, limit]);
|
|
53
53
|
const submitAnswer = useCallback((answer) => {
|
|
54
54
|
if (stdout && wrapperHeight >= stdout.rows) {
|
|
55
55
|
stdout.write(ansiEscapes.clearTerminal);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SelectPrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/SelectPrompt.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SelectPrompt.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/SelectPrompt.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SelectInput, SelectInputProps, Item as SelectItem} from './SelectInput.js'\nimport {InfoTable, InfoTableProps} from './Prompts/InfoTable.js'\nimport {InlineToken, LinkToken, TokenItem, TokenizedText} from './TokenizedText.js'\nimport {handleCtrlC} from '../../ui.js'\nimport {messageWithPunctuation} from '../utilities.js'\nimport {uniqBy} from '../../../../public/common/array.js'\nimport React, {ReactElement, useCallback, useEffect, useState} from 'react'\nimport {Box, measureElement, Text, useApp, useInput, useStdout} from 'ink'\nimport figures from 'figures'\nimport ansiEscapes from 'ansi-escapes'\n\nexport interface SelectPromptProps<T> {\n message: TokenItem<Exclude<InlineToken, LinkToken>>\n choices: SelectInputProps<T>['items']\n onSubmit: (value: T) => void\n infoTable?: InfoTableProps['table']\n defaultValue?: T\n submitWithShortcuts?: boolean\n}\n\n// eslint-disable-next-line react/function-component-definition\nfunction SelectPrompt<T>({\n message,\n choices,\n infoTable,\n onSubmit,\n defaultValue,\n submitWithShortcuts = false,\n}: React.PropsWithChildren<SelectPromptProps<T>>): ReactElement | null {\n if (choices.length === 0) {\n throw new Error('SelectPrompt requires at least one choice')\n }\n const initialValue = defaultValue ? choices.find((choice) => choice.value === defaultValue) : undefined\n const [answer, setAnswer] = useState<SelectItem<T> | undefined>(undefined)\n const {exit: unmountInk} = useApp()\n const [submitted, setSubmitted] = useState(false)\n const {stdout} = useStdout()\n const [wrapperHeight, setWrapperHeight] = useState(0)\n const [selectInputHeight, setSelectInputHeight] = useState(0)\n const [limit, setLimit] = useState(choices.length)\n const numberOfGroups = uniqBy(\n choices.filter((choice) => choice.group),\n 'group',\n ).length\n\n const wrapperRef = useCallback((node) => {\n if (node !== null) {\n const {height} = measureElement(node)\n setWrapperHeight(height)\n }\n }, [])\n\n const inputRef = useCallback((node) => {\n if (node !== null) {\n const {height} = measureElement(node)\n setSelectInputHeight(height)\n }\n }, [])\n\n useEffect(() => {\n function onResize() {\n const availableSpace = stdout!.rows - (wrapperHeight - selectInputHeight)\n // rough estimate of the limit needed based on the space available\n const newLimit = Math.max(2, availableSpace - numberOfGroups * 2 - 6)\n\n if (newLimit < limit) {\n stdout!.write(ansiEscapes.clearTerminal)\n }\n\n setLimit(Math.min(newLimit, choices.length))\n }\n\n onResize()\n\n stdout!.on('resize', onResize)\n return () => {\n stdout!.off('resize', onResize)\n }\n }, [wrapperHeight, selectInputHeight, choices.length, numberOfGroups, stdout, limit])\n\n const submitAnswer = useCallback(\n (answer: SelectItem<T>) => {\n if (stdout && wrapperHeight >= stdout.rows) {\n stdout.write(ansiEscapes.clearTerminal)\n }\n setSubmitted(true)\n unmountInk()\n onSubmit(answer.value)\n },\n [stdout, wrapperHeight, unmountInk, onSubmit],\n )\n\n useInput((input, key) => {\n handleCtrlC(input, key)\n\n if (key.return && answer) {\n submitAnswer(answer)\n }\n })\n\n return (\n <Box flexDirection=\"column\" marginBottom={1} ref={wrapperRef}>\n <Box>\n <Box marginRight={2}>\n <Text>?</Text>\n </Box>\n <TokenizedText item={messageWithPunctuation(message)} />\n </Box>\n {infoTable && !submitted ? (\n <Box marginLeft={7} marginTop={1}>\n <InfoTable table={infoTable} />\n </Box>\n ) : null}\n {submitted ? (\n <Box>\n <Box marginRight={2}>\n <Text color=\"cyan\">{figures.tick}</Text>\n </Box>\n\n <Text color=\"cyan\">{answer!.label}</Text>\n </Box>\n ) : (\n <Box marginTop={1}>\n <SelectInput\n defaultValue={initialValue}\n items={choices}\n infoMessage={\n submitWithShortcuts\n ? `Press ${figures.arrowUp}${figures.arrowDown} arrows to select, enter or a shortcut to confirm`\n : undefined\n }\n onChange={({item, usedShortcut}) => {\n setAnswer(item)\n\n if (submitWithShortcuts && usedShortcut && item) {\n submitAnswer(item)\n }\n }}\n limit={limit}\n ref={inputRef}\n />\n </Box>\n )}\n </Box>\n )\n}\n\nexport {SelectPrompt}\n"]}
|
|
@@ -10,6 +10,7 @@ export interface Task<TContext = unknown> {
|
|
|
10
10
|
export interface TasksProps<TContext> {
|
|
11
11
|
tasks: Task<TContext>[];
|
|
12
12
|
silent?: boolean;
|
|
13
|
+
onComplete?: (ctx: TContext) => void;
|
|
13
14
|
}
|
|
14
|
-
declare function Tasks<TContext>({ tasks, silent }: React.PropsWithChildren<TasksProps<TContext>>): JSX.Element | null;
|
|
15
|
+
declare function Tasks<TContext>({ tasks, silent, onComplete, }: React.PropsWithChildren<TasksProps<TContext>>): JSX.Element | null;
|
|
15
16
|
export { Tasks };
|
|
@@ -36,8 +36,9 @@ async function runTask(task, ctx) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
+
const noop = () => { };
|
|
39
40
|
// eslint-disable-next-line react/function-component-definition
|
|
40
|
-
function Tasks({ tasks, silent = isUnitTest() }) {
|
|
41
|
+
function Tasks({ tasks, silent = isUnitTest(), onComplete = noop, }) {
|
|
41
42
|
const { twoThirds } = useLayout();
|
|
42
43
|
const loadingBar = new Array(twoThirds).fill(loadingBarChar).join('');
|
|
43
44
|
const [currentTask, setCurrentTask] = useState(tasks[0]);
|
|
@@ -60,7 +61,10 @@ function Tasks({ tasks, silent = isUnitTest() }) {
|
|
|
60
61
|
}
|
|
61
62
|
};
|
|
62
63
|
useAsyncAndUnmount(runTasks, {
|
|
63
|
-
onFulfilled: () =>
|
|
64
|
+
onFulfilled: () => {
|
|
65
|
+
setState(TasksState.Success);
|
|
66
|
+
onComplete(ctx.current);
|
|
67
|
+
},
|
|
64
68
|
onRejected: () => setState(TasksState.Failure),
|
|
65
69
|
});
|
|
66
70
|
useInput((input, key) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tasks.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Tasks.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAA;AAChD,OAAO,SAAS,MAAM,wBAAwB,CAAA;AAC9C,OAAO,kBAAkB,MAAM,mCAAmC,CAAA;AAClE,OAAO,EAAC,UAAU,EAAC,MAAM,0CAA0C,CAAA;AACnE,OAAO,EAAC,WAAW,EAAC,MAAM,aAAa,CAAA;AACvC,OAAO,EAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAC,MAAM,KAAK,CAAA;AACjD,OAAO,KAAK,EAAE,EAAC,MAAM,EAAE,QAAQ,EAAC,MAAM,OAAO,CAAA;AAE7C,MAAM,cAAc,GAAG,GAAG,CAAA;
|
|
1
|
+
{"version":3,"file":"Tasks.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/Tasks.tsx"],"names":[],"mappings":"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","sourcesContent":["import {TextAnimation} from './TextAnimation.js'\nimport useLayout from '../hooks/use-layout.js'\nimport useAsyncAndUnmount from '../hooks/use-async-and-unmount.js'\nimport {isUnitTest} from '../../../../public/node/context/local.js'\nimport {handleCtrlC} from '../../ui.js'\nimport {Box, Text, useInput, useStdin} from 'ink'\nimport React, {useRef, useState} from 'react'\n\nconst loadingBarChar = '▀'\n\nexport interface Task<TContext = unknown> {\n title: string\n task: (ctx: TContext, task: Task<TContext>) => Promise<void | Task<TContext>[]>\n retry?: number\n retryCount?: number\n errors?: Error[]\n skip?: (ctx: TContext) => boolean\n}\n\nexport interface TasksProps<TContext> {\n tasks: Task<TContext>[]\n silent?: boolean\n onComplete?: (ctx: TContext) => void\n}\n\nenum TasksState {\n Loading = 'loading',\n Success = 'success',\n Failure = 'failure',\n}\n\nasync function runTask<TContext>(task: Task<TContext>, ctx: TContext) {\n task.retryCount = 0\n task.errors = []\n const retry = task?.retry && task?.retry > 0 ? task.retry + 1 : 1\n\n for (let retries = 1; retries <= retry; retries++) {\n try {\n if (task.skip?.(ctx)) {\n return\n }\n // eslint-disable-next-line no-await-in-loop\n return await task.task(ctx, task)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (error: any) {\n if (retries === retry) {\n throw error\n } else {\n task.errors.push(error)\n task.retryCount = retries\n }\n }\n }\n}\n\nconst noop = () => {}\n\n// eslint-disable-next-line react/function-component-definition\nfunction Tasks<TContext>({\n tasks,\n silent = isUnitTest(),\n onComplete = noop,\n}: React.PropsWithChildren<TasksProps<TContext>>) {\n const {twoThirds} = useLayout()\n const loadingBar = new Array(twoThirds).fill(loadingBarChar).join('')\n const [currentTask, setCurrentTask] = useState<Task<TContext>>(tasks[0]!)\n const [state, setState] = useState<TasksState>(TasksState.Loading)\n const ctx = useRef<TContext>({} as TContext)\n const {isRawModeSupported} = useStdin()\n\n const runTasks = async () => {\n for (const task of tasks) {\n setCurrentTask(task)\n\n // eslint-disable-next-line no-await-in-loop\n const subTasks = await runTask(task, ctx.current)\n\n // subtasks\n if (Array.isArray(subTasks) && subTasks.length > 0 && subTasks.every((task) => 'task' in task)) {\n for (const subTask of subTasks) {\n setCurrentTask(subTask)\n // eslint-disable-next-line no-await-in-loop\n await runTask(subTask, ctx.current)\n }\n }\n }\n }\n\n useAsyncAndUnmount(runTasks, {\n onFulfilled: () => {\n setState(TasksState.Success)\n onComplete(ctx.current)\n },\n onRejected: () => setState(TasksState.Failure),\n })\n\n useInput(\n (input, key) => {\n handleCtrlC(input, key)\n\n if (key.return) {\n return null\n }\n },\n {isActive: Boolean(isRawModeSupported)},\n )\n\n if (silent) {\n return null\n }\n\n return state === TasksState.Loading ? (\n <Box flexDirection=\"column\">\n <TextAnimation text={loadingBar} />\n <Text>{currentTask.title} ...</Text>\n </Box>\n ) : null\n}\n\nexport {Tasks}\n"]}
|
|
@@ -332,5 +332,21 @@ describe('Tasks', () => {
|
|
|
332
332
|
`);
|
|
333
333
|
expect(thirdTaskFunction).toHaveBeenCalled();
|
|
334
334
|
});
|
|
335
|
+
test('has an onComplete function that is called with the context', async () => {
|
|
336
|
+
// Given
|
|
337
|
+
const taskFunction = vi.fn(async (ctx) => {
|
|
338
|
+
ctx.foo = 'bar';
|
|
339
|
+
});
|
|
340
|
+
const task = {
|
|
341
|
+
title: 'task 1',
|
|
342
|
+
task: taskFunction,
|
|
343
|
+
};
|
|
344
|
+
// When
|
|
345
|
+
const context = await new Promise((resolve, _reject) => {
|
|
346
|
+
render(React.createElement(Tasks, { tasks: [task], silent: false, onComplete: resolve }));
|
|
347
|
+
});
|
|
348
|
+
// Then
|
|
349
|
+
expect(context).toEqual({ foo: 'bar' });
|
|
350
|
+
});
|
|
335
351
|
});
|
|
336
352
|
//# sourceMappingURL=Tasks.test.js.map
|