@jakubmazanec/cli 0.1.0-9029e49 → 0.1.0-bc209e5
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 +3 -1
- package/dist/program/Command.d.ts +2 -2
- package/dist/program/Command.js +3 -3
- package/dist/program/Command.js.map +1 -1
- package/dist/program/Program.d.ts +6 -3
- package/dist/program/Program.js +30 -12
- package/dist/program/Program.js.map +1 -1
- package/dist/program/ProgramOptions.d.ts +5 -1
- package/dist/program/ProgramOptions.js +1 -3
- package/dist/program/ProgramOptions.js.map +1 -1
- package/dist/program/ProgramStreams.d.ts +1 -1
- package/dist/program/ProgramStreams.js +1 -1
- package/dist/program/ProgramStreams.js.map +1 -1
- package/dist/program/getProgramDetail.js.map +1 -1
- package/dist/ui/Failure.d.ts +2 -2
- package/dist/ui/Failure.js +2 -2
- package/dist/ui/Failure.js.map +1 -1
- package/dist/ui/Header.d.ts +1 -1
- package/dist/ui/Header.js +1 -1
- package/dist/ui/Header.js.map +1 -1
- package/dist/ui/Help.d.ts +2 -2
- package/dist/ui/Help.js +1 -1
- package/dist/ui/Help.js.map +1 -1
- package/dist/ui/Spinner.js.map +1 -1
- package/dist/ui/form/FormRow.js +1 -1
- package/dist/ui/form/FormRow.js.map +1 -1
- package/dist/ui/form/SelectInput.d.ts +1 -1
- package/dist/ui/form/SelectInput.js +2 -2
- package/dist/ui/form/SelectInput.js.map +1 -1
- package/dist/ui/form/TextInput.js +1 -1
- package/dist/ui/form/TextInput.js.map +1 -1
- package/dist/ui/form/createForm.js +1 -1
- package/dist/ui/form/createForm.js.map +1 -1
- package/dist/ui/getErrorDetail.js +9 -0
- package/dist/ui/getErrorDetail.js.map +1 -1
- package/dist/ui/icons.js.map +1 -1
- package/dist/ui/internals/Wrapper.d.ts +2 -2
- package/dist/ui/internals/Wrapper.js +4 -4
- package/dist/ui/internals/Wrapper.js.map +1 -1
- package/dist/ui/internals/programContext.d.ts +1 -1
- package/dist/ui/internals/programContext.js +1 -1
- package/dist/ui/internals/programContext.js.map +1 -1
- package/dist/ui/internals/rotate.js.map +1 -1
- package/dist/ui/useProgram.d.ts +1 -1
- package/dist/ui/useProgram.js +5 -5
- package/dist/ui/useProgram.js.map +1 -1
- package/package.json +7 -6
- package/src/program/Command.ts +2 -2
- package/src/program/Program.ts +36 -5
- package/src/program/ProgramOptions.ts +7 -1
- package/src/program/ProgramStreams.ts +1 -1
- package/src/ui/Failure.tsx +2 -2
- package/src/ui/Header.tsx +1 -1
- package/src/ui/Help.tsx +2 -2
- package/src/ui/form/SelectInput.tsx +1 -4
- package/src/ui/getErrorDetail.ts +11 -0
- package/src/ui/internals/Wrapper.tsx +5 -5
- package/src/ui/internals/programContext.ts +1 -1
- package/src/ui/useProgram.ts +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/form/TextInput.tsx"],"sourcesContent":["// TODO: resolve\n/* eslint-disable complexity -- TODO */\nimport chalk from 'chalk';\nimport {Box, type BoxProps, Text, useInput} from 'ink';\nimport {useEffect, useState} from 'react';\n\nimport * as colors from '../colors.js';\nimport * as icons from '../icons.js';\n\n/**\n * Text input component props.\n */\nexport type TextInputProps = BoxProps & {\n /** Text to display as a label. */\n label: string;\n\n /** Text to display when `value` is empty. */\n placeholder?: string | undefined;\n\n /** Value. */\n value: string;\n\n /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be \"routed\" to a specific component. */\n focus?: boolean | undefined;\n\n /** Replace all chars and mask the value. Useful for password inputs. */\n mask?: string | undefined;\n\n /** Show cursor and allow navigation using arrow keys. */\n showCursor?: boolean | undefined;\n\n /** Validation function that is called when value changes. If this function throws an error*/\n validate?: ((value: string) => void) | undefined;\n\n /** Handler function that is called when value changes. */\n onChange: (value: string) => void;\n\n /** Handler function that is called when enter is pressed. */\n onSubmit?: ((value: string) => void) | undefined;\n\n /** Indicates whether value has been accepted. Component then doesn't accept more input. */\n isComplete?: boolean | undefined;\n};\n\n/**\n * Text input component.\n */\nexport function TextInput({\n label,\n validate,\n isComplete,\n value: originalValue,\n placeholder = '',\n focus = true,\n mask,\n showCursor = true,\n onChange,\n onSubmit,\n ...boxProps\n}: TextInputProps) {\n let [validationError, setValidationError] = useState('');\n let [state, setState] = useState({\n cursorOffset: (originalValue || '').length,\n cursorWidth: 0,\n });\n\n useEffect(() => {\n setState((previousState) => {\n if (!focus || !showCursor) {\n return previousState;\n }\n\n let newValue = originalValue || '';\n\n if (previousState.cursorOffset > newValue.length - 1) {\n return {\n cursorOffset: newValue.length,\n cursorWidth: 0,\n };\n }\n\n return previousState;\n });\n }, [originalValue, focus, showCursor]);\n\n let value = mask ? mask.repeat(originalValue.length) : originalValue;\n let renderedValue = value;\n let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;\n\n // fake mouse cursor; dealing with with actual cursor and ANSI escape codes is inconvinient\n if (showCursor && focus) {\n renderedPlaceholder =\n placeholder.length > 0\n ? chalk.inverse(placeholder[0]) + chalk[colors.muted](placeholder.slice(1))\n : chalk.inverse(' ');\n\n renderedValue = value.length > 0 ? '' : chalk.inverse(' ');\n\n let i = 0;\n\n for (let char of value) {\n if (i >= state.cursorOffset && i <= state.cursorOffset) {\n renderedValue += chalk.inverse(char);\n } else {\n renderedValue += char;\n }\n\n i += 1;\n }\n\n if (value.length > 0 && state.cursorOffset === value.length) {\n renderedValue += chalk.inverse(' ');\n }\n }\n\n useInput(\n (input, key) => {\n if (\n key.upArrow ||\n key.downArrow ||\n (key.ctrl && input === 'c') ||\n (key.shift && key.tab) ||\n key.tab\n ) {\n return;\n }\n\n if (key.return) {\n if (onSubmit) {\n if (validate) {\n try {\n validate(originalValue);\n } catch (error: unknown) {\n setValidationError((error as Error).message);\n\n return;\n }\n }\n\n if (validationError) {\n setValidationError('');\n } else {\n onSubmit(originalValue);\n }\n }\n\n return;\n }\n\n let nextCursorOffset = state.cursorOffset;\n let nextValue = originalValue;\n let nextCursorWidth = 0;\n\n if (key.leftArrow) {\n if (showCursor) {\n nextCursorOffset -= 1;\n }\n } else if (key.rightArrow) {\n if (showCursor) {\n nextCursorOffset += 1;\n }\n } else if (key.backspace || key.delete) {\n if (state.cursorOffset > 0) {\n nextValue =\n originalValue.slice(0, state.cursorOffset - 1) +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset -= 1;\n }\n } else {\n nextValue =\n originalValue.slice(0, state.cursorOffset) +\n input +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset += input.length;\n\n if (input.length > 1) {\n nextCursorWidth = input.length;\n }\n }\n\n if (state.cursorOffset < 0) {\n nextCursorOffset = 0;\n }\n\n if (state.cursorOffset > originalValue.length) {\n nextCursorOffset = originalValue.length;\n }\n\n setState({\n cursorOffset: nextCursorOffset,\n cursorWidth: nextCursorWidth,\n });\n\n if (nextValue !== originalValue) {\n onChange(nextValue);\n }\n },\n {isActive: focus},\n );\n\n return (\n <Box {...boxProps}>\n <Box marginRight={1}>\n <Text>{isComplete ? icons.success : icons.question}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n <Box marginRight={1}>\n <Text color={colors.highlighted}>{label}</Text>\n </Box>\n {isComplete ? (\n <Text color={colors.body}>{value}</Text>\n ) : (\n <Text color={colors.highlighted}>\n {placeholder ? (value.length > 0 ? renderedValue : renderedPlaceholder) : renderedValue}\n </Text>\n )}\n {validationError ? (\n <Box marginTop={0}>\n <Text color={colors.warning}>{validationError}</Text>\n </Box>\n ) : null}\n </Box>\n </Box>\n );\n}\n"],"names":["chalk","Box","Text","useInput","useEffect","useState","colors","icons","TextInput","label","validate","isComplete","value","originalValue","placeholder","focus","mask","showCursor","onChange","onSubmit","boxProps","validationError","setValidationError","state","setState","cursorOffset","length","cursorWidth","previousState","newValue","repeat","renderedValue","renderedPlaceholder","muted","undefined","inverse","slice","i","char","input","key","upArrow","downArrow","ctrl","shift","tab","return","error","message","nextCursorOffset","nextValue","nextCursorWidth","leftArrow","rightArrow","backspace","delete","isActive","marginRight","success","question","flexDirection","color","highlighted","body","marginTop","warning"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/form/TextInput.tsx"],"sourcesContent":["// TODO: resolve\n/* eslint-disable complexity -- TODO */\nimport chalk from 'chalk';\nimport {Box, type BoxProps, Text, useInput} from 'ink';\nimport {useEffect, useState} from 'react';\n\nimport * as colors from '../colors.js';\nimport * as icons from '../icons.js';\n\n/**\n * Text input component props.\n */\nexport type TextInputProps = BoxProps & {\n /** Text to display as a label. */\n label: string;\n\n /** Text to display when `value` is empty. */\n placeholder?: string | undefined;\n\n /** Value. */\n value: string;\n\n /** Should component listen to Listen to user's input? Useful in case there are multiple input components at the same time and input must be \"routed\" to a specific component. */\n focus?: boolean | undefined;\n\n /** Replace all chars and mask the value. Useful for password inputs. */\n mask?: string | undefined;\n\n /** Show cursor and allow navigation using arrow keys. */\n showCursor?: boolean | undefined;\n\n /** Validation function that is called when value changes. If this function throws an error*/\n validate?: ((value: string) => void) | undefined;\n\n /** Handler function that is called when value changes. */\n onChange: (value: string) => void;\n\n /** Handler function that is called when enter is pressed. */\n onSubmit?: ((value: string) => void) | undefined;\n\n /** Indicates whether value has been accepted. Component then doesn't accept more input. */\n isComplete?: boolean | undefined;\n};\n\n/**\n * Text input component.\n */\nexport function TextInput({\n label,\n validate,\n isComplete,\n value: originalValue,\n placeholder = '',\n focus = true,\n mask,\n showCursor = true,\n onChange,\n onSubmit,\n ...boxProps\n}: TextInputProps) {\n let [validationError, setValidationError] = useState('');\n let [state, setState] = useState({\n cursorOffset: (originalValue || '').length,\n cursorWidth: 0,\n });\n\n useEffect(() => {\n setState((previousState) => {\n if (!focus || !showCursor) {\n return previousState;\n }\n\n let newValue = originalValue || '';\n\n if (previousState.cursorOffset > newValue.length - 1) {\n return {\n cursorOffset: newValue.length,\n cursorWidth: 0,\n };\n }\n\n return previousState;\n });\n }, [originalValue, focus, showCursor]);\n\n let value = mask ? mask.repeat(originalValue.length) : originalValue;\n let renderedValue = value;\n let renderedPlaceholder = placeholder ? chalk[colors.muted](placeholder) : undefined;\n\n // fake mouse cursor; dealing with with actual cursor and ANSI escape codes is inconvinient\n if (showCursor && focus) {\n renderedPlaceholder =\n placeholder.length > 0\n ? chalk.inverse(placeholder[0]) + chalk[colors.muted](placeholder.slice(1))\n : chalk.inverse(' ');\n\n renderedValue = value.length > 0 ? '' : chalk.inverse(' ');\n\n let i = 0;\n\n for (let char of value) {\n if (i >= state.cursorOffset && i <= state.cursorOffset) {\n renderedValue += chalk.inverse(char);\n } else {\n renderedValue += char;\n }\n\n i += 1;\n }\n\n if (value.length > 0 && state.cursorOffset === value.length) {\n renderedValue += chalk.inverse(' ');\n }\n }\n\n useInput(\n (input, key) => {\n if (\n key.upArrow ||\n key.downArrow ||\n (key.ctrl && input === 'c') ||\n (key.shift && key.tab) ||\n key.tab\n ) {\n return;\n }\n\n if (key.return) {\n if (onSubmit) {\n if (validate) {\n try {\n validate(originalValue);\n } catch (error: unknown) {\n setValidationError((error as Error).message);\n\n return;\n }\n }\n\n if (validationError) {\n setValidationError('');\n } else {\n onSubmit(originalValue);\n }\n }\n\n return;\n }\n\n let nextCursorOffset = state.cursorOffset;\n let nextValue = originalValue;\n let nextCursorWidth = 0;\n\n if (key.leftArrow) {\n if (showCursor) {\n nextCursorOffset -= 1;\n }\n } else if (key.rightArrow) {\n if (showCursor) {\n nextCursorOffset += 1;\n }\n } else if (key.backspace || key.delete) {\n if (state.cursorOffset > 0) {\n nextValue =\n originalValue.slice(0, state.cursorOffset - 1) +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset -= 1;\n }\n } else {\n nextValue =\n originalValue.slice(0, state.cursorOffset) +\n input +\n originalValue.slice(state.cursorOffset, originalValue.length);\n\n nextCursorOffset += input.length;\n\n if (input.length > 1) {\n nextCursorWidth = input.length;\n }\n }\n\n if (state.cursorOffset < 0) {\n nextCursorOffset = 0;\n }\n\n if (state.cursorOffset > originalValue.length) {\n nextCursorOffset = originalValue.length;\n }\n\n setState({\n cursorOffset: nextCursorOffset,\n cursorWidth: nextCursorWidth,\n });\n\n if (nextValue !== originalValue) {\n onChange(nextValue);\n }\n },\n {isActive: focus},\n );\n\n return (\n <Box {...boxProps}>\n <Box marginRight={1}>\n <Text>{isComplete ? icons.success : icons.question}</Text>\n </Box>\n\n <Box flexDirection=\"column\">\n <Box marginRight={1}>\n <Text color={colors.highlighted}>{label}</Text>\n </Box>\n {isComplete ? (\n <Text color={colors.body}>{value}</Text>\n ) : (\n <Text color={colors.highlighted}>\n {placeholder ? (value.length > 0 ? renderedValue : renderedPlaceholder) : renderedValue}\n </Text>\n )}\n {validationError ? (\n <Box marginTop={0}>\n <Text color={colors.warning}>{validationError}</Text>\n </Box>\n ) : null}\n </Box>\n </Box>\n );\n}\n"],"names":["chalk","Box","Text","useInput","useEffect","useState","colors","icons","TextInput","label","validate","isComplete","value","originalValue","placeholder","focus","mask","showCursor","onChange","onSubmit","boxProps","validationError","setValidationError","state","setState","cursorOffset","length","cursorWidth","previousState","newValue","repeat","renderedValue","renderedPlaceholder","muted","undefined","inverse","slice","i","char","input","key","upArrow","downArrow","ctrl","shift","tab","return","error","message","nextCursorOffset","nextValue","nextCursorWidth","leftArrow","rightArrow","backspace","delete","isActive","marginRight","success","question","flexDirection","color","highlighted","body","marginTop","warning"],"mappings":"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"}
|
|
@@ -8,7 +8,7 @@ import { FormRow } from './FormRow.js';
|
|
|
8
8
|
* @param formConfig Form configuration.
|
|
9
9
|
* @returns Form component.
|
|
10
10
|
*/ export function createForm(formConfig) {
|
|
11
|
-
let Form = ({ rows
|
|
11
|
+
let Form = ({ rows, values: results, onChange, onComplete: onFinish, ...boxProps })=>{
|
|
12
12
|
let handleChange = useCallback((value, rowName)=>{
|
|
13
13
|
if (onChange) {
|
|
14
14
|
onChange({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/form/createForm.tsx"],"sourcesContent":["import {Box, type BoxProps} from 'ink';\nimport {useCallback} from 'react';\n\nimport {type Form} from './Form.js';\nimport {type FormConfig} from './FormConfig.js';\nimport {type FormProps} from './FormProps.js';\nimport {FormRow} from './FormRow.js';\n\n/**\n * Creates form component that contains individual rows with inputs based on the provided config.\n *\n * @param formConfig Form configuration.\n * @returns Form component.\n */\nexport function createForm<C extends FormConfig>(formConfig: C): Form<C> {\n let Form = ({\n rows,\n values: results,\n onChange,\n onComplete: onFinish,\n ...boxProps\n }: FormProps<C>) => {\n let handleChange = useCallback(\n (value: unknown, rowName: keyof C['rows']) => {\n if (onChange) {\n onChange({\n ...results,\n [rowName]: {\n ...results[rowName],\n value,\n },\n });\n }\n },\n [onChange, results],\n );\n\n let handleFinish = useCallback(\n (value: unknown, rowName: keyof C['rows']) => {\n onFinish({\n ...results,\n [rowName]: {\n value,\n isComplete: true,\n },\n });\n },\n [onFinish, results],\n );\n\n let inputElements = [];\n let activeInputName: string | null = null;\n\n for (let [rowName] of Object.entries(formConfig.rows)) {\n if (!results[rowName as keyof C['rows']].isComplete) {\n activeInputName = String(rowName);\n\n break;\n }\n }\n\n for (let [rowName, input] of Object.entries(formConfig.rows)) {\n if (results[rowName as keyof C['rows']].isComplete || rowName === activeInputName) {\n inputElements.push(\n <FormRow\n key={rowName}\n marginTop={inputElements.length === 0 ? 0 : 1}\n focus={rowName === activeInputName}\n isComplete={results[rowName as keyof C['rows']].isComplete}\n config={input}\n rows={rows}\n rowName={rowName}\n value={results[rowName as keyof C['rows']].value}\n onChange={handleChange}\n onComplete={handleFinish}\n />,\n );\n }\n }\n\n return (\n <Box {...(boxProps as BoxProps)} flexDirection=\"column\">\n {inputElements}\n </Box>\n );\n };\n\n return Object.assign(Form, {config: formConfig});\n}\n"],"names":["Box","useCallback","FormRow","createForm","formConfig","Form","rows","values","results","onChange","onComplete","onFinish","boxProps","handleChange","value","rowName","handleFinish","isComplete","inputElements","activeInputName","Object","entries","String","input","push","marginTop","length","focus","config","flexDirection","assign"],"mappings":";AAAA,SAAQA,GAAG,QAAsB,MAAM;AACvC,SAAQC,WAAW,QAAO,QAAQ;AAKlC,SAAQC,OAAO,QAAO,eAAe;AAErC;;;;;CAKC,GACD,OAAO,SAASC,WAAiCC,UAAa;IAC5D,IAAIC,OAAO,CAAC,EACVC,
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/form/createForm.tsx"],"sourcesContent":["import {Box, type BoxProps} from 'ink';\nimport {useCallback} from 'react';\n\nimport {type Form} from './Form.js';\nimport {type FormConfig} from './FormConfig.js';\nimport {type FormProps} from './FormProps.js';\nimport {FormRow} from './FormRow.js';\n\n/**\n * Creates form component that contains individual rows with inputs based on the provided config.\n *\n * @param formConfig Form configuration.\n * @returns Form component.\n */\nexport function createForm<C extends FormConfig>(formConfig: C): Form<C> {\n let Form = ({\n rows,\n values: results,\n onChange,\n onComplete: onFinish,\n ...boxProps\n }: FormProps<C>) => {\n let handleChange = useCallback(\n (value: unknown, rowName: keyof C['rows']) => {\n if (onChange) {\n onChange({\n ...results,\n [rowName]: {\n ...results[rowName],\n value,\n },\n });\n }\n },\n [onChange, results],\n );\n\n let handleFinish = useCallback(\n (value: unknown, rowName: keyof C['rows']) => {\n onFinish({\n ...results,\n [rowName]: {\n value,\n isComplete: true,\n },\n });\n },\n [onFinish, results],\n );\n\n let inputElements = [];\n let activeInputName: string | null = null;\n\n for (let [rowName] of Object.entries(formConfig.rows)) {\n if (!results[rowName as keyof C['rows']].isComplete) {\n activeInputName = String(rowName);\n\n break;\n }\n }\n\n for (let [rowName, input] of Object.entries(formConfig.rows)) {\n if (results[rowName as keyof C['rows']].isComplete || rowName === activeInputName) {\n inputElements.push(\n <FormRow\n key={rowName}\n marginTop={inputElements.length === 0 ? 0 : 1}\n focus={rowName === activeInputName}\n isComplete={results[rowName as keyof C['rows']].isComplete}\n config={input}\n rows={rows}\n rowName={rowName}\n value={results[rowName as keyof C['rows']].value}\n onChange={handleChange}\n onComplete={handleFinish}\n />,\n );\n }\n }\n\n return (\n <Box {...(boxProps as BoxProps)} flexDirection=\"column\">\n {inputElements}\n </Box>\n );\n };\n\n return Object.assign(Form, {config: formConfig});\n}\n"],"names":["Box","useCallback","FormRow","createForm","formConfig","Form","rows","values","results","onChange","onComplete","onFinish","boxProps","handleChange","value","rowName","handleFinish","isComplete","inputElements","activeInputName","Object","entries","String","input","push","marginTop","length","focus","config","flexDirection","assign"],"mappings":";AAAA,SAAQA,GAAG,QAAsB,MAAM;AACvC,SAAQC,WAAW,QAAO,QAAQ;AAKlC,SAAQC,OAAO,QAAO,eAAe;AAErC;;;;;CAKC,GACD,OAAO,SAASC,WAAiCC,UAAa;IAC5D,IAAIC,OAAO,CAAC,EACVC,IAAI,EACJC,QAAQC,OAAO,EACfC,QAAQ,EACRC,YAAYC,QAAQ,EACpB,GAAGC,UACU;QACb,IAAIC,eAAeZ,YACjB,CAACa,OAAgBC;YACf,IAAIN,UAAU;gBACZA,SAAS;oBACP,GAAGD,OAAO;oBACV,CAACO,QAAQ,EAAE;wBACT,GAAGP,OAAO,CAACO,QAAQ;wBACnBD;oBACF;gBACF;YACF;QACF,GACA;YAACL;YAAUD;SAAQ;QAGrB,IAAIQ,eAAef,YACjB,CAACa,OAAgBC;YACfJ,SAAS;gBACP,GAAGH,OAAO;gBACV,CAACO,QAAQ,EAAE;oBACTD;oBACAG,YAAY;gBACd;YACF;QACF,GACA;YAACN;YAAUH;SAAQ;QAGrB,IAAIU,gBAAgB,EAAE;QACtB,IAAIC,kBAAiC;QAErC,KAAK,IAAI,CAACJ,QAAQ,IAAIK,OAAOC,OAAO,CAACjB,WAAWE,IAAI,EAAG;YACrD,IAAI,CAACE,OAAO,CAACO,QAA2B,CAACE,UAAU,EAAE;gBACnDE,kBAAkBG,OAAOP;gBAEzB;YACF;QACF;QAEA,KAAK,IAAI,CAACA,SAASQ,MAAM,IAAIH,OAAOC,OAAO,CAACjB,WAAWE,IAAI,EAAG;YAC5D,IAAIE,OAAO,CAACO,QAA2B,CAACE,UAAU,IAAIF,YAAYI,iBAAiB;gBACjFD,cAAcM,IAAI,eAChB,KAACtB;oBAECuB,WAAWP,cAAcQ,MAAM,KAAK,IAAI,IAAI;oBAC5CC,OAAOZ,YAAYI;oBACnBF,YAAYT,OAAO,CAACO,QAA2B,CAACE,UAAU;oBAC1DW,QAAQL;oBACRjB,MAAMA;oBACNS,SAASA;oBACTD,OAAON,OAAO,CAACO,QAA2B,CAACD,KAAK;oBAChDL,UAAUI;oBACVH,YAAYM;mBATPD;YAYX;QACF;QAEA,qBACE,KAACf;YAAK,GAAIY,QAAQ;YAAeiB,eAAc;sBAC5CX;;IAGP;IAEA,OAAOE,OAAOU,MAAM,CAACzB,MAAM;QAACuB,QAAQxB;IAAU;AAChD"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ValidationError, ParsingError } from '@jakubmazanec/args';
|
|
2
|
+
import { zodIssuesToStrings } from '@jakubmazanec/zod-utils';
|
|
3
|
+
import { ZodError } from 'zod';
|
|
2
4
|
/**
|
|
3
5
|
* Creates detailed information about an error.
|
|
4
6
|
*/ export function getErrorDetail(error) {
|
|
@@ -37,6 +39,13 @@ import { ValidationError, ParsingError } from '@jakubmazanec/args';
|
|
|
37
39
|
}
|
|
38
40
|
return errorDetail;
|
|
39
41
|
}
|
|
42
|
+
if (error instanceof ZodError) {
|
|
43
|
+
let errorDetail = {
|
|
44
|
+
label: 'Error',
|
|
45
|
+
description: zodIssuesToStrings(error.issues).join('\n')
|
|
46
|
+
};
|
|
47
|
+
return errorDetail;
|
|
48
|
+
}
|
|
40
49
|
if (error instanceof Error) {
|
|
41
50
|
let errorDetail = {
|
|
42
51
|
label: 'Error',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ui/getErrorDetail.ts"],"sourcesContent":["import {ValidationError, ParsingError} from '@jakubmazanec/args';\n\nimport {type ErrorDetail} from './ErrorDetail.js';\n\n/**\n * Creates detailed information about an error.\n */\nexport function getErrorDetail(error: unknown): ErrorDetail {\n // TODO: add all codes\n if (error instanceof ParsingError) {\n switch (error.code) {\n default: {\n return {\n label: 'Parsing error',\n description: error.message,\n };\n }\n }\n }\n\n // TODO: add all codes\n if (error instanceof ValidationError) {\n switch (error.code) {\n default: {\n return {\n label: 'Validation error',\n description: error.message,\n };\n }\n }\n }\n\n // TODO: add all built-in errors\n if (error instanceof ReferenceError) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: error.message,\n };\n\n if (error.stack) {\n errorDetail.stack = error.stack;\n }\n\n return errorDetail;\n }\n\n if (error instanceof Error) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: error.message,\n };\n\n if (error.stack) {\n errorDetail.stack = error.stack;\n }\n\n if (error.cause instanceof Error) {\n errorDetail.cause = {\n description: error.cause.message,\n };\n\n if (error.cause.stack) {\n errorDetail.cause.stack = error.cause.stack;\n }\n }\n\n return errorDetail;\n }\n\n return {\n label: 'Error',\n };\n}\n"],"names":["ValidationError","ParsingError","getErrorDetail","error","code","label","description","message","ReferenceError","errorDetail","stack","Error","cause"],"mappings":"AAAA,SAAQA,eAAe,EAAEC,YAAY,QAAO,qBAAqB;
|
|
1
|
+
{"version":3,"sources":["../../src/ui/getErrorDetail.ts"],"sourcesContent":["import {ValidationError, ParsingError} from '@jakubmazanec/args';\nimport {zodIssuesToStrings} from '@jakubmazanec/zod-utils';\nimport {ZodError} from 'zod';\n\nimport {type ErrorDetail} from './ErrorDetail.js';\n\n/**\n * Creates detailed information about an error.\n */\nexport function getErrorDetail(error: unknown): ErrorDetail {\n // TODO: add all codes\n if (error instanceof ParsingError) {\n switch (error.code) {\n default: {\n return {\n label: 'Parsing error',\n description: error.message,\n };\n }\n }\n }\n\n // TODO: add all codes\n if (error instanceof ValidationError) {\n switch (error.code) {\n default: {\n return {\n label: 'Validation error',\n description: error.message,\n };\n }\n }\n }\n\n // TODO: add all built-in errors\n if (error instanceof ReferenceError) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: error.message,\n };\n\n if (error.stack) {\n errorDetail.stack = error.stack;\n }\n\n return errorDetail;\n }\n\n if (error instanceof ZodError) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: zodIssuesToStrings(error.issues).join('\\n'),\n };\n\n return errorDetail;\n }\n\n if (error instanceof Error) {\n let errorDetail: ErrorDetail = {\n label: 'Error',\n description: error.message,\n };\n\n if (error.stack) {\n errorDetail.stack = error.stack;\n }\n\n if (error.cause instanceof Error) {\n errorDetail.cause = {\n description: error.cause.message,\n };\n\n if (error.cause.stack) {\n errorDetail.cause.stack = error.cause.stack;\n }\n }\n\n return errorDetail;\n }\n\n return {\n label: 'Error',\n };\n}\n"],"names":["ValidationError","ParsingError","zodIssuesToStrings","ZodError","getErrorDetail","error","code","label","description","message","ReferenceError","errorDetail","stack","issues","join","Error","cause"],"mappings":"AAAA,SAAQA,eAAe,EAAEC,YAAY,QAAO,qBAAqB;AACjE,SAAQC,kBAAkB,QAAO,0BAA0B;AAC3D,SAAQC,QAAQ,QAAO,MAAM;AAI7B;;CAEC,GACD,OAAO,SAASC,eAAeC,KAAc;IAC3C,sBAAsB;IACtB,IAAIA,iBAAiBJ,cAAc;QACjC,OAAQI,MAAMC,IAAI;YAChB;gBAAS;oBACP,OAAO;wBACLC,OAAO;wBACPC,aAAaH,MAAMI,OAAO;oBAC5B;gBACF;QACF;IACF;IAEA,sBAAsB;IACtB,IAAIJ,iBAAiBL,iBAAiB;QACpC,OAAQK,MAAMC,IAAI;YAChB;gBAAS;oBACP,OAAO;wBACLC,OAAO;wBACPC,aAAaH,MAAMI,OAAO;oBAC5B;gBACF;QACF;IACF;IAEA,gCAAgC;IAChC,IAAIJ,iBAAiBK,gBAAgB;QACnC,IAAIC,cAA2B;YAC7BJ,OAAO;YACPC,aAAaH,MAAMI,OAAO;QAC5B;QAEA,IAAIJ,MAAMO,KAAK,EAAE;YACfD,YAAYC,KAAK,GAAGP,MAAMO,KAAK;QACjC;QAEA,OAAOD;IACT;IAEA,IAAIN,iBAAiBF,UAAU;QAC7B,IAAIQ,cAA2B;YAC7BJ,OAAO;YACPC,aAAaN,mBAAmBG,MAAMQ,MAAM,EAAEC,IAAI,CAAC;QACrD;QAEA,OAAOH;IACT;IAEA,IAAIN,iBAAiBU,OAAO;QAC1B,IAAIJ,cAA2B;YAC7BJ,OAAO;YACPC,aAAaH,MAAMI,OAAO;QAC5B;QAEA,IAAIJ,MAAMO,KAAK,EAAE;YACfD,YAAYC,KAAK,GAAGP,MAAMO,KAAK;QACjC;QAEA,IAAIP,MAAMW,KAAK,YAAYD,OAAO;YAChCJ,YAAYK,KAAK,GAAG;gBAClBR,aAAaH,MAAMW,KAAK,CAACP,OAAO;YAClC;YAEA,IAAIJ,MAAMW,KAAK,CAACJ,KAAK,EAAE;gBACrBD,YAAYK,KAAK,CAACJ,KAAK,GAAGP,MAAMW,KAAK,CAACJ,KAAK;YAC7C;QACF;QAEA,OAAOD;IACT;IAEA,OAAO;QACLJ,OAAO;IACT;AACF"}
|
package/dist/ui/icons.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ui/icons.ts"],"sourcesContent":["import chalk from 'chalk';\n\nimport * as colors from './colors.js';\n\nexport const question = chalk[colors.notice]('?');\nexport const info = chalk[colors.info]('i');\nexport const success = chalk[colors.success]('✓');\nexport const warning = chalk[colors.warning]('▲');\nexport const error = chalk[colors.failure]('×');\nexport const unselectedSquare = chalk[colors.unselected]('◻');\nexport const selectedSquare = chalk[colors.selected]('◼');\nexport const unselectedCircle = chalk[colors.unselected]('○');\nexport const selectedCircle = chalk[colors.selected]('●');\n"],"names":["chalk","colors","question","notice","info","success","warning","error","failure","unselectedSquare","unselected","selectedSquare","selected","unselectedCircle","selectedCircle"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAE1B,YAAYC,YAAY,cAAc;AAEtC,OAAO,MAAMC,WAAWF,KAAK,CAACC,OAAOE,
|
|
1
|
+
{"version":3,"sources":["../../src/ui/icons.ts"],"sourcesContent":["import chalk from 'chalk';\n\nimport * as colors from './colors.js';\n\nexport const question = chalk[colors.notice]('?');\nexport const info = chalk[colors.info]('i');\nexport const success = chalk[colors.success]('✓');\nexport const warning = chalk[colors.warning]('▲');\nexport const error = chalk[colors.failure]('×');\nexport const unselectedSquare = chalk[colors.unselected]('◻');\nexport const selectedSquare = chalk[colors.selected]('◼');\nexport const unselectedCircle = chalk[colors.unselected]('○');\nexport const selectedCircle = chalk[colors.selected]('●');\n"],"names":["chalk","colors","question","notice","info","success","warning","error","failure","unselectedSquare","unselected","selectedSquare","selected","unselectedCircle","selectedCircle"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAE1B,YAAYC,YAAY,cAAc;AAEtC,OAAO,MAAMC,WAAWF,KAAK,CAACC,OAAOE,MAAM,CAAC,CAAC,KAAK;AAClD,OAAO,MAAMC,OAAOJ,KAAK,CAACC,OAAOG,IAAI,CAAC,CAAC,KAAK;AAC5C,OAAO,MAAMC,UAAUL,KAAK,CAACC,OAAOI,OAAO,CAAC,CAAC,KAAK;AAClD,OAAO,MAAMC,UAAUN,KAAK,CAACC,OAAOK,OAAO,CAAC,CAAC,KAAK;AAClD,OAAO,MAAMC,QAAQP,KAAK,CAACC,OAAOO,OAAO,CAAC,CAAC,KAAK;AAChD,OAAO,MAAMC,mBAAmBT,KAAK,CAACC,OAAOS,UAAU,CAAC,CAAC,KAAK;AAC9D,OAAO,MAAMC,iBAAiBX,KAAK,CAACC,OAAOW,QAAQ,CAAC,CAAC,KAAK;AAC1D,OAAO,MAAMC,mBAAmBb,KAAK,CAACC,OAAOS,UAAU,CAAC,CAAC,KAAK;AAC9D,OAAO,MAAMI,iBAAiBd,KAAK,CAACC,OAAOW,QAAQ,CAAC,CAAC,KAAK"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type FC, type PropsWithChildren } from 'react';
|
|
2
2
|
import { type Program } from '../../program.js';
|
|
3
3
|
/**
|
|
4
|
-
* {@
|
|
4
|
+
* {@link Wrapper} component props.
|
|
5
5
|
*/
|
|
6
6
|
export type WrapperProps = {
|
|
7
7
|
program: Program;
|
|
8
8
|
};
|
|
9
9
|
/**
|
|
10
|
-
* A component that wraps anything {@
|
|
10
|
+
* A component that wraps anything {@link Program} tries to render with an error boundary and all neccessary providers.
|
|
11
11
|
*/
|
|
12
12
|
export declare const Wrapper: FC<PropsWithChildren<WrapperProps>>;
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box } from 'ink';
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
|
-
import
|
|
4
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
|
5
5
|
import { programContext } from './programContext.js';
|
|
6
6
|
import { Failure } from '../Failure.js';
|
|
7
7
|
/**
|
|
8
|
-
* A component that wraps anything {@
|
|
9
|
-
*/ export const Wrapper = ({ program
|
|
8
|
+
* A component that wraps anything {@link Program} tries to render with an error boundary and all neccessary providers.
|
|
9
|
+
*/ export const Wrapper = ({ program, children })=>{
|
|
10
10
|
let programContextValue = useMemo(()=>({
|
|
11
11
|
program
|
|
12
12
|
}), [
|
|
13
13
|
program
|
|
14
14
|
]);
|
|
15
|
-
return /*#__PURE__*/ _jsx(
|
|
15
|
+
return /*#__PURE__*/ _jsx(ErrorBoundary, {
|
|
16
16
|
FallbackComponent: Failure,
|
|
17
17
|
children: /*#__PURE__*/ _jsx(programContext.Provider, {
|
|
18
18
|
value: programContextValue,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/internals/Wrapper.tsx"],"sourcesContent":["import {Box} from 'ink';\nimport {useMemo, type FC, type PropsWithChildren} from 'react';\nimport
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/internals/Wrapper.tsx"],"sourcesContent":["import {Box} from 'ink';\nimport {useMemo, type FC, type PropsWithChildren} from 'react';\nimport {ErrorBoundary} from 'react-error-boundary';\n\nimport {programContext} from './programContext.js';\nimport {type Program} from '../../program.js';\nimport {Failure} from '../Failure.js';\n\n/**\n * {@link Wrapper} component props.\n */\nexport type WrapperProps = {\n program: Program;\n};\n\n/**\n * A component that wraps anything {@link Program} tries to render with an error boundary and all neccessary providers.\n */\nexport const Wrapper: FC<PropsWithChildren<WrapperProps>> = ({program, children}) => {\n let programContextValue = useMemo(\n () => ({\n program,\n }),\n [program],\n );\n\n return (\n <ErrorBoundary FallbackComponent={Failure}>\n <programContext.Provider value={programContextValue}>\n <Box>{children}</Box>\n </programContext.Provider>\n </ErrorBoundary>\n );\n};\n"],"names":["Box","useMemo","ErrorBoundary","programContext","Failure","Wrapper","program","children","programContextValue","FallbackComponent","Provider","value"],"mappings":";AAAA,SAAQA,GAAG,QAAO,MAAM;AACxB,SAAQC,OAAO,QAAwC,QAAQ;AAC/D,SAAQC,aAAa,QAAO,uBAAuB;AAEnD,SAAQC,cAAc,QAAO,sBAAsB;AAEnD,SAAQC,OAAO,QAAO,gBAAgB;AAStC;;CAEC,GACD,OAAO,MAAMC,UAA+C,CAAC,EAACC,OAAO,EAAEC,QAAQ,EAAC;IAC9E,IAAIC,sBAAsBP,QACxB,IAAO,CAAA;YACLK;QACF,CAAA,GACA;QAACA;KAAQ;IAGX,qBACE,KAACJ;QAAcO,mBAAmBL;kBAChC,cAAA,KAACD,eAAeO,QAAQ;YAACC,OAAOH;sBAC9B,cAAA,KAACR;0BAAKO;;;;AAId,EAAE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/internals/programContext.ts"],"sourcesContent":["import React from 'react';\n\nimport {type Program} from '../../program/Program.js';\n\nexport type ProgramContext = {\n program: Program;\n};\n\n/**\n * A React context that stores {@
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/internals/programContext.ts"],"sourcesContent":["import React from 'react';\n\nimport {type Program} from '../../program/Program.js';\n\nexport type ProgramContext = {\n program: Program;\n};\n\n/**\n * A React context that stores {@link Program} instance.\n */\nexport const programContext = React.createContext<ProgramContext>({} as ProgramContext);\n"],"names":["React","programContext","createContext"],"mappings":"AAAA,OAAOA,WAAW,QAAQ;AAQ1B;;CAEC,GACD,OAAO,MAAMC,iBAAiBD,MAAME,aAAa,CAAiB,CAAC,GAAqB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/ui/internals/rotate.ts"],"sourcesContent":["/**\n * Rotate an array by `n` places.\n *\n * @param value An array to rotate.\n * @param n By how many places the items should move.\n * @returns The new item with items rotated.\n */\nexport function rotate<T>(value: T[], n: number) {\n let x = [...value];\n\n return [...x.splice(-n % x.length), ...x];\n}\n"],"names":["rotate","value","n","x","splice","length"],"mappings":"AAAA;;;;;;CAMC,GACD,OAAO,SAASA,OAAUC,KAAU,EAAEC,CAAS;IAC7C,IAAIC,IAAI;WAAIF;KAAM;IAElB,OAAO;WAAIE,EAAEC,
|
|
1
|
+
{"version":3,"sources":["../../../src/ui/internals/rotate.ts"],"sourcesContent":["/**\n * Rotate an array by `n` places.\n *\n * @param value An array to rotate.\n * @param n By how many places the items should move.\n * @returns The new item with items rotated.\n */\nexport function rotate<T>(value: T[], n: number) {\n let x = [...value];\n\n return [...x.splice(-n % x.length), ...x];\n}\n"],"names":["rotate","value","n","x","splice","length"],"mappings":"AAAA;;;;;;CAMC,GACD,OAAO,SAASA,OAAUC,KAAU,EAAEC,CAAS;IAC7C,IAAIC,IAAI;WAAIF;KAAM;IAElB,OAAO;WAAIE,EAAEC,MAAM,CAAC,CAACF,IAAIC,EAAEE,MAAM;WAAMF;KAAE;AAC3C"}
|
package/dist/ui/useProgram.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* A React hook for accessing {@
|
|
2
|
+
* A React hook for accessing {@link Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.
|
|
3
3
|
*/
|
|
4
4
|
export declare function useProgram(): {
|
|
5
5
|
program: import("../program.js").Program;
|
package/dist/ui/useProgram.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { useApp } from 'ink';
|
|
2
2
|
import { useCallback, useContext } from 'react';
|
|
3
|
-
import
|
|
3
|
+
import { useErrorBoundary } from 'react-error-boundary';
|
|
4
4
|
import { programContext } from './internals.js';
|
|
5
5
|
/**
|
|
6
|
-
* A React hook for accessing {@
|
|
6
|
+
* A React hook for accessing {@link Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.
|
|
7
7
|
*/ export function useProgram() {
|
|
8
|
-
let { showBoundary
|
|
9
|
-
let { exit: appExit
|
|
10
|
-
let { program
|
|
8
|
+
let { showBoundary } = useErrorBoundary();
|
|
9
|
+
let { exit: appExit } = useApp();
|
|
10
|
+
let { program } = useContext(programContext);
|
|
11
11
|
let exit = useCallback((error)=>{
|
|
12
12
|
showBoundary(error);
|
|
13
13
|
appExit(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ui/useProgram.ts"],"sourcesContent":["import {useApp} from 'ink';\nimport {useCallback, useContext} from 'react';\nimport
|
|
1
|
+
{"version":3,"sources":["../../src/ui/useProgram.ts"],"sourcesContent":["import {useApp} from 'ink';\nimport {useCallback, useContext} from 'react';\nimport {useErrorBoundary} from 'react-error-boundary';\n\nimport {programContext} from './internals.js';\n\n/**\n * A React hook for accessing {@link Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.\n */\nexport function useProgram() {\n let {showBoundary} = useErrorBoundary();\n let {exit: appExit} = useApp();\n let {program} = useContext(programContext);\n\n let exit = useCallback(\n (error: unknown) => {\n showBoundary(error);\n appExit(error as Error);\n },\n [appExit, showBoundary],\n );\n\n return {program, exit};\n}\n"],"names":["useApp","useCallback","useContext","useErrorBoundary","programContext","useProgram","showBoundary","exit","appExit","program","error"],"mappings":"AAAA,SAAQA,MAAM,QAAO,MAAM;AAC3B,SAAQC,WAAW,EAAEC,UAAU,QAAO,QAAQ;AAC9C,SAAQC,gBAAgB,QAAO,uBAAuB;AAEtD,SAAQC,cAAc,QAAO,iBAAiB;AAE9C;;CAEC,GACD,OAAO,SAASC;IACd,IAAI,EAACC,YAAY,EAAC,GAAGH;IACrB,IAAI,EAACI,MAAMC,OAAO,EAAC,GAAGR;IACtB,IAAI,EAACS,OAAO,EAAC,GAAGP,WAAWE;IAE3B,IAAIG,OAAON,YACT,CAACS;QACCJ,aAAaI;QACbF,QAAQE;IACV,GACA;QAACF;QAASF;KAAa;IAGzB,OAAO;QAACG;QAASF;IAAI;AACvB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jakubmazanec/cli",
|
|
3
|
-
"version": "0.1.0-
|
|
3
|
+
"version": "0.1.0-bc209e5",
|
|
4
4
|
"description": "TypeScript-first library for building CLIs.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,13 +27,14 @@
|
|
|
27
27
|
"typecheck": "tsc --project tsconfig.typecheck.json"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@jakubmazanec/args": "1.0.1-
|
|
31
|
-
"@jakubmazanec/error": "2.0.1-
|
|
32
|
-
"@jakubmazanec/ts-utils": "2.1.0-
|
|
33
|
-
"@jakubmazanec/zod-utils": "1.0.0-
|
|
30
|
+
"@jakubmazanec/args": "1.0.1-bc209e5",
|
|
31
|
+
"@jakubmazanec/error": "2.0.1-bc209e5",
|
|
32
|
+
"@jakubmazanec/ts-utils": "2.1.0-bc209e5",
|
|
33
|
+
"@jakubmazanec/zod-utils": "1.0.0-bc209e5",
|
|
34
34
|
"chalk": "^5.2.0",
|
|
35
35
|
"lodash": "^4.17.21",
|
|
36
|
-
"react-error-boundary": "^4.0.7"
|
|
36
|
+
"react-error-boundary": "^4.0.7",
|
|
37
|
+
"zod": "^3.14.4"
|
|
37
38
|
},
|
|
38
39
|
"devDependencies": {
|
|
39
40
|
"@jest/globals": "^29.5.0",
|
package/src/program/Command.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {type Program} from './Program.js';
|
|
|
12
12
|
// import {type ProgramCommand} from './ProgramCommand.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Command encapsulates piece of a CLI's logic and is executed when {@
|
|
15
|
+
* Command encapsulates piece of a CLI's logic and is executed when {@link Program} instance parses that command from the command line arguments.
|
|
16
16
|
*/
|
|
17
17
|
export class Command<
|
|
18
18
|
A extends string,
|
|
@@ -104,7 +104,7 @@ export class Command<
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
* Executes command action with provided parsed arguments and {@
|
|
107
|
+
* Executes command action with provided parsed arguments and {@link Program} instance.
|
|
108
108
|
*/
|
|
109
109
|
async run(
|
|
110
110
|
parsedArgs: Arguments<{
|
package/src/program/Program.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any -- needed */
|
|
2
2
|
import {type ParametersConfig, type Argv, type OptionsConfig} from '@jakubmazanec/args';
|
|
3
|
+
import {type ComponentType} from 'react';
|
|
4
|
+
import {type FallbackProps} from 'react-error-boundary';
|
|
3
5
|
|
|
4
6
|
import {type Command} from './Command.js';
|
|
5
7
|
import {ProgramError} from './ProgramError.js';
|
|
@@ -9,7 +11,7 @@ import {Wrapper} from '../ui/internals.js';
|
|
|
9
11
|
import {Failure, Help} from '../ui.js';
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
|
-
* Program manages and executes {@
|
|
14
|
+
* Program manages and executes {@link Command} instances attached to it based on parsing command line arguments.
|
|
13
15
|
*/
|
|
14
16
|
export class Program {
|
|
15
17
|
/** Program name. */
|
|
@@ -37,7 +39,10 @@ export class Program {
|
|
|
37
39
|
stdout: process.stdout,
|
|
38
40
|
};
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
/** Custom error boundary fallback component */
|
|
43
|
+
ErrorBoundaryFallbackComponent: ComponentType<FallbackProps>;
|
|
44
|
+
|
|
45
|
+
constructor({name, version, description, bin, ErrorBoundaryFallbackComponent}: ProgramOptions) {
|
|
41
46
|
if (name) {
|
|
42
47
|
this.name = name;
|
|
43
48
|
}
|
|
@@ -53,6 +58,12 @@ export class Program {
|
|
|
53
58
|
if (bin) {
|
|
54
59
|
this.bin = bin;
|
|
55
60
|
}
|
|
61
|
+
|
|
62
|
+
if (ErrorBoundaryFallbackComponent) {
|
|
63
|
+
this.ErrorBoundaryFallbackComponent = ErrorBoundaryFallbackComponent;
|
|
64
|
+
} else {
|
|
65
|
+
this.ErrorBoundaryFallbackComponent = Failure;
|
|
66
|
+
}
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
/**
|
|
@@ -92,7 +103,9 @@ export class Program {
|
|
|
92
103
|
});
|
|
93
104
|
} catch (error: unknown) {
|
|
94
105
|
if (error instanceof Error) {
|
|
95
|
-
await this.renderElement(
|
|
106
|
+
await this.renderElement(
|
|
107
|
+
createElement(this.ErrorBoundaryFallbackComponent, {error, resetErrorBoundary: () => {}}),
|
|
108
|
+
);
|
|
96
109
|
}
|
|
97
110
|
} finally {
|
|
98
111
|
this.isRendering = false;
|
|
@@ -117,10 +130,28 @@ export class Program {
|
|
|
117
130
|
let {createElement} = await import('react');
|
|
118
131
|
|
|
119
132
|
for (let error of parsedArgs.errors) {
|
|
120
|
-
await this.renderElement(
|
|
133
|
+
await this.renderElement(
|
|
134
|
+
createElement(this.ErrorBoundaryFallbackComponent, {
|
|
135
|
+
error,
|
|
136
|
+
resetErrorBoundary: () => {},
|
|
137
|
+
}),
|
|
138
|
+
);
|
|
121
139
|
}
|
|
122
140
|
} else {
|
|
123
|
-
|
|
141
|
+
try {
|
|
142
|
+
await command.run(parsedArgs, this);
|
|
143
|
+
} catch (error: unknown) {
|
|
144
|
+
let {createElement} = await import('react');
|
|
145
|
+
|
|
146
|
+
if (error instanceof Error) {
|
|
147
|
+
await this.renderElement(
|
|
148
|
+
createElement(this.ErrorBoundaryFallbackComponent, {
|
|
149
|
+
error,
|
|
150
|
+
resetErrorBoundary: () => {},
|
|
151
|
+
}),
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
124
155
|
|
|
125
156
|
break;
|
|
126
157
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import {type ComponentType} from 'react';
|
|
2
|
+
import {type FallbackProps} from 'react-error-boundary';
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
|
-
* Options for creating new {@
|
|
5
|
+
* Options for creating new {@link Program} instance.
|
|
3
6
|
*/
|
|
4
7
|
export type ProgramOptions = {
|
|
5
8
|
/** Program name. */
|
|
@@ -13,4 +16,7 @@ export type ProgramOptions = {
|
|
|
13
16
|
|
|
14
17
|
/** Program executable name. */
|
|
15
18
|
bin?: string | undefined;
|
|
19
|
+
|
|
20
|
+
/** Custom error boundary fallback component */
|
|
21
|
+
ErrorBoundaryFallbackComponent?: ComponentType<FallbackProps>;
|
|
16
22
|
};
|
package/src/ui/Failure.tsx
CHANGED
|
@@ -7,10 +7,10 @@ import {Header} from './Header.js';
|
|
|
7
7
|
import * as icons from './icons.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* {@
|
|
10
|
+
* {@link Failure} component props.
|
|
11
11
|
*/
|
|
12
12
|
export type FailureProps = BoxProps & {
|
|
13
|
-
/** Error that describes a failure state. Function {@
|
|
13
|
+
/** Error that describes a failure state. Function {@link getErrorDetail} is used for getting all the information from the error; you can use it to implement own retrieval logic and pass the result as `errorDetail` prop. */
|
|
14
14
|
error: Error;
|
|
15
15
|
|
|
16
16
|
/** Information about the failure state. Overrides information retireved from `error` prop. */
|
package/src/ui/Header.tsx
CHANGED
|
@@ -4,7 +4,7 @@ import {type PropsWithChildren} from 'react';
|
|
|
4
4
|
import {Spinner} from './Spinner.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* {@
|
|
7
|
+
* {@link Header} component props.
|
|
8
8
|
*/
|
|
9
9
|
export type HeaderProps = BoxProps & {
|
|
10
10
|
/** Icon (recommended is 1 character) to render before the header itself. */
|
package/src/ui/Help.tsx
CHANGED
|
@@ -7,10 +7,10 @@ import * as icons from './icons.js';
|
|
|
7
7
|
import {type Program} from '../program.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* {@
|
|
10
|
+
* {@link Help} component props.
|
|
11
11
|
*/
|
|
12
12
|
export type HelpProps = BoxProps & {
|
|
13
|
-
/** {@
|
|
13
|
+
/** {@link Program} instance. */
|
|
14
14
|
program: Program;
|
|
15
15
|
};
|
|
16
16
|
|
|
@@ -47,7 +47,6 @@ export function SelectInput({
|
|
|
47
47
|
limit: customLimit,
|
|
48
48
|
onSelect,
|
|
49
49
|
onHighlight,
|
|
50
|
-
marginTop = 0,
|
|
51
50
|
isComplete,
|
|
52
51
|
...boxProps
|
|
53
52
|
}: SelectInputProps): JSX.Element {
|
|
@@ -151,9 +150,7 @@ export function SelectInput({
|
|
|
151
150
|
<Text color={colors.unselected}>{icons.unselectedCircle}</Text>
|
|
152
151
|
)}
|
|
153
152
|
</Box>
|
|
154
|
-
<Text
|
|
155
|
-
color={isSelected ? colors.highlighted : colors.muted}
|
|
156
|
-
>{`${item.label}`}</Text>
|
|
153
|
+
<Text color={isSelected ? colors.highlighted : colors.muted}>{item.label}</Text>
|
|
157
154
|
</Box>
|
|
158
155
|
);
|
|
159
156
|
})}
|
package/src/ui/getErrorDetail.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import {ValidationError, ParsingError} from '@jakubmazanec/args';
|
|
2
|
+
import {zodIssuesToStrings} from '@jakubmazanec/zod-utils';
|
|
3
|
+
import {ZodError} from 'zod';
|
|
2
4
|
|
|
3
5
|
import {type ErrorDetail} from './ErrorDetail.js';
|
|
4
6
|
|
|
@@ -44,6 +46,15 @@ export function getErrorDetail(error: unknown): ErrorDetail {
|
|
|
44
46
|
return errorDetail;
|
|
45
47
|
}
|
|
46
48
|
|
|
49
|
+
if (error instanceof ZodError) {
|
|
50
|
+
let errorDetail: ErrorDetail = {
|
|
51
|
+
label: 'Error',
|
|
52
|
+
description: zodIssuesToStrings(error.issues).join('\n'),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return errorDetail;
|
|
56
|
+
}
|
|
57
|
+
|
|
47
58
|
if (error instanceof Error) {
|
|
48
59
|
let errorDetail: ErrorDetail = {
|
|
49
60
|
label: 'Error',
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import {Box} from 'ink';
|
|
2
2
|
import {useMemo, type FC, type PropsWithChildren} from 'react';
|
|
3
|
-
import
|
|
3
|
+
import {ErrorBoundary} from 'react-error-boundary';
|
|
4
4
|
|
|
5
5
|
import {programContext} from './programContext.js';
|
|
6
6
|
import {type Program} from '../../program.js';
|
|
7
7
|
import {Failure} from '../Failure.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* {@
|
|
10
|
+
* {@link Wrapper} component props.
|
|
11
11
|
*/
|
|
12
12
|
export type WrapperProps = {
|
|
13
13
|
program: Program;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* A component that wraps anything {@
|
|
17
|
+
* A component that wraps anything {@link Program} tries to render with an error boundary and all neccessary providers.
|
|
18
18
|
*/
|
|
19
19
|
export const Wrapper: FC<PropsWithChildren<WrapperProps>> = ({program, children}) => {
|
|
20
20
|
let programContextValue = useMemo(
|
|
@@ -25,10 +25,10 @@ export const Wrapper: FC<PropsWithChildren<WrapperProps>> = ({program, children}
|
|
|
25
25
|
);
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
|
-
<
|
|
28
|
+
<ErrorBoundary FallbackComponent={Failure}>
|
|
29
29
|
<programContext.Provider value={programContextValue}>
|
|
30
30
|
<Box>{children}</Box>
|
|
31
31
|
</programContext.Provider>
|
|
32
|
-
</
|
|
32
|
+
</ErrorBoundary>
|
|
33
33
|
);
|
|
34
34
|
};
|
package/src/ui/useProgram.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {useApp} from 'ink';
|
|
2
2
|
import {useCallback, useContext} from 'react';
|
|
3
|
-
import
|
|
3
|
+
import {useErrorBoundary} from 'react-error-boundary';
|
|
4
4
|
|
|
5
5
|
import {programContext} from './internals.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* A React hook for accessing {@
|
|
8
|
+
* A React hook for accessing {@link Program} instance and `exit` function that is used for showing an error (via an error boundary) and exiting.
|
|
9
9
|
*/
|
|
10
10
|
export function useProgram() {
|
|
11
|
-
let {showBoundary} =
|
|
11
|
+
let {showBoundary} = useErrorBoundary();
|
|
12
12
|
let {exit: appExit} = useApp();
|
|
13
13
|
let {program} = useContext(programContext);
|
|
14
14
|
|