@jongh/cli 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/index.cjs +7 -0
  2. package/dist/index.js +6 -1
  3. package/package.json +26 -5
  4. package/.lintstagedrc.json +0 -4
  5. package/CHANGELOG.md +0 -7
  6. package/dist/components/Accordion/Accordion.tsx +0 -281
  7. package/dist/components/Accordion/index.tsx +0 -37
  8. package/dist/components/Accordion/useAccordionHeight.ts +0 -25
  9. package/dist/components/Avatar/Avatar.tsx +0 -18
  10. package/dist/components/Avatar/useAvatar.ts +0 -73
  11. package/dist/components/Button/index.tsx +0 -32
  12. package/dist/components/Primitive/index.tsx +0 -58
  13. package/dist/components/RovingIndex/RovingItem.tsx +0 -47
  14. package/dist/components/RovingIndex/RovingTabIndexRoot.tsx +0 -92
  15. package/dist/components/RovingIndex/index.ts +0 -3
  16. package/dist/components/RovingIndex/useRovingTabIndex.tsx +0 -76
  17. package/dist/components/Select/index.tsx +0 -231
  18. package/dist/components/Slider/index.tsx +0 -141
  19. package/dist/components/Slider/style.ts +0 -29
  20. package/dist/components/Tabs/Tab.tsx +0 -39
  21. package/dist/components/Tabs/TabContent.tsx +0 -62
  22. package/dist/components/Tabs/TabIndicator.tsx +0 -6
  23. package/dist/components/Tabs/TabList.tsx +0 -117
  24. package/dist/components/Tabs/index.ts +0 -43
  25. package/dist/components/Tabs/useRovingTabIndex.tsx +0 -171
  26. package/dist/components/Tabs/useTabContext.ts +0 -11
  27. package/dist/components/TagButton/TagButton.tsx +0 -75
  28. package/dist/components/TagButton/style.ts +0 -35
  29. package/dist/components/core/Polymorphic/index.ts +0 -26
  30. package/dist/components/index.ts +0 -5
  31. package/dist/index.d.ts +0 -1
  32. package/src/copyComponent.ts +0 -29
  33. package/src/getConfig.ts +0 -56
  34. package/src/index.ts +0 -46
  35. package/tsconfig.json +0 -14
  36. package/tsup.config.ts +0 -14
@@ -1,39 +0,0 @@
1
- import { forwardRef, useId, type ReactNode, type ForwardedRef } from "react"
2
- import { useControlledState } from "../../hooks/useControllableState"
3
- import { Slot } from "@radix-ui/react-slot"
4
- import { TabProvider } from "./useTabContext"
5
-
6
- export type TabProps = {
7
- children?: ReactNode
8
- selected?: string
9
- defaultValue?: string
10
- onSelect?: (value: string) => void
11
- asChild?: boolean
12
- }
13
-
14
- export const Tab = forwardRef(
15
- (
16
- { children, selected, defaultValue, onSelect, asChild, ...props }: TabProps,
17
- ref: ForwardedRef<HTMLDivElement>,
18
- ) => {
19
- const Element = asChild ? Slot : "div"
20
-
21
- const [value, setValue] = useControlledState({
22
- prop: selected,
23
- onChange: onSelect,
24
- defaultProp: defaultValue,
25
- })
26
-
27
- const onSelectItem = (value: string) => {
28
- setValue(value)
29
- }
30
-
31
- return (
32
- <TabProvider selected={value} onSelect={onSelectItem} tabId={useId()}>
33
- <Element ref={ref} {...props}>
34
- {children}
35
- </Element>
36
- </TabProvider>
37
- )
38
- },
39
- )
@@ -1,62 +0,0 @@
1
- import { type ReactNode } from "react"
2
- import { useTabContext } from "./useTabContext"
3
- import { motion } from "framer-motion"
4
-
5
- export interface TabContentProps {
6
- children: ReactNode
7
- value: string
8
- }
9
-
10
- const tabContentVariant = {
11
- active: {
12
- display: "block",
13
- transition: {
14
- staggerChildren: 0.2,
15
- },
16
- },
17
- inactive: {
18
- display: "none",
19
- },
20
- } as const
21
-
22
- const cardVariant = {
23
- active: {
24
- opacity: 1,
25
- x: 0,
26
- transition: {
27
- duration: 0.3,
28
- },
29
- },
30
- inactive: {
31
- opacity: 0,
32
- x: 10,
33
- transition: {
34
- duration: 0.3,
35
- },
36
- },
37
- }
38
-
39
- export const TabContent = ({ children, value, ...props }: TabContentProps) => {
40
- const { selected, tabId } = useTabContext("tab")
41
-
42
- const isSelected = selected === value
43
-
44
- return (
45
- <motion.div
46
- role="tabpanel"
47
- tabIndex={0}
48
- id={tabId + "-tabpanel-" + value}
49
- data-state={isSelected ? "active" : "inactive"}
50
- aria-labelledby={tabId + "-tabitem-" + value}
51
- key={tabId + "-tabpanel-" + value}
52
- variants={tabContentVariant}
53
- animate={isSelected ? "active" : "inactive"}
54
- initial="inactive"
55
- {...props}
56
- >
57
- <motion.div key={value} variants={cardVariant}>
58
- {isSelected && children}
59
- </motion.div>
60
- </motion.div>
61
- )
62
- }
@@ -1,6 +0,0 @@
1
- import { motion } from "framer-motion"
2
-
3
- export interface TabIndicatorProps {}
4
- export const TabIndicator = ({ ...props }: TabIndicatorProps) => {
5
- return <motion.div {...props}></motion.div>
6
- }
@@ -1,117 +0,0 @@
1
- import {
2
- type ReactNode,
3
- type ReactElement,
4
- type ComponentPropsWithRef,
5
- forwardRef,
6
- } from "react"
7
- import { useTabContext } from "./useTabContext"
8
-
9
- import { RovingTabIndexRoot, useRovingTabIndex } from "../RovingIndex"
10
-
11
- import { Slot } from "@radix-ui/react-slot"
12
- import { composeRefs } from "../../hooks/useComposedRefs"
13
- import {
14
- getNextFocusableId,
15
- getPrevFocusableId,
16
- } from "../../utils/getFocusableId"
17
-
18
- export interface TabListProps {
19
- children: ReactNode
20
- className?: string
21
- }
22
-
23
- const setIndicatorStyle = (target: HTMLElement) => {
24
- const targetRect = target.getBoundingClientRect()
25
- const parent = target.parentElement //TabList
26
- const parentRect = parent?.getBoundingClientRect()
27
-
28
- if (!targetRect || !parentRect || !parent) {
29
- return
30
- }
31
- const scrollLeft = parent.scrollLeft //overflow일때 고려
32
-
33
- const indicatorLeft = targetRect.left - parentRect.left + scrollLeft
34
-
35
- document.documentElement.style.setProperty(
36
- "--indicator-left",
37
- `${indicatorLeft}px`,
38
- )
39
- document.documentElement.style.setProperty(
40
- "--indicator-width",
41
- `${targetRect.width}px`,
42
- )
43
- }
44
-
45
- export const TabList = ({ children, ...props }: TabListProps) => {
46
- const { selected } = useTabContext("tab")
47
- return (
48
- <RovingTabIndexRoot active={selected}>
49
- <div role="tablist" {...props}>
50
- {children}
51
- </div>
52
- </RovingTabIndexRoot>
53
- )
54
- }
55
- export interface TabItemProps extends ComponentPropsWithRef<"button"> {
56
- value: string
57
- }
58
-
59
- export const RovingItem = ({
60
- value,
61
- children,
62
- }: {
63
- value: string
64
- children: ReactElement
65
- }) => {
66
- const { getOrderedItems, getRovingProps } = useRovingTabIndex(value)
67
- return (
68
- <Slot
69
- {...getRovingProps<"button">({
70
- onKeyDown: (e) => {
71
- const items = getOrderedItems()
72
- let nextItem
73
- if (e.key === "ArrowRight") {
74
- nextItem = getNextFocusableId(items, value)
75
- } else if (e.key === "ArrowLeft") {
76
- nextItem = getPrevFocusableId(items, value)
77
- }
78
- nextItem?.element.focus()
79
- },
80
- })}
81
- >
82
- {children}
83
- </Slot>
84
- )
85
- }
86
-
87
- export const TabItem = forwardRef<HTMLButtonElement, TabItemProps>(
88
- ({ children, value, ...props }: TabItemProps, forwardRef) => {
89
- const { selected, onSelect, tabId } = useTabContext("tab")
90
- const isSelected = selected === value
91
-
92
- const handleSelect = () => {
93
- onSelect?.(value)
94
- }
95
-
96
- return (
97
- <RovingItem value={value}>
98
- <button
99
- ref={composeRefs((node) => {
100
- if (isSelected && node) {
101
- setIndicatorStyle(node as HTMLElement)
102
- }
103
- }, forwardRef)}
104
- onClick={handleSelect}
105
- onFocus={handleSelect}
106
- role="tab"
107
- aria-selected={isSelected}
108
- id={tabId + "-tabitem-" + value}
109
- aria-controls={tabId + "-tabpanel-" + value}
110
- {...props}
111
- >
112
- {children}
113
- </button>
114
- </RovingItem>
115
- )
116
- },
117
- )
@@ -1,43 +0,0 @@
1
- "use client"
2
- import { Tab, TabProps } from "./Tab"
3
- import { TabContent, TabContentProps } from "./TabContent"
4
- import { TabIndicator, TabIndicatorProps } from "./TabIndicator"
5
- import { TabList, TabListProps } from "./TabList"
6
- import { TabItem, TabItemProps } from "./TabList"
7
- import { type TabsVariantProps, tabs } from "styled-system/recipes"
8
- import type { Assign, HTMLStyledProps } from "styled-system/types"
9
- import { createStyleContext } from "../../utils/createStyleContext"
10
-
11
- const { withProvider, withContext } = createStyleContext(tabs)
12
-
13
- const Root = withProvider<
14
- HTMLDivElement,
15
- Assign<Assign<HTMLStyledProps<"div">, TabProps>, TabsVariantProps>
16
- >(Tab, "root")
17
-
18
- const Content = withContext<
19
- HTMLDivElement,
20
- Assign<HTMLStyledProps<"div">, TabContentProps>
21
- >(TabContent, "content")
22
-
23
- const Indicator = withContext<
24
- HTMLDivElement,
25
- Assign<HTMLStyledProps<"div">, TabIndicatorProps>
26
- >(TabIndicator, "indicator")
27
-
28
- const List = withContext<
29
- HTMLDivElement,
30
- Assign<HTMLStyledProps<"div">, TabListProps>
31
- >(TabList, "list")
32
-
33
- const Item = withContext<
34
- HTMLButtonElement,
35
- Assign<HTMLStyledProps<"button">, TabItemProps>
36
- >(TabItem, "item")
37
-
38
- export const Tabs = Object.assign(Root, {
39
- Content,
40
- List,
41
- Item,
42
- Indicator,
43
- })
@@ -1,171 +0,0 @@
1
- import isHotkey from "is-hotkey"
2
- import {
3
- ReactNode,
4
- useCallback,
5
- useRef,
6
- useState,
7
- FocusEvent,
8
- MouseEvent,
9
- KeyboardEvent,
10
- ComponentPropsWithoutRef,
11
- ElementType,
12
- MutableRefObject,
13
- } from "react"
14
- import { useControlledState } from "../../hooks/useControllableState"
15
- import { createContext } from "../../hooks/createContext"
16
- import { composeRefs } from "../../hooks/useComposedRefs"
17
-
18
- export type RovingTabIndexItem = {
19
- value: string
20
- element: HTMLElement
21
- }
22
-
23
- function onFocusFirst(candidates: HTMLElement[]) {
24
- const previousFocus = document.activeElement
25
- while (document.activeElement === previousFocus && candidates.length > 0) {
26
- candidates.shift()?.focus()
27
- }
28
- }
29
-
30
- type RovingTabIndexContext = {
31
- currentFocusedValue: string | null
32
- setFocusableId: (id: string) => void
33
- onShiftTab: () => void
34
- getOrderedItems: () => RovingTabIndexItem[]
35
- elements: MutableRefObject<Map<string, HTMLElement>>
36
- }
37
-
38
- const [RovingTabIndexProvider, useRovingTabIndexContext] =
39
- createContext<RovingTabIndexContext>("rovingTabIndex")
40
-
41
- const NODE_SELECTOR = "data-roving-tabindex-node"
42
- const ROOT_SELECTOR = "data-roving-tabindex-root"
43
- const NOT_FOCUSABLE_SELECTOR = "data-roving-tabindex-not-focusable"
44
- const SELECTOR_ACTIVE = `:where([${NODE_SELECTOR}=true]):not(:where([${NOT_FOCUSABLE_SELECTOR}=true] *))`
45
-
46
- type RovingTabIndexRootBaseProps<T> = {
47
- children: ReactNode | ReactNode[]
48
- active?: string
49
- setActive?: (value: string) => void
50
- as?: T
51
- }
52
-
53
- type RovingTabIndexRootProps<T extends ElementType> =
54
- RovingTabIndexRootBaseProps<T> &
55
- Omit<ComponentPropsWithoutRef<T>, keyof RovingTabIndexRootBaseProps<T>>
56
-
57
- export const RovingTabIndexRoot = <T extends ElementType>({
58
- children,
59
- active,
60
- setActive,
61
- as,
62
- ref,
63
- ...props
64
- }: RovingTabIndexRootProps<T>) => {
65
- const Component = as || "div"
66
- const [isShiftTabbing, setIsShiftTabbing] = useState(false)
67
-
68
- const [value = null, setValue] = useControlledState({
69
- prop: active,
70
- onChange: setActive,
71
- })
72
-
73
- const rootRef = useRef<HTMLDivElement | null>(null)
74
- const elementsRef = useRef<Map<string, HTMLElement>>(new Map())
75
- const getOrderedItems = useCallback(() => {
76
- if (!rootRef.current) return []
77
- const activeElements = Array.from(
78
- rootRef.current.querySelectorAll(SELECTOR_ACTIVE),
79
- )
80
-
81
- return Array.from(elementsRef.current)
82
- .sort(
83
- (a, b) => activeElements.indexOf(a[1]) - activeElements.indexOf(b[1]),
84
- )
85
- .map(([value, element]) => ({ value, element }))
86
- }, [])
87
-
88
- return (
89
- <RovingTabIndexProvider
90
- setFocusableId={(value: string) => {
91
- setValue(value)
92
- }}
93
- onShiftTab={() => {
94
- setIsShiftTabbing(true)
95
- }}
96
- currentFocusedValue={value}
97
- getOrderedItems={getOrderedItems}
98
- elements={elementsRef}
99
- >
100
- <Component
101
- {...{ [ROOT_SELECTOR]: true }}
102
- // tabIndex={isShiftTabbing ? -1 : 0}
103
- onFocus={(e) => {
104
- if (e.target !== e.currentTarget) return
105
- if (isShiftTabbing) return
106
- const orderedItems = getOrderedItems()
107
- if (orderedItems.length === 0) return
108
-
109
- const candidates = [
110
- elementsRef.current.get(value ?? ""),
111
- ...orderedItems.map((i) => i.element),
112
- ].filter((element): element is HTMLElement => element != null)
113
-
114
- onFocusFirst(candidates)
115
- }}
116
- onBlur={() => setIsShiftTabbing(false)}
117
- ref={composeRefs(ref, rootRef)}
118
- {...props}
119
- >
120
- {children}
121
- </Component>
122
- </RovingTabIndexProvider>
123
- )
124
- }
125
-
126
- export const useRovingTabIndex = (value: string) => {
127
- const {
128
- currentFocusedValue,
129
- setFocusableId,
130
- onShiftTab,
131
- getOrderedItems,
132
- elements,
133
- } = useRovingTabIndexContext("rovingTabIndex")
134
-
135
- return {
136
- getOrderedItems,
137
- isFocusable: currentFocusedValue === value,
138
- getRovingProps: <T extends ElementType>(
139
- props?: ComponentPropsWithoutRef<T>,
140
- ) => ({
141
- ...props,
142
- ref: (element: HTMLElement | null) => {
143
- if (element) {
144
- elements.current.set(value, element)
145
- } else {
146
- elements.current.delete(value)
147
- }
148
- },
149
- onMouseDown: (e: MouseEvent) => {
150
- props?.onMouseDown?.(e)
151
- if (e.target !== e.currentTarget) return
152
- setFocusableId(value)
153
- },
154
- onKeyDown: (e: KeyboardEvent) => {
155
- props?.onKeyDown?.(e)
156
- if (e.target !== e.currentTarget) return
157
- if (isHotkey("shift+tab", e)) {
158
- onShiftTab()
159
- return
160
- }
161
- },
162
- onFocus: (e: FocusEvent) => {
163
- props?.onFocus?.(e)
164
- if (e.target !== e.currentTarget) return
165
- setFocusableId(value)
166
- },
167
- [NODE_SELECTOR]: true,
168
- tabIndex: currentFocusedValue === value ? 0 : -1,
169
- }),
170
- }
171
- }
@@ -1,11 +0,0 @@
1
- import { createContext } from "../../hooks/createContext"
2
-
3
- type Value = string
4
-
5
- export interface TabContext {
6
- selected?: Value
7
- onSelect?: (index: Value) => void
8
- tabId: string
9
- }
10
-
11
- export const [TabProvider, useTabContext] = createContext<TabContext>("tab")
@@ -1,75 +0,0 @@
1
- // import { cx } from "@styled-system/css"
2
- // // import { tagButton, type TagButtonVariant } from "@styled-system/recipes"
3
- // import { forwardRef } from "react"
4
- // import { useControlledState } from "../../hooks/useControllableState"
5
-
6
- // type TagButtonProps = Partial<TagButtonVariant> & {
7
- // /** 버튼 내부에 표시될 텍스트 */
8
- // children: string
9
-
10
- // /** 버튼의 비활성화 여부를 지정합니다. true일 경우 버튼이 비활성화됩니다. */
11
- // disabled?: boolean
12
-
13
- // /** 버튼에 적용할 추가적인 CSS 클래스명 */
14
- // className?: string
15
-
16
- // /** 버튼의 고유 식별자 */
17
- // id?: string
18
-
19
- // /**
20
- // * 버튼 클릭 시 실행될 함수
21
- // * @param e 선택적 boolean 매개변수
22
- // */
23
- // onClick?: (e?: boolean) => void
24
-
25
- // /** 버튼의 클릭 상태를 나타냅니다. true일 경우 클릭된 상태를 의미합니다. */
26
- // isClicked?: boolean
27
-
28
- // /** 버튼의 초기 클릭 상태를 설정합니다. true일 경우 처음부터 클릭된 상태로 시작합니다. */
29
- // defaultClick?: boolean
30
- // }
31
-
32
- // export const TagButton = forwardRef<HTMLSpanElement, TagButtonProps>(
33
- // (
34
- // {
35
- // children,
36
- // disabled = false,
37
- // className,
38
- // id,
39
- // onClick,
40
- // isClicked,
41
- // defaultClick = false,
42
- // ...rest
43
- // },
44
- // ref,
45
- // ) => {
46
- // const [clicked, setClicked] = useControlledState({
47
- // prop: isClicked,
48
- // defaultProp: defaultClick,
49
- // onChange: onClick,
50
- // })
51
- // //
52
- // return (
53
- // <span
54
- // role="button"
55
- // ref={ref}
56
- // id={id}
57
- // className={cx(tagButton({ ...rest }), className)}
58
- // aria-disabled={disabled}
59
- // aria-pressed={clicked}
60
- // {...(disabled && { "data-invalid": "true" })}
61
- // data-testid={id}
62
- // onClick={() => {
63
- // if (disabled) {
64
- // return
65
- // }
66
- // setClicked((prev) => !prev)
67
- // }}
68
- // >
69
- // {children}
70
- // </span>
71
- // )
72
- // },
73
- // )
74
-
75
- // TagButton.displayName = "TagButton"
@@ -1,35 +0,0 @@
1
- import { define } from "../../../dev"
2
- export const tagButtonRecipe = define.recipe({
3
- className: "tagButton",
4
- base: {
5
- textAlign: "center",
6
- borderRadius: "rounded",
7
- padding: "4px 10px",
8
- fontWeight: "normal",
9
- cursor: "pointer",
10
- border: "1px solid black",
11
- color: "text_primary",
12
- _hover: { boxShadow: "0px 0px 3px", boxShadowColor: "grey_300" },
13
- _invalid: {
14
- cursor: "not-allowed",
15
- },
16
- _pressed: {
17
- borderWidth: "2px",
18
- borderColor: "blue_300",
19
- },
20
- maxWidth: "screen",
21
- },
22
- variants: {
23
- size: {
24
- small: {
25
- fontSize: "sm",
26
- },
27
- large: {
28
- fontSize: "lg",
29
- },
30
- },
31
- },
32
- defaultVariants: {
33
- size: "small",
34
- },
35
- })
@@ -1,26 +0,0 @@
1
- // polymorphic.d.ts
2
- import {
3
- type ComponentPropsWithRef,
4
- type ComponentPropsWithoutRef,
5
- type ElementType,
6
- } from "react"
7
-
8
- type AsProp<C extends ElementType> = {
9
- as?: C
10
- }
11
-
12
- export type AsRef<C extends ElementType> = ComponentPropsWithRef<C>["ref"]
13
-
14
- export type AsComponentProps<
15
- C extends ElementType,
16
- Props = object,
17
- > = AsProp<C> &
18
- ComponentPropsWithoutRef<C> &
19
- Props & {
20
- ref?: AsRef<C>
21
- }
22
-
23
- export type AsComponentPropsWithRef<
24
- C extends ElementType,
25
- Props = object,
26
- > = Props & { ref?: AsRef<C> }
@@ -1,5 +0,0 @@
1
- export * from "./Button"
2
- export * from "./Accordion"
3
- export * from "./Tabs"
4
- export * from "./Slider"
5
- export * from "./Select"
package/dist/index.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
@@ -1,29 +0,0 @@
1
- // src/copy-components.ts
2
- import path from "node:path"
3
- import fs from "fs-extra"
4
- import { fileURLToPath } from "node:url"
5
-
6
- const __dirname = path.dirname(fileURLToPath(import.meta.url))
7
-
8
- const COMPONENTS_DIR = path.join(__dirname, "./components/Button/index.tsx")
9
-
10
- export async function copyComponent(componentName: string, outputPath: string) {
11
- try {
12
- // 1. 우리 패키지 안의 컴포넌트 경로
13
- const sourceFile = COMPONENTS_DIR
14
-
15
- // 2. 유저 프로젝트의 목적지 경로 (CLI 실행 위치 기준)
16
- const targetFile = path.join(
17
- process.cwd(),
18
- outputPath,
19
- `${componentName}.tsx`,
20
- )
21
-
22
- // 3. 복사 실행
23
- await fs.copy(sourceFile, targetFile)
24
-
25
- console.log(`${componentName} copied to ${outputPath}`)
26
- } catch (error) {
27
- throw new Error(`Failed to copy component: ${error}`)
28
- }
29
- }
package/src/getConfig.ts DELETED
@@ -1,56 +0,0 @@
1
- import path from "node:path"
2
- import fs from "fs-extra"
3
- import { packageDirectory } from "pkg-dir"
4
- import * as p from "@clack/prompts"
5
- //TODO: json schema 제공
6
-
7
- const initialPath = "./src/components/ui"
8
- const jsonFileName = "component.json"
9
-
10
- export async function getConfig(): Promise<{ outputPath: string }> {
11
- const packageDir = await packageDirectory()
12
- const configPath = path.join(packageDir || process.cwd(), jsonFileName)
13
- try {
14
- // 1. 프로젝트 루트 디렉토리 찾기
15
-
16
- // 2. 설정 파일 읽기 시도
17
- const config = await fs.readJSON(configPath)
18
- return config
19
- } catch {
20
- p.note("설정 파일이 존재하지 않습니다")
21
- const config = await promptConfig() //3. 사용자에게 경로 관련 입력 받기
22
-
23
- // 4. 새 설정 파일 저장
24
- await fs.outputJSON(
25
- configPath,
26
- {
27
- ...config,
28
- },
29
- { spaces: 2 },
30
- )
31
-
32
- return config
33
- }
34
- }
35
-
36
- const promptConfig = async () =>
37
- p.group(
38
- {
39
- outputPath: () =>
40
- p.text({
41
- message: "컴포넌트 저장 경로를 설정해주세요",
42
- initialValue: initialPath,
43
- validate: (value) => {
44
- if (!value) return "Please enter a path."
45
- if (!value.startsWith("."))
46
- return "Please enter a relative path to the project root."
47
- },
48
- }),
49
- },
50
- {
51
- onCancel: () => {
52
- p.cancel("Operation cancelled.")
53
- process.exit(0)
54
- },
55
- },
56
- )