@shopify/cli-kit 3.46.0-pre.3 → 3.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/assets/cli-ruby/lib/project_types/extension/models/specification_handlers/theme_app_extension.rb +1 -1
  2. package/assets/cli-ruby/lib/shopify_cli/theme/dev_server/cdn_fonts.rb +6 -2
  3. package/assets/cli-ruby/lib/shopify_cli/version.rb +1 -1
  4. package/dist/private/node/api.d.ts +1 -0
  5. package/dist/private/node/api.js +1 -0
  6. package/dist/private/node/api.js.map +1 -1
  7. package/dist/private/node/constants.d.ts +1 -1
  8. package/dist/private/node/constants.js +1 -1
  9. package/dist/private/node/constants.js.map +1 -1
  10. package/dist/private/node/content-tokens.js +1 -1
  11. package/dist/private/node/content-tokens.js.map +1 -1
  12. package/dist/private/node/demo-recorder.d.ts +20 -0
  13. package/dist/private/node/demo-recorder.js +122 -0
  14. package/dist/private/node/demo-recorder.js.map +1 -0
  15. package/dist/private/node/session/schema.d.ts +203 -41
  16. package/dist/private/node/tree-kill.d.ts +1 -0
  17. package/dist/private/node/tree-kill.js +7 -0
  18. package/dist/private/node/tree-kill.js.map +1 -0
  19. package/dist/private/node/ui/alert.js +4 -0
  20. package/dist/private/node/ui/alert.js.map +1 -1
  21. package/dist/private/node/ui/components/Alert.test.js +2 -2
  22. package/dist/private/node/ui/components/Alert.test.js.map +1 -1
  23. package/dist/private/node/ui/components/AutocompletePrompt.d.ts +3 -1
  24. package/dist/private/node/ui/components/AutocompletePrompt.js +8 -4
  25. package/dist/private/node/ui/components/AutocompletePrompt.js.map +1 -1
  26. package/dist/private/node/ui/components/AutocompletePrompt.test.js +33 -7
  27. package/dist/private/node/ui/components/AutocompletePrompt.test.js.map +1 -1
  28. package/dist/private/node/ui/components/ConcurrentOutput.d.ts +2 -2
  29. package/dist/private/node/ui/components/ConcurrentOutput.js +23 -6
  30. package/dist/private/node/ui/components/ConcurrentOutput.js.map +1 -1
  31. package/dist/private/node/ui/components/ConcurrentOutput.test.js +122 -8
  32. package/dist/private/node/ui/components/ConcurrentOutput.test.js.map +1 -1
  33. package/dist/private/node/ui/components/FatalError.js +5 -3
  34. package/dist/private/node/ui/components/FatalError.js.map +1 -1
  35. package/dist/private/node/ui/components/FatalError.test.js +113 -0
  36. package/dist/private/node/ui/components/FatalError.test.js.map +1 -1
  37. package/dist/private/node/ui/components/List.d.ts +1 -1
  38. package/dist/private/node/ui/components/List.js +2 -1
  39. package/dist/private/node/ui/components/List.js.map +1 -1
  40. package/dist/private/node/ui/components/List.test.js +19 -0
  41. package/dist/private/node/ui/components/List.test.js.map +1 -1
  42. package/dist/private/node/ui/components/Prompts/InfoTable.js +5 -1
  43. package/dist/private/node/ui/components/Prompts/InfoTable.js.map +1 -1
  44. package/dist/private/node/ui/components/Prompts/InfoTable.test.js +5 -4
  45. package/dist/private/node/ui/components/Prompts/InfoTable.test.js.map +1 -1
  46. package/dist/private/node/ui/components/SelectPrompt.d.ts +3 -1
  47. package/dist/private/node/ui/components/SelectPrompt.js +4 -2
  48. package/dist/private/node/ui/components/SelectPrompt.js.map +1 -1
  49. package/dist/private/node/ui/components/SelectPrompt.test.js +22 -0
  50. package/dist/private/node/ui/components/SelectPrompt.test.js.map +1 -1
  51. package/dist/private/node/ui/components/Tasks.d.ts +3 -1
  52. package/dist/private/node/ui/components/Tasks.js +7 -3
  53. package/dist/private/node/ui/components/Tasks.js.map +1 -1
  54. package/dist/private/node/ui/components/Tasks.test.js +22 -1
  55. package/dist/private/node/ui/components/Tasks.test.js.map +1 -1
  56. package/dist/private/node/ui/components/TextPrompt.d.ts +2 -0
  57. package/dist/private/node/ui/components/TextPrompt.js +4 -2
  58. package/dist/private/node/ui/components/TextPrompt.js.map +1 -1
  59. package/dist/private/node/ui/components/TextPrompt.test.js +9 -0
  60. package/dist/private/node/ui/components/TextPrompt.test.js.map +1 -1
  61. package/dist/private/node/ui/components/TokenizedText.d.ts +1 -1
  62. package/dist/private/node/ui/components/TokenizedText.js.map +1 -1
  63. package/dist/private/node/ui/components/TokenizedText.test.js +10 -0
  64. package/dist/private/node/ui/components/TokenizedText.test.js.map +1 -1
  65. package/dist/private/node/ui/hooks/use-abort-signal.d.ts +4 -0
  66. package/dist/private/node/ui/hooks/use-abort-signal.js +14 -0
  67. package/dist/private/node/ui/hooks/use-abort-signal.js.map +1 -0
  68. package/dist/private/node/ui/hooks/use-async-and-unmount.d.ts +1 -1
  69. package/dist/private/node/ui/hooks/use-async-and-unmount.js +1 -1
  70. package/dist/private/node/ui/hooks/use-async-and-unmount.js.map +1 -1
  71. package/dist/private/node/ui.js +3 -3
  72. package/dist/private/node/ui.js.map +1 -1
  73. package/dist/public/common/version.d.ts +1 -1
  74. package/dist/public/common/version.js +1 -1
  75. package/dist/public/common/version.js.map +1 -1
  76. package/dist/public/node/cli.js +2 -0
  77. package/dist/public/node/cli.js.map +1 -1
  78. package/dist/public/node/colors.js.map +1 -0
  79. package/dist/public/node/context/local.d.ts +1 -1
  80. package/dist/public/node/context/local.js +2 -2
  81. package/dist/public/node/context/local.js.map +1 -1
  82. package/dist/public/node/error-handler.js +7 -2
  83. package/dist/public/node/error-handler.js.map +1 -1
  84. package/dist/public/node/error.d.ts +8 -3
  85. package/dist/public/node/error.js +12 -5
  86. package/dist/public/node/error.js.map +1 -1
  87. package/dist/public/node/fs.d.ts +1 -0
  88. package/dist/public/node/hooks/prerun.js +2 -0
  89. package/dist/public/node/hooks/prerun.js.map +1 -1
  90. package/dist/public/node/node-package-manager.d.ts +11 -0
  91. package/dist/public/node/node-package-manager.js +8 -6
  92. package/dist/public/node/node-package-manager.js.map +1 -1
  93. package/dist/public/node/output.d.ts +6 -1
  94. package/dist/public/node/output.js +6 -2
  95. package/dist/public/node/output.js.map +1 -1
  96. package/dist/public/node/plugins/tunnel.d.ts +19 -4
  97. package/dist/public/node/plugins/tunnel.js +1 -1
  98. package/dist/public/node/plugins/tunnel.js.map +1 -1
  99. package/dist/public/node/plugins.d.ts +1 -12
  100. package/dist/public/node/plugins.js +0 -23
  101. package/dist/public/node/plugins.js.map +1 -1
  102. package/dist/public/node/ruby.js +2 -0
  103. package/dist/public/node/ruby.js.map +1 -1
  104. package/dist/public/node/system.d.ts +1 -0
  105. package/dist/public/node/system.js +8 -3
  106. package/dist/public/node/system.js.map +1 -1
  107. package/dist/public/node/tcp.js +1 -1
  108. package/dist/public/node/tcp.js.map +1 -1
  109. package/dist/public/node/ui.d.ts +17 -8
  110. package/dist/public/node/ui.js +75 -19
  111. package/dist/public/node/ui.js.map +1 -1
  112. package/dist/tsconfig.tsbuildinfo +1 -1
  113. package/package.json +13 -12
  114. package/dist/private/node/colors.js.map +0 -1
  115. /package/dist/{private → public}/node/colors.d.ts +0 -0
  116. /package/dist/{private → public}/node/colors.js +0 -0
@@ -30,5 +30,24 @@ describe('List', async () => {
30
30
  3. Item 3"
31
31
  `);
32
32
  });
33
+ test('title can be made of tokens', async () => {
34
+ const options = {
35
+ title: [
36
+ 'List title',
37
+ {
38
+ bold: ' (bold)',
39
+ },
40
+ ],
41
+ items: ['Item 1', 'Item 2', 'Item 3'],
42
+ ordered: false,
43
+ };
44
+ const { lastFrame } = render(React.createElement(List, { ...options }));
45
+ expect(lastFrame()).toMatchInlineSnapshot(`
46
+ "List title  (bold)
47
+ • Item 1
48
+ • Item 2
49
+ • Item 3"
50
+ `);
51
+ });
33
52
  });
34
53
  //# sourceMappingURL=List.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"List.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/List.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAC9B,OAAO,EAAC,QAAQ,EAAC,MAAM,mCAAmC,CAAA;AAC1D,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,QAAQ,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;IAC1B,IAAI,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,OAAO,GAAG;YACd,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;YACrC,OAAO,EAAE,KAAK;SACf,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,IAAI,OAAK,OAAO,GAAI,CAAC,CAAA;QAEjD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;KAKpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,OAAO,GAAG;YACd,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;YACrC,OAAO,EAAE,IAAI;SACd,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,IAAI,OAAK,OAAO,GAAI,CAAC,CAAA;QAEjD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;KAIpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {List} from './List.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {render} from '../../testing/ui.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\n\ndescribe('List', async () => {\n test('renders unordered items', async () => {\n const options = {\n title: 'List title',\n items: ['Item 1', 'Item 2', 'Item 3'],\n ordered: false,\n }\n\n const {lastFrame} = render(<List {...options} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"List title\n • Item 1\n • Item 2\n • Item 3\"\n `)\n })\n\n test('renders ordered items', async () => {\n const options = {\n items: ['Item 1', 'Item 2', 'Item 3'],\n ordered: true,\n }\n\n const {lastFrame} = render(<List {...options} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \" 1. Item 1\n 2. Item 2\n 3. Item 3\"\n `)\n })\n})\n"]}
1
+ {"version":3,"file":"List.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/List.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAA;AAC9B,OAAO,EAAC,QAAQ,EAAC,MAAM,mCAAmC,CAAA;AAC1D,OAAO,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,QAAQ,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;IAC1B,IAAI,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QACzC,MAAM,OAAO,GAAG;YACd,KAAK,EAAE,YAAY;YACnB,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;YACrC,OAAO,EAAE,KAAK;SACf,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,IAAI,OAAK,OAAO,GAAI,CAAC,CAAA;QAEjD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;KAKpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,OAAO,GAAG;YACd,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;YACrC,OAAO,EAAE,IAAI;SACd,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,IAAI,OAAK,OAAO,GAAI,CAAC,CAAA;QAEjD,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;KAIpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;QAC7C,MAAM,OAAO,GAAG;YACd,KAAK,EAAE;gBACL,YAAY;gBACZ;oBACE,IAAI,EAAE,SAAS;iBAChB;aACF;YACD,KAAK,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;YACrC,OAAO,EAAE,KAAK;SACf,CAAA;QAED,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CAAC,oBAAC,IAAI,OAAK,OAAO,GAAI,CAAC,CAAA;QAEjD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,qBAAqB,CAAC;;;;;KAKzC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {List} from './List.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {render} from '../../testing/ui.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\n\ndescribe('List', async () => {\n test('renders unordered items', async () => {\n const options = {\n title: 'List title',\n items: ['Item 1', 'Item 2', 'Item 3'],\n ordered: false,\n }\n\n const {lastFrame} = render(<List {...options} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"List title\n • Item 1\n • Item 2\n • Item 3\"\n `)\n })\n\n test('renders ordered items', async () => {\n const options = {\n items: ['Item 1', 'Item 2', 'Item 3'],\n ordered: true,\n }\n\n const {lastFrame} = render(<List {...options} />)\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \" 1. Item 1\n 2. Item 2\n 3. Item 3\"\n `)\n })\n\n test('title can be made of tokens', async () => {\n const options = {\n title: [\n 'List title',\n {\n bold: ' (bold)',\n },\n ],\n items: ['Item 1', 'Item 2', 'Item 3'],\n ordered: false,\n }\n\n const {lastFrame} = render(<List {...options} />)\n\n expect(lastFrame()).toMatchInlineSnapshot(`\n \"List title \u001b[1m (bold)\u001b[22m\n • Item 1\n • Item 2\n • Item 3\"\n `)\n })\n})\n"]}
@@ -6,7 +6,11 @@ const InfoTable = ({ table }) => {
6
6
  const sections = Array.isArray(table)
7
7
  ? table
8
8
  : Object.keys(table).map((header) => ({ header, items: table[header], color: undefined, helperText: undefined }));
9
- const headerColumnWidth = Math.max(...sections.map((section) => section.header.length));
9
+ const headerColumnWidth = Math.max(...sections.map((section) => {
10
+ return Math.max(...section.header.split('\n').map((line) => {
11
+ return line.length;
12
+ }));
13
+ }));
10
14
  return (React.createElement(Box, { flexDirection: "column" }, sections.map((section, index) => (React.createElement(Box, { key: index, marginBottom: index === sections.length - 1 ? 0 : 1 },
11
15
  section.header.length > 0 && (React.createElement(Box, { width: headerColumnWidth + 1 },
12
16
  React.createElement(Text, { color: section.color },
@@ -1 +1 @@
1
- {"version":3,"file":"InfoTable.js","sourceRoot":"","sources":["../../../../../../src/private/node/ui/components/Prompts/InfoTable.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,YAAY,CAAA;AAC/B,OAAO,EAAC,UAAU,EAAC,MAAM,wCAAwC,CAAA;AAEjE,OAAO,EAAC,GAAG,EAAE,IAAI,EAAY,MAAM,KAAK,CAAA;AACxC,OAAO,KAA0B,MAAM,OAAO,CAAA;AAmB9C,MAAM,SAAS,GAAsC,CAAC,EAAC,KAAK,EAAC,EAAE,EAAE;IAC/D,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACnC,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAC,CAAC,CAAC,CAAA;IAClH,MAAM,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IAEvF,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,IACxB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAChC,oBAAC,GAAG,IAAC,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAC5B,oBAAC,GAAG,IAAC,KAAK,EAAE,iBAAiB,GAAG,CAAC;YAC/B,oBAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK;gBAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC;oBAAS,CAC5D,CACP;QACD,oBAAC,GAAG,IAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ;YACrF,oBAAC,IAAI,IAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAI;YAClE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CACpB,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;gBACf,oBAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAG,OAAO,CAAC,UAAU,CAAQ,CACnD,CACP,CAAC,CAAC,CAAC,IAAI,CACJ,CACF,CACP,CAAC,CACE,CACP,CAAA;AACH,CAAC,CAAA;AAED,OAAO,EAAC,SAAS,EAAC,CAAA","sourcesContent":["import {List} from '../List.js'\nimport {capitalize} from '../../../../../public/common/string.js'\nimport {InlineToken, TokenItem} from '../TokenizedText.js'\nimport {Box, Text, TextProps} from 'ink'\nimport React, {FunctionComponent} from 'react'\n\ntype Items = TokenItem<InlineToken>[]\n\nexport interface InfoTableSection {\n color?: TextProps['color']\n header: string\n helperText?: string\n items: Items\n}\n\nexport interface InfoTableProps {\n table:\n | {\n [header: string]: Items\n }\n | InfoTableSection[]\n}\n\nconst InfoTable: FunctionComponent<InfoTableProps> = ({table}) => {\n const sections = Array.isArray(table)\n ? table\n : Object.keys(table).map((header) => ({header, items: table[header]!, color: undefined, helperText: undefined}))\n const headerColumnWidth = Math.max(...sections.map((section) => section.header.length))\n\n return (\n <Box flexDirection=\"column\">\n {sections.map((section, index) => (\n <Box key={index} marginBottom={index === sections.length - 1 ? 0 : 1}>\n {section.header.length > 0 && (\n <Box width={headerColumnWidth + 1}>\n <Text color={section.color}>{capitalize(section.header)}:</Text>\n </Box>\n )}\n <Box marginLeft={section.header.length > 0 ? 2 : 0} flexGrow={1} flexDirection=\"column\">\n <List margin={false} items={section.items} color={section.color} />\n {section.helperText ? (\n <Box marginTop={1}>\n <Text color={section.color}>{section.helperText}</Text>\n </Box>\n ) : null}\n </Box>\n </Box>\n ))}\n </Box>\n )\n}\n\nexport {InfoTable}\n"]}
1
+ {"version":3,"file":"InfoTable.js","sourceRoot":"","sources":["../../../../../../src/private/node/ui/components/Prompts/InfoTable.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,IAAI,EAAC,MAAM,YAAY,CAAA;AAC/B,OAAO,EAAC,UAAU,EAAC,MAAM,wCAAwC,CAAA;AAEjE,OAAO,EAAC,GAAG,EAAE,IAAI,EAAY,MAAM,KAAK,CAAA;AACxC,OAAO,KAA0B,MAAM,OAAO,CAAA;AAmB9C,MAAM,SAAS,GAAsC,CAAC,EAAC,KAAK,EAAC,EAAE,EAAE;IAC/D,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACnC,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAC,CAAC,CAAC,CAAA;IAElH,MAAM,iBAAiB,GAAG,IAAI,CAAC,GAAG,CAChC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC1B,OAAO,IAAI,CAAC,GAAG,CACb,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACzC,OAAO,IAAI,CAAC,MAAM,CAAA;QACpB,CAAC,CAAC,CACH,CAAA;IACH,CAAC,CAAC,CACH,CAAA;IAED,OAAO,CACL,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,IACxB,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,CAChC,oBAAC,GAAG,IAAC,GAAG,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,KAAK,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,CAC5B,oBAAC,GAAG,IAAC,KAAK,EAAE,iBAAiB,GAAG,CAAC;YAC/B,oBAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK;gBAAG,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC;oBAAS,CAC5D,CACP;QACD,oBAAC,GAAG,IAAC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ;YACrF,oBAAC,IAAI,IAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,GAAI;YAClE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CACpB,oBAAC,GAAG,IAAC,SAAS,EAAE,CAAC;gBACf,oBAAC,IAAI,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK,IAAG,OAAO,CAAC,UAAU,CAAQ,CACnD,CACP,CAAC,CAAC,CAAC,IAAI,CACJ,CACF,CACP,CAAC,CACE,CACP,CAAA;AACH,CAAC,CAAA;AAED,OAAO,EAAC,SAAS,EAAC,CAAA","sourcesContent":["import {List} from '../List.js'\nimport {capitalize} from '../../../../../public/common/string.js'\nimport {InlineToken, TokenItem} from '../TokenizedText.js'\nimport {Box, Text, TextProps} from 'ink'\nimport React, {FunctionComponent} from 'react'\n\ntype Items = TokenItem<InlineToken>[]\n\nexport interface InfoTableSection {\n color?: TextProps['color']\n header: string\n helperText?: string\n items: Items\n}\n\nexport interface InfoTableProps {\n table:\n | {\n [header: string]: Items\n }\n | InfoTableSection[]\n}\n\nconst InfoTable: FunctionComponent<InfoTableProps> = ({table}) => {\n const sections = Array.isArray(table)\n ? table\n : Object.keys(table).map((header) => ({header, items: table[header]!, color: undefined, helperText: undefined}))\n\n const headerColumnWidth = Math.max(\n ...sections.map((section) => {\n return Math.max(\n ...section.header.split('\\n').map((line) => {\n return line.length\n }),\n )\n }),\n )\n\n return (\n <Box flexDirection=\"column\">\n {sections.map((section, index) => (\n <Box key={index} marginBottom={index === sections.length - 1 ? 0 : 1}>\n {section.header.length > 0 && (\n <Box width={headerColumnWidth + 1}>\n <Text color={section.color}>{capitalize(section.header)}:</Text>\n </Box>\n )}\n <Box marginLeft={section.header.length > 0 ? 2 : 0} flexGrow={1} flexDirection=\"column\">\n <List margin={false} items={section.items} color={section.color} />\n {section.helperText ? (\n <Box marginTop={1}>\n <Text color={section.color}>{section.helperText}</Text>\n </Box>\n ) : null}\n </Box>\n </Box>\n ))}\n </Box>\n )\n}\n\nexport {InfoTable}\n"]}
@@ -7,13 +7,14 @@ describe('InfoTable', async () => {
7
7
  test('renders a horizontal table with bullet points', async () => {
8
8
  const { lastFrame } = render(React.createElement(InfoTable, { table: {
9
9
  'header 1': ['some', 'items'],
10
- 'header 2': [['one item', { link: { label: 'Shopify', url: 'https://shopify.com' } }]],
10
+ 'header 2\nlonger text here': [['one item', { link: { label: 'Shopify', url: 'https://shopify.com' } }]],
11
11
  } }));
12
12
  expect(unstyled(lastFrame())).toMatchInlineSnapshot(`
13
- "Header 1: • some
14
- • items
13
+ "Header 1: • some
14
+ • items
15
15
 
16
- Header 2: • one item Shopify ( https://shopify.com )"
16
+ Header 2 • one item Shopify ( https://shopify.com )
17
+ longer text here:"
17
18
  `);
18
19
  });
19
20
  test('supports an empty header value', async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"InfoTable.test.js","sourceRoot":"","sources":["../../../../../../src/private/node/ui/components/Prompts/InfoTable.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAC,QAAQ,EAAC,MAAM,sCAAsC,CAAA;AAC7D,OAAO,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAA;AAC7C,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;IAC/B,IAAI,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CACxB,oBAAC,SAAS,IACR,KAAK,EAAE;gBACL,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;gBAC7B,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,qBAAqB,EAAC,EAAC,CAAC,CAAC;aACnF,GACD,CACH,CAAA;QAED,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;KAKpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CACxB,oBAAC,SAAS,IACR,KAAK,EAAE;gBACL,EAAE,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;aACtB,GACD,CACH,CAAA;QAED,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;KAGpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {InfoTable} from './InfoTable.js'\nimport {unstyled} from '../../../../../public/node/output.js'\nimport {render} from '../../../testing/ui.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\n\ndescribe('InfoTable', async () => {\n test('renders a horizontal table with bullet points', async () => {\n const {lastFrame} = render(\n <InfoTable\n table={{\n 'header 1': ['some', 'items'],\n 'header 2': [['one item', {link: {label: 'Shopify', url: 'https://shopify.com'}}]],\n }}\n />,\n )\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"Header 1: • some\n • items\n\n Header 2: • one item Shopify ( https://shopify.com )\"\n `)\n })\n\n test('supports an empty header value', async () => {\n const {lastFrame} = render(\n <InfoTable\n table={{\n '': ['some', 'items'],\n }}\n />,\n )\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"• some\n • items\"\n `)\n })\n})\n"]}
1
+ {"version":3,"file":"InfoTable.test.js","sourceRoot":"","sources":["../../../../../../src/private/node/ui/components/Prompts/InfoTable.test.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAC,MAAM,gBAAgB,CAAA;AACxC,OAAO,EAAC,QAAQ,EAAC,MAAM,sCAAsC,CAAA;AAC7D,OAAO,EAAC,MAAM,EAAC,MAAM,wBAAwB,CAAA;AAC7C,OAAO,EAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAC,MAAM,QAAQ,CAAA;AAC7C,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,QAAQ,CAAC,WAAW,EAAE,KAAK,IAAI,EAAE;IAC/B,IAAI,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CACxB,oBAAC,SAAS,IACR,KAAK,EAAE;gBACL,UAAU,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;gBAC7B,4BAA4B,EAAE,CAAC,CAAC,UAAU,EAAE,EAAC,IAAI,EAAE,EAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,qBAAqB,EAAC,EAAC,CAAC,CAAC;aACrG,GACD,CACH,CAAA;QAED,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;;;;KAMpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,EAAC,SAAS,EAAC,GAAG,MAAM,CACxB,oBAAC,SAAS,IACR,KAAK,EAAE;gBACL,EAAE,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;aACtB,GACD,CACH,CAAA;QAED,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAG,CAAC,CAAC,CAAC,qBAAqB,CAAC;;;KAGpD,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import {InfoTable} from './InfoTable.js'\nimport {unstyled} from '../../../../../public/node/output.js'\nimport {render} from '../../../testing/ui.js'\nimport {describe, expect, test} from 'vitest'\nimport React from 'react'\n\ndescribe('InfoTable', async () => {\n test('renders a horizontal table with bullet points', async () => {\n const {lastFrame} = render(\n <InfoTable\n table={{\n 'header 1': ['some', 'items'],\n 'header 2\\nlonger text here': [['one item', {link: {label: 'Shopify', url: 'https://shopify.com'}}]],\n }}\n />,\n )\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"Header 1: • some\n • items\n\n Header 2 • one item Shopify ( https://shopify.com )\n longer text here:\"\n `)\n })\n\n test('supports an empty header value', async () => {\n const {lastFrame} = render(\n <InfoTable\n table={{\n '': ['some', 'items'],\n }}\n />,\n )\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"• some\n • items\"\n `)\n })\n})\n"]}
@@ -1,6 +1,7 @@
1
1
  import { SelectInputProps } from './SelectInput.js';
2
2
  import { InfoTableProps } from './Prompts/InfoTable.js';
3
3
  import { InlineToken, LinkToken, TokenItem } from './TokenizedText.js';
4
+ import { AbortSignal } from '../../../../public/node/abort.js';
4
5
  import React, { ReactElement } from 'react';
5
6
  export interface SelectPromptProps<T> {
6
7
  message: TokenItem<Exclude<InlineToken, LinkToken>>;
@@ -9,6 +10,7 @@ export interface SelectPromptProps<T> {
9
10
  infoTable?: InfoTableProps['table'];
10
11
  defaultValue?: T;
11
12
  submitWithShortcuts?: boolean;
13
+ abortSignal?: AbortSignal;
12
14
  }
13
- declare function SelectPrompt<T>({ message, choices, infoTable, onSubmit, defaultValue, submitWithShortcuts, }: React.PropsWithChildren<SelectPromptProps<T>>): ReactElement | null;
15
+ declare function SelectPrompt<T>({ message, choices, infoTable, onSubmit, defaultValue, submitWithShortcuts, abortSignal, }: React.PropsWithChildren<SelectPromptProps<T>>): ReactElement | null;
14
16
  export { SelectPrompt };
@@ -4,12 +4,13 @@ import { TokenizedText } from './TokenizedText.js';
4
4
  import { handleCtrlC } from '../../ui.js';
5
5
  import { messageWithPunctuation } from '../utilities.js';
6
6
  import { uniqBy } from '../../../../public/common/array.js';
7
+ import useAbortSignal from '../hooks/use-abort-signal.js';
7
8
  import React, { useCallback, useLayoutEffect, useState } from 'react';
8
9
  import { Box, measureElement, Text, useApp, useInput, useStdout } from 'ink';
9
10
  import figures from 'figures';
10
11
  import ansiEscapes from 'ansi-escapes';
11
12
  // eslint-disable-next-line react/function-component-definition
12
- function SelectPrompt({ message, choices, infoTable, onSubmit, defaultValue, submitWithShortcuts = false, }) {
13
+ function SelectPrompt({ message, choices, infoTable, onSubmit, defaultValue, submitWithShortcuts = false, abortSignal, }) {
13
14
  if (choices.length === 0) {
14
15
  throw new Error('SelectPrompt requires at least one choice');
15
16
  }
@@ -58,13 +59,14 @@ function SelectPrompt({ message, choices, infoTable, onSubmit, defaultValue, sub
58
59
  unmountInk();
59
60
  onSubmit(answer.value);
60
61
  }, [stdout, wrapperHeight, unmountInk, onSubmit]);
62
+ const { isAborted } = useAbortSignal(abortSignal);
61
63
  useInput((input, key) => {
62
64
  handleCtrlC(input, key);
63
65
  if (key.return && answer) {
64
66
  submitAnswer(answer);
65
67
  }
66
68
  });
67
- return (React.createElement(Box, { flexDirection: "column", marginBottom: 1, ref: wrapperRef },
69
+ return isAborted ? null : (React.createElement(Box, { flexDirection: "column", marginBottom: 1, ref: wrapperRef },
68
70
  React.createElement(Box, null,
69
71
  React.createElement(Box, { marginRight: 2 },
70
72
  React.createElement(Text, null, "?")),
@@ -1 +1 @@
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, useLayoutEffect, 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 =\n typeof defaultValue === 'undefined' ? undefined : choices.find((choice) => choice.value === defaultValue)\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 useLayoutEffect(() => {\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 - 4)\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"]}
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 {AbortSignal} from '../../../../public/node/abort.js'\nimport useAbortSignal from '../hooks/use-abort-signal.js'\nimport React, {ReactElement, useCallback, useLayoutEffect, 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 abortSignal?: AbortSignal\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 abortSignal,\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 =\n typeof defaultValue === 'undefined' ? undefined : choices.find((choice) => choice.value === defaultValue)\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 useLayoutEffect(() => {\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 - 4)\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 const {isAborted} = useAbortSignal(abortSignal)\n\n useInput((input, key) => {\n handleCtrlC(input, key)\n\n if (key.return && answer) {\n submitAnswer(answer)\n }\n })\n\n return isAborted ? null : (\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"]}
@@ -2,6 +2,7 @@ import { SelectPrompt } from './SelectPrompt.js';
2
2
  import { getLastFrameAfterUnmount, sendInputAndWaitForChange, waitForInputsToBeReady, render } from '../../testing/ui.js';
3
3
  import { unstyled } from '../../../../public/node/output.js';
4
4
  import { Stdout } from '../../ui.js';
5
+ import { AbortController } from '../../../../public/node/abort.js';
5
6
  import { beforeEach, describe, expect, test, vi } from 'vitest';
6
7
  import React from 'react';
7
8
  import { useStdout } from 'ink';
@@ -251,5 +252,26 @@ describe('SelectPrompt', async () => {
251
252
  "
252
253
  `);
253
254
  });
255
+ test('abortController can be used to exit the prompt from outside', async () => {
256
+ const items = [
257
+ { label: 'a', value: 'a' },
258
+ { label: 'b', value: 'b' },
259
+ ];
260
+ const abortController = new AbortController();
261
+ const renderInstance = render(React.createElement(SelectPrompt, { choices: items, onSubmit: () => { }, message: "Test question?", abortSignal: abortController.signal }));
262
+ const promise = renderInstance.waitUntilExit();
263
+ expect(unstyled(renderInstance.lastFrame())).toMatchInlineSnapshot(`
264
+ "? Test question?
265
+
266
+ > (1) a
267
+ (2) b
268
+
269
+ Press ↑↓ arrows to select, enter to confirm
270
+ "
271
+ `);
272
+ abortController.abort();
273
+ expect(getLastFrameAfterUnmount(renderInstance)).toEqual('');
274
+ await expect(promise).resolves.toEqual(undefined);
275
+ });
254
276
  });
255
277
  //# sourceMappingURL=SelectPrompt.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SelectPrompt.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/SelectPrompt.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SelectPrompt} from './SelectPrompt.js'\nimport {getLastFrameAfterUnmount, sendInputAndWaitForChange, waitForInputsToBeReady, render} from '../../testing/ui.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {Stdout} from '../../ui.js'\nimport {beforeEach, describe, expect, test, vi} from 'vitest'\nimport React from 'react'\nimport {useStdout} from 'ink'\n\nvi.mock('ink', async () => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const original: any = await vi.importActual('ink')\n return {\n ...original,\n useStdout: vi.fn(),\n }\n})\n\nconst ARROW_DOWN = '\\u001B[B'\nconst ENTER = '\\r'\n\nbeforeEach(() => {\n vi.mocked(useStdout).mockReturnValue({\n stdout: new Stdout({\n columns: 80,\n rows: 80,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n }) as any,\n write: () => {},\n })\n})\n\ndescribe('SelectPrompt', async () => {\n test('choose an answer', async () => {\n const onEnter = vi.fn()\n\n const items = [\n {label: 'first', value: 'first'},\n {label: 'second', value: 'second'},\n {label: 'third', value: 'third'},\n ]\n\n const infoTable = {Add: ['new-ext'], Remove: ['integrated-demand-ext', 'order-discount']}\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n infoTable={infoTable}\n onSubmit={onEnter}\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ARROW_DOWN)\n await sendInputAndWaitForChange(renderInstance, ENTER)\n\n expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n \u001b[36m✔\u001b[39m \u001b[36msecond\u001b[39m\n \"\n `)\n\n expect(onEnter).toHaveBeenCalledWith(items[1]!.value)\n })\n\n test('renders groups', async () => {\n const items = [\n {label: 'first', value: 'first', group: 'Automations', key: 'f'},\n {label: 'second', value: 'second', group: 'Automations', key: 's'},\n {label: 'third', value: 'third', group: 'Merchant Admin'},\n {label: 'fourth', value: 'fourth', group: 'Merchant Admin'},\n {label: 'fifth', value: 'fifth', key: 'a'},\n {label: 'sixth', value: 'sixth'},\n {label: 'seventh', value: 'seventh'},\n {label: 'eighth', value: 'eighth'},\n {label: 'ninth', value: 'ninth'},\n {label: 'tenth', value: 'tenth'},\n ]\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n onSubmit={() => {}}\n />,\n )\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n\n \u001b[1mAutomations\u001b[22m\n \u001b[36m>\u001b[39m \u001b[36m(f) first\u001b[39m\n (s) second\n\n \u001b[1mMerchant Admin\u001b[22m\n (3) third\n (4) fourth\n\n \u001b[1mOther\u001b[22m\n (a) fifth\n (6) sixth\n (7) seventh\n (8) eighth\n (9) ninth\n (10) tenth\n\n \u001b[2mPress ↑↓ arrows to select, enter to confirm\u001b[22m\n \"\n `)\n })\n\n test('supports an info table', async () => {\n const items = [\n {label: 'first', value: 'first'},\n {label: 'second', value: 'second'},\n {label: 'third', value: 'third'},\n {label: 'fourth', value: 'fourth'},\n ]\n\n const infoTable = {\n Add: ['new-ext'],\n Remove: ['integrated-demand-ext', ['order-discount', {subdued: '(1)'}]],\n }\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n infoTable={infoTable}\n onSubmit={() => {}}\n />,\n )\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n\n Add: • new-ext\n\n Remove: • integrated-demand-ext\n • order-discount \u001b[2m(1)\u001b[22m\n\n \u001b[36m>\u001b[39m \u001b[36m(1) first\u001b[39m\n (2) second\n (3) third\n (4) fourth\n\n \u001b[2mPress ↑↓ arrows to select, enter to confirm\u001b[22m\n \"\n `)\n })\n\n test(\"it doesn't submit if there are no choices\", async () => {\n const onEnter = vi.fn()\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const items: any[] = []\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n onSubmit={onEnter}\n />,\n )\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toContain(\n 'ERROR SelectPrompt requires at least one choice',\n )\n })\n\n test(\"doesn't append a colon to the message if it ends with a question mark\", async () => {\n const {lastFrame} = render(\n <SelectPrompt choices={[{label: 'a', value: 'a'}]} onSubmit={() => {}} message=\"Test question?\" />,\n )\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n > (1) a\n\n Press ↑↓ arrows to select, enter to confirm\n \"\n `)\n })\n\n test('accepts a default value', async () => {\n const onEnter = vi.fn()\n const items = [\n {label: 'a', value: 'a'},\n {label: 'b', value: 'b'},\n ]\n\n const renderInstance = render(\n <SelectPrompt choices={items} onSubmit={onEnter} message=\"Test question?\" defaultValue=\"b\" />,\n )\n\n expect(unstyled(renderInstance.lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n (1) a\n > (2) b\n\n Press ↑↓ arrows to select, enter to confirm\n \"\n `)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n\n expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m✔\u001b[39m \u001b[36mb\u001b[39m\n \"\n `)\n expect(onEnter).toHaveBeenCalledWith(items[1]!.value)\n })\n\n test('can submit the initial value', async () => {\n const onEnter = vi.fn()\n const items = [\n {label: 'a', value: 'a'},\n {label: 'b', value: 'b'},\n ]\n\n const renderInstance = render(<SelectPrompt choices={items} onSubmit={onEnter} message=\"Test question?\" />)\n\n expect(unstyled(renderInstance.lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n > (1) a\n (2) b\n\n Press ↑↓ arrows to select, enter to confirm\n \"\n `)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n\n expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m✔\u001b[39m \u001b[36ma\u001b[39m\n \"\n `)\n expect(onEnter).toHaveBeenCalledWith(items[0]!.value)\n })\n\n test('allow submitting with a shortcut directly', async () => {\n const onEnter = vi.fn()\n const items = [\n {label: 'a', value: 'a', key: 'a'},\n {label: 'b', value: 'b', key: 'b'},\n ]\n\n const renderInstance = render(\n <SelectPrompt choices={items} onSubmit={onEnter} message=\"Test question?\" submitWithShortcuts />,\n )\n\n expect(unstyled(renderInstance.lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n > (a) a\n (b) b\n\n Press ↑↓ arrows to select, enter or a shortcut to confirm\n \"\n `)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'b')\n\n expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m✔\u001b[39m \u001b[36mb\u001b[39m\n \"\n `)\n expect(onEnter).toHaveBeenCalledWith(items[1]!.value)\n })\n\n test('adapts to the height of the container', async () => {\n vi.mocked(useStdout).mockReturnValue({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stdout: new Stdout({rows: 10}) as any,\n write: () => {},\n })\n\n const items = [\n {label: 'first', value: 'first', group: 'Automations', key: 'f'},\n {label: 'second', value: 'second', group: 'Automations', key: 's'},\n {label: 'third', value: 'third', group: 'Merchant Admin'},\n {label: 'fourth', value: 'fourth', group: 'Merchant Admin'},\n {label: 'fifth', value: 'fifth', key: 'a'},\n {label: 'sixth', value: 'sixth'},\n {label: 'seventh', value: 'seventh'},\n {label: 'eighth', value: 'eighth'},\n {label: 'ninth', value: 'ninth'},\n {label: 'tenth', value: 'tenth'},\n ]\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n onSubmit={() => {}}\n />,\n )\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n\n \u001b[1mAutomations\u001b[22m\n \u001b[36m>\u001b[39m \u001b[36m(f) first\u001b[39m\n (s) second\n\n \u001b[2mShowing 2 of 10 items.\u001b[22m\n \u001b[2mPress ↑↓ arrows to select, enter to confirm\u001b[22m\n \"\n `)\n })\n\n test('allows passing false as the default value', async () => {\n const items = [\n {label: 'yes', value: true},\n {label: 'no', value: false},\n ]\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n onSubmit={() => {}}\n defaultValue={false}\n />,\n )\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n\n (1) yes\n \u001b[36m>\u001b[39m \u001b[36m(2) no\u001b[39m\n\n \u001b[2mPress ↑↓ arrows to select, enter to confirm\u001b[22m\n \"\n `)\n })\n})\n"]}
1
+ {"version":3,"file":"SelectPrompt.test.js","sourceRoot":"","sources":["../../../../../src/private/node/ui/components/SelectPrompt.test.tsx"],"names":[],"mappings":"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","sourcesContent":["import {SelectPrompt} from './SelectPrompt.js'\nimport {getLastFrameAfterUnmount, sendInputAndWaitForChange, waitForInputsToBeReady, render} from '../../testing/ui.js'\nimport {unstyled} from '../../../../public/node/output.js'\nimport {Stdout} from '../../ui.js'\nimport {AbortController} from '../../../../public/node/abort.js'\nimport {beforeEach, describe, expect, test, vi} from 'vitest'\nimport React from 'react'\nimport {useStdout} from 'ink'\n\nvi.mock('ink', async () => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const original: any = await vi.importActual('ink')\n return {\n ...original,\n useStdout: vi.fn(),\n }\n})\n\nconst ARROW_DOWN = '\\u001B[B'\nconst ENTER = '\\r'\n\nbeforeEach(() => {\n vi.mocked(useStdout).mockReturnValue({\n stdout: new Stdout({\n columns: 80,\n rows: 80,\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n }) as any,\n write: () => {},\n })\n})\n\ndescribe('SelectPrompt', async () => {\n test('choose an answer', async () => {\n const onEnter = vi.fn()\n\n const items = [\n {label: 'first', value: 'first'},\n {label: 'second', value: 'second'},\n {label: 'third', value: 'third'},\n ]\n\n const infoTable = {Add: ['new-ext'], Remove: ['integrated-demand-ext', 'order-discount']}\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n infoTable={infoTable}\n onSubmit={onEnter}\n />,\n )\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ARROW_DOWN)\n await sendInputAndWaitForChange(renderInstance, ENTER)\n\n expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n \u001b[36m✔\u001b[39m \u001b[36msecond\u001b[39m\n \"\n `)\n\n expect(onEnter).toHaveBeenCalledWith(items[1]!.value)\n })\n\n test('renders groups', async () => {\n const items = [\n {label: 'first', value: 'first', group: 'Automations', key: 'f'},\n {label: 'second', value: 'second', group: 'Automations', key: 's'},\n {label: 'third', value: 'third', group: 'Merchant Admin'},\n {label: 'fourth', value: 'fourth', group: 'Merchant Admin'},\n {label: 'fifth', value: 'fifth', key: 'a'},\n {label: 'sixth', value: 'sixth'},\n {label: 'seventh', value: 'seventh'},\n {label: 'eighth', value: 'eighth'},\n {label: 'ninth', value: 'ninth'},\n {label: 'tenth', value: 'tenth'},\n ]\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n onSubmit={() => {}}\n />,\n )\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n\n \u001b[1mAutomations\u001b[22m\n \u001b[36m>\u001b[39m \u001b[36m(f) first\u001b[39m\n (s) second\n\n \u001b[1mMerchant Admin\u001b[22m\n (3) third\n (4) fourth\n\n \u001b[1mOther\u001b[22m\n (a) fifth\n (6) sixth\n (7) seventh\n (8) eighth\n (9) ninth\n (10) tenth\n\n \u001b[2mPress ↑↓ arrows to select, enter to confirm\u001b[22m\n \"\n `)\n })\n\n test('supports an info table', async () => {\n const items = [\n {label: 'first', value: 'first'},\n {label: 'second', value: 'second'},\n {label: 'third', value: 'third'},\n {label: 'fourth', value: 'fourth'},\n ]\n\n const infoTable = {\n Add: ['new-ext'],\n Remove: ['integrated-demand-ext', ['order-discount', {subdued: '(1)'}]],\n }\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n infoTable={infoTable}\n onSubmit={() => {}}\n />,\n )\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n\n Add: • new-ext\n\n Remove: • integrated-demand-ext\n • order-discount \u001b[2m(1)\u001b[22m\n\n \u001b[36m>\u001b[39m \u001b[36m(1) first\u001b[39m\n (2) second\n (3) third\n (4) fourth\n\n \u001b[2mPress ↑↓ arrows to select, enter to confirm\u001b[22m\n \"\n `)\n })\n\n test(\"it doesn't submit if there are no choices\", async () => {\n const onEnter = vi.fn()\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const items: any[] = []\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n onSubmit={onEnter}\n />,\n )\n\n expect(unstyled(getLastFrameAfterUnmount(renderInstance)!)).toContain(\n 'ERROR SelectPrompt requires at least one choice',\n )\n })\n\n test(\"doesn't append a colon to the message if it ends with a question mark\", async () => {\n const {lastFrame} = render(\n <SelectPrompt choices={[{label: 'a', value: 'a'}]} onSubmit={() => {}} message=\"Test question?\" />,\n )\n\n expect(unstyled(lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n > (1) a\n\n Press ↑↓ arrows to select, enter to confirm\n \"\n `)\n })\n\n test('accepts a default value', async () => {\n const onEnter = vi.fn()\n const items = [\n {label: 'a', value: 'a'},\n {label: 'b', value: 'b'},\n ]\n\n const renderInstance = render(\n <SelectPrompt choices={items} onSubmit={onEnter} message=\"Test question?\" defaultValue=\"b\" />,\n )\n\n expect(unstyled(renderInstance.lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n (1) a\n > (2) b\n\n Press ↑↓ arrows to select, enter to confirm\n \"\n `)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n\n expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m✔\u001b[39m \u001b[36mb\u001b[39m\n \"\n `)\n expect(onEnter).toHaveBeenCalledWith(items[1]!.value)\n })\n\n test('can submit the initial value', async () => {\n const onEnter = vi.fn()\n const items = [\n {label: 'a', value: 'a'},\n {label: 'b', value: 'b'},\n ]\n\n const renderInstance = render(<SelectPrompt choices={items} onSubmit={onEnter} message=\"Test question?\" />)\n\n expect(unstyled(renderInstance.lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n > (1) a\n (2) b\n\n Press ↑↓ arrows to select, enter to confirm\n \"\n `)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, ENTER)\n\n expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m✔\u001b[39m \u001b[36ma\u001b[39m\n \"\n `)\n expect(onEnter).toHaveBeenCalledWith(items[0]!.value)\n })\n\n test('allow submitting with a shortcut directly', async () => {\n const onEnter = vi.fn()\n const items = [\n {label: 'a', value: 'a', key: 'a'},\n {label: 'b', value: 'b', key: 'b'},\n ]\n\n const renderInstance = render(\n <SelectPrompt choices={items} onSubmit={onEnter} message=\"Test question?\" submitWithShortcuts />,\n )\n\n expect(unstyled(renderInstance.lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n > (a) a\n (b) b\n\n Press ↑↓ arrows to select, enter or a shortcut to confirm\n \"\n `)\n\n await waitForInputsToBeReady()\n await sendInputAndWaitForChange(renderInstance, 'b')\n\n expect(getLastFrameAfterUnmount(renderInstance)).toMatchInlineSnapshot(`\n \"? Test question?\n \u001b[36m✔\u001b[39m \u001b[36mb\u001b[39m\n \"\n `)\n expect(onEnter).toHaveBeenCalledWith(items[1]!.value)\n })\n\n test('adapts to the height of the container', async () => {\n vi.mocked(useStdout).mockReturnValue({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n stdout: new Stdout({rows: 10}) as any,\n write: () => {},\n })\n\n const items = [\n {label: 'first', value: 'first', group: 'Automations', key: 'f'},\n {label: 'second', value: 'second', group: 'Automations', key: 's'},\n {label: 'third', value: 'third', group: 'Merchant Admin'},\n {label: 'fourth', value: 'fourth', group: 'Merchant Admin'},\n {label: 'fifth', value: 'fifth', key: 'a'},\n {label: 'sixth', value: 'sixth'},\n {label: 'seventh', value: 'seventh'},\n {label: 'eighth', value: 'eighth'},\n {label: 'ninth', value: 'ninth'},\n {label: 'tenth', value: 'tenth'},\n ]\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n onSubmit={() => {}}\n />,\n )\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n\n \u001b[1mAutomations\u001b[22m\n \u001b[36m>\u001b[39m \u001b[36m(f) first\u001b[39m\n (s) second\n\n \u001b[2mShowing 2 of 10 items.\u001b[22m\n \u001b[2mPress ↑↓ arrows to select, enter to confirm\u001b[22m\n \"\n `)\n })\n\n test('allows passing false as the default value', async () => {\n const items = [\n {label: 'yes', value: true},\n {label: 'no', value: false},\n ]\n\n const renderInstance = render(\n <SelectPrompt\n message=\"Associate your project with the org Castile Ventures?\"\n choices={items}\n onSubmit={() => {}}\n defaultValue={false}\n />,\n )\n\n expect(renderInstance.lastFrame()).toMatchInlineSnapshot(`\n \"? Associate your project with the org Castile Ventures?\n\n (1) yes\n \u001b[36m>\u001b[39m \u001b[36m(2) no\u001b[39m\n\n \u001b[2mPress ↑↓ arrows to select, enter to confirm\u001b[22m\n \"\n `)\n })\n\n test('abortController can be used to exit the prompt from outside', async () => {\n const items = [\n {label: 'a', value: 'a'},\n {label: 'b', value: 'b'},\n ]\n\n const abortController = new AbortController()\n\n const renderInstance = render(\n <SelectPrompt\n choices={items}\n onSubmit={() => {}}\n message=\"Test question?\"\n abortSignal={abortController.signal}\n />,\n )\n\n const promise = renderInstance.waitUntilExit()\n\n expect(unstyled(renderInstance.lastFrame()!)).toMatchInlineSnapshot(`\n \"? Test question?\n\n > (1) a\n (2) b\n\n Press ↑↓ arrows to select, enter to confirm\n \"\n `)\n\n abortController.abort()\n\n expect(getLastFrameAfterUnmount(renderInstance)).toEqual('')\n await expect(promise).resolves.toEqual(undefined)\n })\n})\n"]}
@@ -1,3 +1,4 @@
1
+ import { AbortSignal } from '../../../../public/node/abort.js';
1
2
  import React from 'react';
2
3
  export interface Task<TContext = unknown> {
3
4
  title: string;
@@ -11,6 +12,7 @@ export interface TasksProps<TContext> {
11
12
  tasks: Task<TContext>[];
12
13
  silent?: boolean;
13
14
  onComplete?: (ctx: TContext) => void;
15
+ abortSignal?: AbortSignal;
14
16
  }
15
- declare function Tasks<TContext>({ tasks, silent, onComplete, }: React.PropsWithChildren<TasksProps<TContext>>): JSX.Element | null;
17
+ declare function Tasks<TContext>({ tasks, silent, onComplete, abortSignal, }: React.PropsWithChildren<TasksProps<TContext>>): JSX.Element | null;
16
18
  export { Tasks };
@@ -3,6 +3,7 @@ import useLayout from '../hooks/use-layout.js';
3
3
  import useAsyncAndUnmount from '../hooks/use-async-and-unmount.js';
4
4
  import { isUnitTest } from '../../../../public/node/context/local.js';
5
5
  import { handleCtrlC } from '../../ui.js';
6
+ import useAbortSignal from '../hooks/use-abort-signal.js';
6
7
  import { Box, Text, useInput, useStdin } from 'ink';
7
8
  import React, { useRef, useState } from 'react';
8
9
  const loadingBarChar = '▀';
@@ -38,7 +39,7 @@ async function runTask(task, ctx) {
38
39
  }
39
40
  const noop = () => { };
40
41
  // eslint-disable-next-line react/function-component-definition
41
- function Tasks({ tasks, silent = isUnitTest(), onComplete = noop, }) {
42
+ function Tasks({ tasks, silent = isUnitTest(), onComplete = noop, abortSignal, }) {
42
43
  const { twoThirds } = useLayout();
43
44
  const loadingBar = new Array(twoThirds).fill(loadingBarChar).join('');
44
45
  const [currentTask, setCurrentTask] = useState(tasks[0]);
@@ -65,7 +66,9 @@ function Tasks({ tasks, silent = isUnitTest(), onComplete = noop, }) {
65
66
  setState(TasksState.Success);
66
67
  onComplete(ctx.current);
67
68
  },
68
- onRejected: () => setState(TasksState.Failure),
69
+ onRejected: () => {
70
+ setState(TasksState.Failure);
71
+ },
69
72
  });
70
73
  useInput((input, key) => {
71
74
  handleCtrlC(input, key);
@@ -73,10 +76,11 @@ function Tasks({ tasks, silent = isUnitTest(), onComplete = noop, }) {
73
76
  return null;
74
77
  }
75
78
  }, { isActive: Boolean(isRawModeSupported) });
79
+ const { isAborted } = useAbortSignal(abortSignal);
76
80
  if (silent) {
77
81
  return null;
78
82
  }
79
- return state === TasksState.Loading ? (React.createElement(Box, { flexDirection: "column" },
83
+ return state === TasksState.Loading && !isAborted ? (React.createElement(Box, { flexDirection: "column" },
80
84
  React.createElement(TextAnimation, { text: loadingBar }),
81
85
  React.createElement(Text, null,
82
86
  currentTask.title,
@@ -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;AAiB1B,IAAK,UAIJ;AAJD,WAAK,UAAU;IACb,iCAAmB,CAAA;IACnB,iCAAmB,CAAA;IACnB,iCAAmB,CAAA;AACrB,CAAC,EAJI,UAAU,KAAV,UAAU,QAId;AAED,KAAK,UAAU,OAAO,CAAW,IAAoB,EAAE,GAAa;IAClE,IAAI,CAAC,UAAU,GAAG,CAAC,CAAA;IACnB,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA;IAChB,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAEjE,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,KAAK,EAAE,OAAO,EAAE,EAAE;QACjD,IAAI;YACF,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE;gBACpB,OAAM;aACP;YACD,4CAA4C;YAC5C,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;YACjC,8DAA8D;SAC/D;QAAC,OAAO,KAAU,EAAE;YACnB,IAAI,OAAO,KAAK,KAAK,EAAE;gBACrB,MAAM,KAAK,CAAA;aACZ;iBAAM;gBACL,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBACvB,IAAI,CAAC,UAAU,GAAG,OAAO,CAAA;aAC1B;SACF;KACF;AACH,CAAC;AAED,MAAM,IAAI,GAAG,GAAG,EAAE,GAAE,CAAC,CAAA;AAErB,+DAA+D;AAC/D,SAAS,KAAK,CAAW,EACvB,KAAK,EACL,MAAM,GAAG,UAAU,EAAE,EACrB,UAAU,GAAG,IAAI,GAC6B;IAC9C,MAAM,EAAC,SAAS,EAAC,GAAG,SAAS,EAAE,CAAA;IAC/B,MAAM,UAAU,GAAG,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACrE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAiB,KAAK,CAAC,CAAC,CAAE,CAAC,CAAA;IACzE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAa,UAAU,CAAC,OAAO,CAAC,CAAA;IAClE,MAAM,GAAG,GAAG,MAAM,CAAW,EAAc,CAAC,CAAA;IAC5C,MAAM,EAAC,kBAAkB,EAAC,GAAG,QAAQ,EAAE,CAAA;IAEvC,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE;YACxB,cAAc,CAAC,IAAI,CAAC,CAAA;YAEpB,4CAA4C;YAC5C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;YAEjD,WAAW;YACX,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,IAAI,IAAI,CAAC,EAAE;gBAC9F,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE;oBAC9B,cAAc,CAAC,OAAO,CAAC,CAAA;oBACvB,4CAA4C;oBAC5C,MAAM,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,CAAA;iBACpC;aACF;SACF;IACH,CAAC,CAAA;IAED,kBAAkB,CAAC,QAAQ,EAAE;QAC3B,WAAW,EAAE,GAAG,EAAE;YAChB,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAA;YAC5B,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACzB,CAAC;QACD,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC;KAC/C,CAAC,CAAA;IAEF,QAAQ,CACN,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACb,WAAW,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAEvB,IAAI,GAAG,CAAC,MAAM,EAAE;YACd,OAAO,IAAI,CAAA;SACZ;IACH,CAAC,EACD,EAAC,QAAQ,EAAE,OAAO,CAAC,kBAAkB,CAAC,EAAC,CACxC,CAAA;IAED,IAAI,MAAM,EAAE;QACV,OAAO,IAAI,CAAA;KACZ;IAED,OAAO,KAAK,KAAK,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CACpC,oBAAC,GAAG,IAAC,aAAa,EAAC,QAAQ;QACzB,oBAAC,aAAa,IAAC,IAAI,EAAE,UAAU,GAAI;QACnC,oBAAC,IAAI;YAAE,WAAW,CAAC,KAAK;mBAAY,CAChC,CACP,CAAC,CAAC,CAAC,IAAI,CAAA;AACV,CAAC;AAED,OAAO,EAAC,KAAK,EAAC,CAAA","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"]}
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 {AbortSignal} from '../../../../public/node/abort.js'\nimport useAbortSignal from '../hooks/use-abort-signal.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 abortSignal?: AbortSignal\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 abortSignal,\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: () => {\n setState(TasksState.Failure)\n },\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 const {isAborted} = useAbortSignal(abortSignal)\n\n if (silent) {\n return null\n }\n\n return state === TasksState.Loading && !isAborted ? (\n <Box flexDirection=\"column\">\n <TextAnimation text={loadingBar} />\n <Text>{currentTask.title} ...</Text>\n </Box>\n ) : null\n}\n\nexport {Tasks}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { Tasks } from './Tasks.js';
2
2
  import { getLastFrameAfterUnmount, render } from '../../testing/ui.js';
3
3
  import { unstyled } from '../../../../public/node/output.js';
4
+ import { AbortController } from '../../../../public/node/abort.js';
4
5
  import React from 'react';
5
6
  import { describe, expect, test, vi } from 'vitest';
6
7
  describe('Tasks', () => {
@@ -43,6 +44,7 @@ describe('Tasks', () => {
43
44
  });
44
45
  test('stops at the task that throws error', async () => {
45
46
  // Given
47
+ const abortController = new AbortController();
46
48
  const secondTaskFunction = vi.fn(async () => { });
47
49
  const firstTask = {
48
50
  title: 'task 1',
@@ -55,7 +57,7 @@ describe('Tasks', () => {
55
57
  task: secondTaskFunction,
56
58
  };
57
59
  // When
58
- const renderInstance = render(React.createElement(Tasks, { tasks: [firstTask, secondTask], silent: false }));
60
+ const renderInstance = render(React.createElement(Tasks, { tasks: [firstTask, secondTask], silent: false, abortSignal: abortController.signal }));
59
61
  // Then
60
62
  await expect(renderInstance.waitUntilExit()).rejects.toThrowError('something went wrong');
61
63
  expect(secondTaskFunction).toHaveBeenCalledTimes(0);
@@ -323,6 +325,25 @@ describe('Tasks', () => {
323
325
  // Then
324
326
  expect(context).toEqual({ foo: 'bar' });
325
327
  });
328
+ test('abortController can be used to exit from outside', async () => {
329
+ // Given
330
+ const abortController = new AbortController();
331
+ const firstTaskFunction = vi.fn(async () => {
332
+ await new Promise((resolve) => setTimeout(resolve, 10000));
333
+ });
334
+ const firstTask = {
335
+ title: 'task 1',
336
+ task: firstTaskFunction,
337
+ };
338
+ // When
339
+ const renderInstance = render(React.createElement(Tasks, { tasks: [firstTask], silent: false, abortSignal: abortController.signal }));
340
+ await taskHasRendered();
341
+ const promise = renderInstance.waitUntilExit();
342
+ abortController.abort();
343
+ // Then
344
+ expect(unstyled(getLastFrameAfterUnmount(renderInstance))).toEqual('');
345
+ await expect(promise).resolves.toEqual(undefined);
346
+ });
326
347
  });
327
348
  async function taskHasRendered() {
328
349
  await new Promise((resolve) => setTimeout(resolve, 100));