@react-native-reusables/cli 0.6.3 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/config.json +16 -0
- package/.github/actions/setup/action.yml +21 -0
- package/.github/workflows/check.yml +54 -0
- package/.github/workflows/release.yml +0 -0
- package/.github/workflows/snapshot.yml +24 -0
- package/.prettierrc +8 -0
- package/.vscode/extensions.json +6 -0
- package/.vscode/settings.json +46 -0
- package/LICENSE +1 -1
- package/README.md +97 -0
- package/eslint.config.mjs +118 -0
- package/package.json +64 -8
- package/patches/@changesets__get-github-info@0.6.0.patch +48 -0
- package/scripts/copy-package-json.ts +32 -0
- package/src/bin.ts +18 -0
- package/src/cli.ts +66 -0
- package/src/contexts/cli-options.ts +29 -0
- package/src/project-manifest.ts +369 -0
- package/src/services/commands/add.ts +127 -0
- package/src/services/commands/doctor.ts +287 -0
- package/src/services/commands/init.ts +94 -0
- package/src/services/git.ts +39 -0
- package/src/services/package-manager.ts +48 -0
- package/src/services/project-config.ts +295 -0
- package/src/services/required-files-checker.ts +375 -0
- package/src/services/spinner.ts +15 -0
- package/src/services/template.ts +222 -0
- package/src/utils/retry-with.ts +9 -0
- package/src/utils/run-command.ts +10 -0
- package/test/Dummy.test.ts +7 -0
- package/tsconfig.base.json +53 -0
- package/tsconfig.json +14 -0
- package/tsconfig.scripts.json +17 -0
- package/tsconfig.src.json +10 -0
- package/tsconfig.test.json +10 -0
- package/tsup.config.ts +9 -0
- package/vitest.config.ts +12 -0
- package/bin.cjs +0 -59056
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Options } from "@effect/cli"
|
|
2
|
+
import { Context, Option } from "effect"
|
|
3
|
+
|
|
4
|
+
type StylingLibrary = "nativewind" | "uniwind"
|
|
5
|
+
|
|
6
|
+
class CliOptions extends Context.Tag("CommandOptions")<
|
|
7
|
+
CliOptions,
|
|
8
|
+
Readonly<{
|
|
9
|
+
cwd: string
|
|
10
|
+
yes: boolean
|
|
11
|
+
stylingLibrary: StylingLibrary | undefined
|
|
12
|
+
}>
|
|
13
|
+
>() { }
|
|
14
|
+
|
|
15
|
+
const cwd = Options.directory("cwd", { exists: "yes" }).pipe(Options.withDefault("."), Options.withAlias("c"))
|
|
16
|
+
const yes = Options.boolean("yes", { aliases: ["y"] })
|
|
17
|
+
const summary = Options.boolean("summary").pipe(Options.withAlias("s"))
|
|
18
|
+
const overwrite = Options.boolean("overwrite", { aliases: ["o"] })
|
|
19
|
+
const all = Options.boolean("all", { aliases: ["a"] })
|
|
20
|
+
const path = Options.text("path").pipe(Options.withDefault(""), Options.withAlias("p"))
|
|
21
|
+
const stylingLibrary = Options.choice("styling-library", ["nativewind", "uniwind"] as const).pipe(
|
|
22
|
+
Options.optional,
|
|
23
|
+
Options.map(Option.getOrUndefined),
|
|
24
|
+
Options.withDescription("Override the detected styling library for this command"),
|
|
25
|
+
)
|
|
26
|
+
const template = Options.text("template").pipe(Options.withAlias("t"), Options.withDefault(""))
|
|
27
|
+
|
|
28
|
+
export { CliOptions, cwd, summary, yes, overwrite, all, path, stylingLibrary, template }
|
|
29
|
+
export type { StylingLibrary }
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
interface FileCheck {
|
|
2
|
+
name: string
|
|
3
|
+
fileNames: Array<string>
|
|
4
|
+
docs: string
|
|
5
|
+
includes: Array<{
|
|
6
|
+
content: Array<string>
|
|
7
|
+
message: string
|
|
8
|
+
docs: string
|
|
9
|
+
}>
|
|
10
|
+
stylingLibraries: Array<"nativewind" | "uniwind">
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type CustomFileCheck = Omit<FileCheck, "fileNames"> & { defaultFileNames?: ReadonlyArray<string> }
|
|
14
|
+
|
|
15
|
+
interface FileWithContent extends FileCheck {
|
|
16
|
+
content: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface MissingInclude {
|
|
20
|
+
fileName: string
|
|
21
|
+
content: ReadonlyArray<string>
|
|
22
|
+
message: string
|
|
23
|
+
docs: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const CORE_DEPENDENCIES = [
|
|
27
|
+
"expo",
|
|
28
|
+
"react-native-reanimated",
|
|
29
|
+
"react-native-safe-area-context",
|
|
30
|
+
"tailwindcss-animate",
|
|
31
|
+
"class-variance-authority",
|
|
32
|
+
"clsx",
|
|
33
|
+
"tailwind-merge"
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
const DEPENDENCIES = {
|
|
37
|
+
nativewind: [...CORE_DEPENDENCIES, "nativewind"],
|
|
38
|
+
uniwind: [...CORE_DEPENDENCIES, "uniwind"]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const DEV_DEPENDENCIES = ["tailwindcss@^3.4.14"]
|
|
42
|
+
|
|
43
|
+
const FILE_CHECKS: Array<FileCheck> = [
|
|
44
|
+
{
|
|
45
|
+
name: "Babel Config",
|
|
46
|
+
fileNames: ["babel.config.js", "babel.config.ts"],
|
|
47
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#3-add-the-babel-preset",
|
|
48
|
+
includes: [
|
|
49
|
+
{
|
|
50
|
+
content: ["nativewind/babel", "jsxImportSource"],
|
|
51
|
+
message: "jsxImportSource or nativewind/babel is missing",
|
|
52
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#3-add-the-babel-preset"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
stylingLibraries: ["nativewind"] as const
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "Metro Config",
|
|
59
|
+
fileNames: ["metro.config.js", "metro.config.ts"],
|
|
60
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#4-create-or-modify-your-metroconfigjs",
|
|
61
|
+
includes: [
|
|
62
|
+
{
|
|
63
|
+
content: ["withNativeWind("],
|
|
64
|
+
message: "The withNativeWind function is missing",
|
|
65
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#4-create-or-modify-your-metroconfigjs"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
content: ["inlineRem", "16"],
|
|
69
|
+
message: "The 'inlineRem: 16' is missing",
|
|
70
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#update-the-default-inlined-rem-value"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
stylingLibraries: ["nativewind"] as const
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "Metro Config",
|
|
77
|
+
fileNames: ["metro.config.js", "metro.config.ts"],
|
|
78
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#4-create-or-modify-your-metroconfigjs",
|
|
79
|
+
includes: [
|
|
80
|
+
{
|
|
81
|
+
content: ["withUniwindConfig("],
|
|
82
|
+
message: "The withUniwindConfig function is missing",
|
|
83
|
+
docs: "https://docs.uniwind.dev/api/metro-config#metro-config-js"
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
stylingLibraries: ["uniwind"] as const
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: "Root Layout",
|
|
90
|
+
fileNames: ["app/_layout.tsx", "src/app/_layout.tsx"],
|
|
91
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#add-the-portal-host-to-your-root-layout", //
|
|
92
|
+
includes: [
|
|
93
|
+
{
|
|
94
|
+
content: [".css"],
|
|
95
|
+
message: "The css file import is missing",
|
|
96
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#5-import-your-css-file"
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
content: ["<PortalHost"],
|
|
100
|
+
message: "The PortalHost component is missing",
|
|
101
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#add-the-portal-host-to-your-root-layout"
|
|
102
|
+
}
|
|
103
|
+
],
|
|
104
|
+
stylingLibraries: ["nativewind", "uniwind"] as const
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
const DEPRECATED_FROM_LIB: Array<Omit<FileCheck, "docs" | "stylingLibraries">> = [
|
|
109
|
+
{
|
|
110
|
+
name: "Icons",
|
|
111
|
+
fileNames: ["icons/iconWithClassName.ts"],
|
|
112
|
+
includes: [
|
|
113
|
+
{
|
|
114
|
+
content: ["iconWithClassName"],
|
|
115
|
+
message: "lib/icons and its contents are deprecated. Use the new icon wrapper from components/ui/icon.",
|
|
116
|
+
docs: "https://reactnativereusables.com/docs/changelog#august-2025-deprecated"
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: "Constants",
|
|
122
|
+
fileNames: ["constants.ts"],
|
|
123
|
+
includes: [
|
|
124
|
+
{
|
|
125
|
+
content: ["NAV_THEME"],
|
|
126
|
+
message: "Usage of lib/constants for NAV_THEME is deprecated. Use lib/theme instead.",
|
|
127
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles"
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "useColorScheme",
|
|
133
|
+
fileNames: ["useColorScheme.tsx"],
|
|
134
|
+
includes: [
|
|
135
|
+
{
|
|
136
|
+
content: ["useColorScheme"],
|
|
137
|
+
message: "lib/useColorScheme is deprecated. Use Nativewind's color scheme hook instead.",
|
|
138
|
+
docs: "https://www.nativewind.dev/docs/api/use-color-scheme"
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
const DEPRECATED_FROM_UI: Array<Omit<FileCheck, "docs" | "stylingLibraries">> = [
|
|
145
|
+
{
|
|
146
|
+
name: "Typography",
|
|
147
|
+
fileNames: ["typography.tsx"],
|
|
148
|
+
includes: [
|
|
149
|
+
{
|
|
150
|
+
content: [
|
|
151
|
+
"function H1({",
|
|
152
|
+
"function H2({",
|
|
153
|
+
"function H3({",
|
|
154
|
+
"function H4({",
|
|
155
|
+
"function P({",
|
|
156
|
+
"function BlockQuote({",
|
|
157
|
+
"function Code({",
|
|
158
|
+
"function Lead({",
|
|
159
|
+
"function Large({",
|
|
160
|
+
"function Small({",
|
|
161
|
+
"function Muted({"
|
|
162
|
+
],
|
|
163
|
+
message:
|
|
164
|
+
"Typography is deprecated. Instead, use the Text component with its variant prop (e.g. <Text variant='h1'>Title</Text>)",
|
|
165
|
+
docs: "https://reactnativereusables.com/docs/components/text#typography"
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
// Excludes foreground colors since it is formatted differently in all 3 styling files (tailwind config, global.css, theme.ts)
|
|
172
|
+
const CSS_VARIABLE_NAMES = [
|
|
173
|
+
"background",
|
|
174
|
+
"foreground",
|
|
175
|
+
"card",
|
|
176
|
+
"popover",
|
|
177
|
+
"primary",
|
|
178
|
+
"secondary",
|
|
179
|
+
"muted",
|
|
180
|
+
"accent",
|
|
181
|
+
"destructive",
|
|
182
|
+
"border",
|
|
183
|
+
"input",
|
|
184
|
+
"ring",
|
|
185
|
+
"radius"
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
const CUSTOM_FILE_CHECKS: Record<string, CustomFileCheck> = {
|
|
189
|
+
tailwindConfig: {
|
|
190
|
+
name: "Tailwind Config",
|
|
191
|
+
defaultFileNames: ["tailwind.config.js", "tailwind.config.ts"],
|
|
192
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles",
|
|
193
|
+
includes: [
|
|
194
|
+
{
|
|
195
|
+
content: ["nativewind/preset"],
|
|
196
|
+
message: "The nativewind preset is missing",
|
|
197
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#2-setup-tailwind-css"
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
content: CSS_VARIABLE_NAMES,
|
|
201
|
+
message: "At least one of the color css variables is missing",
|
|
202
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles"
|
|
203
|
+
}
|
|
204
|
+
],
|
|
205
|
+
stylingLibraries: ["nativewind"] as const
|
|
206
|
+
},
|
|
207
|
+
theme: {
|
|
208
|
+
name: "Theme",
|
|
209
|
+
defaultFileNames: ["lib/theme.ts"],
|
|
210
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles",
|
|
211
|
+
includes: [
|
|
212
|
+
{
|
|
213
|
+
content: CSS_VARIABLE_NAMES,
|
|
214
|
+
message: "At least one of the color variables is missing",
|
|
215
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles"
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
content: ["NAV_THEME"],
|
|
219
|
+
message: "The NAV_THEME is missing",
|
|
220
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles"
|
|
221
|
+
}
|
|
222
|
+
],
|
|
223
|
+
stylingLibraries: ["nativewind", "uniwind"] as const
|
|
224
|
+
},
|
|
225
|
+
nativewindEnv: {
|
|
226
|
+
name: "Nativewind Env",
|
|
227
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#7-typescript-setup-optional",
|
|
228
|
+
includes: [
|
|
229
|
+
{
|
|
230
|
+
content: ["nativewind/types"],
|
|
231
|
+
message: "The nativewind types are missing",
|
|
232
|
+
docs: "https://www.nativewind.dev/docs/getting-started/installation#7-typescript-setup-optional"
|
|
233
|
+
}
|
|
234
|
+
],
|
|
235
|
+
stylingLibraries: ["nativewind"] as const
|
|
236
|
+
},
|
|
237
|
+
uniwindTypes: {
|
|
238
|
+
name: "Uniwind Types",
|
|
239
|
+
defaultFileNames: ["uniwind-types.d.ts"],
|
|
240
|
+
docs: "https://docs.uniwind.dev/api/metro-config#dtsfile",
|
|
241
|
+
includes: [
|
|
242
|
+
{
|
|
243
|
+
content: ["uniwind/types"],
|
|
244
|
+
message: "The uniwind types are missing",
|
|
245
|
+
docs: "https://docs.uniwind.dev/api/metro-config#dtsfile"
|
|
246
|
+
}
|
|
247
|
+
],
|
|
248
|
+
stylingLibraries: ["uniwind"] as const
|
|
249
|
+
},
|
|
250
|
+
utils: {
|
|
251
|
+
name: "Utils",
|
|
252
|
+
defaultFileNames: ["lib/utils.ts"],
|
|
253
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#add-a-cn-helper",
|
|
254
|
+
includes: [
|
|
255
|
+
{
|
|
256
|
+
content: ["function cn("],
|
|
257
|
+
message: "The cn function is missing",
|
|
258
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#add-a-cn-helper"
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
stylingLibraries: ["nativewind", "uniwind"] as const
|
|
262
|
+
},
|
|
263
|
+
css: {
|
|
264
|
+
name: "CSS",
|
|
265
|
+
defaultFileNames: ["globals.css", "src/global.css"],
|
|
266
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles",
|
|
267
|
+
includes: [
|
|
268
|
+
{
|
|
269
|
+
content: ["@tailwind base", "@tailwind components", "@tailwind utilities"],
|
|
270
|
+
message: "The tailwind layer directives are missing",
|
|
271
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles"
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
content: CSS_VARIABLE_NAMES,
|
|
275
|
+
message: "At least one of the color css variables is missing",
|
|
276
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles"
|
|
277
|
+
}
|
|
278
|
+
],
|
|
279
|
+
stylingLibraries: ["nativewind"] as const
|
|
280
|
+
},
|
|
281
|
+
uniwindCss: {
|
|
282
|
+
name: "CSS",
|
|
283
|
+
defaultFileNames: ["globals.css", "src/global.css"],
|
|
284
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles",
|
|
285
|
+
includes: [
|
|
286
|
+
{
|
|
287
|
+
content: ["tailwindcss", "uniwind"],
|
|
288
|
+
message: "The tailwind layer directives are missing",
|
|
289
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles"
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
content: CSS_VARIABLE_NAMES,
|
|
293
|
+
message: "At least one of the color css variables is missing",
|
|
294
|
+
docs: "https://reactnativereusables.com/docs/installation/manual#configure-your-styles"
|
|
295
|
+
}
|
|
296
|
+
],
|
|
297
|
+
stylingLibraries: ["uniwind"] as const
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const NATIVEWIND_ENV_FILE = "nativewind-env.d.ts"
|
|
302
|
+
const UNIWIND_TYPES_FILE = "uniwind-types.d.ts"
|
|
303
|
+
|
|
304
|
+
const COMPONENTS = [
|
|
305
|
+
"accordion",
|
|
306
|
+
"alert-dialog",
|
|
307
|
+
"alert",
|
|
308
|
+
"aspect-ratio",
|
|
309
|
+
"avatar",
|
|
310
|
+
"badge",
|
|
311
|
+
"button",
|
|
312
|
+
"card",
|
|
313
|
+
"checkbox",
|
|
314
|
+
"collapsible",
|
|
315
|
+
"context-menu",
|
|
316
|
+
"dialog",
|
|
317
|
+
"dropdown-menu",
|
|
318
|
+
"hover-card",
|
|
319
|
+
"input",
|
|
320
|
+
"label",
|
|
321
|
+
"menubar",
|
|
322
|
+
"popover",
|
|
323
|
+
"progress",
|
|
324
|
+
"radio-group",
|
|
325
|
+
"select",
|
|
326
|
+
"separator",
|
|
327
|
+
"skeleton",
|
|
328
|
+
"switch",
|
|
329
|
+
"tabs",
|
|
330
|
+
"text",
|
|
331
|
+
"textarea",
|
|
332
|
+
"toggle-group",
|
|
333
|
+
"toggle",
|
|
334
|
+
"tooltip"
|
|
335
|
+
]
|
|
336
|
+
|
|
337
|
+
const TEMPLATES = [
|
|
338
|
+
{
|
|
339
|
+
name: "Minimal (Nativewind)",
|
|
340
|
+
url: "https://github.com/founded-labs/react-native-reusables-templates.git",
|
|
341
|
+
subPath: "minimal"
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
name: "Minimal (Uniwind)",
|
|
345
|
+
url: "https://github.com/founded-labs/react-native-reusables-templates.git",
|
|
346
|
+
subPath: "minimal-uniwind"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: "Clerk auth (Nativewind)",
|
|
350
|
+
url: "https://github.com/founded-labs/react-native-reusables-templates.git",
|
|
351
|
+
subPath: "clerk-auth"
|
|
352
|
+
}
|
|
353
|
+
]
|
|
354
|
+
|
|
355
|
+
const PROJECT_MANIFEST = {
|
|
356
|
+
dependencies: DEPENDENCIES,
|
|
357
|
+
devDependencies: DEV_DEPENDENCIES,
|
|
358
|
+
fileChecks: FILE_CHECKS,
|
|
359
|
+
deprecatedFromLib: DEPRECATED_FROM_LIB,
|
|
360
|
+
deprecatedFromUi: DEPRECATED_FROM_UI,
|
|
361
|
+
customFileChecks: CUSTOM_FILE_CHECKS,
|
|
362
|
+
nativewindEnvFile: NATIVEWIND_ENV_FILE,
|
|
363
|
+
uniwindTypesFile: UNIWIND_TYPES_FILE,
|
|
364
|
+
components: COMPONENTS,
|
|
365
|
+
templates: TEMPLATES
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export { PROJECT_MANIFEST }
|
|
369
|
+
export type { FileCheck, CustomFileCheck, FileWithContent, MissingInclude }
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { CliOptions, type StylingLibrary } from "@cli/contexts/cli-options.js"
|
|
2
|
+
import { PROJECT_MANIFEST } from "@cli/project-manifest.js"
|
|
3
|
+
import { Doctor } from "@cli/services/commands/doctor.js"
|
|
4
|
+
import { runCommand } from "@cli/utils/run-command.js"
|
|
5
|
+
import { Prompt } from "@effect/cli"
|
|
6
|
+
import { Effect, Layer } from "effect"
|
|
7
|
+
import { PackageManager } from "../package-manager.js"
|
|
8
|
+
import { ProjectConfig } from "../project-config.js"
|
|
9
|
+
|
|
10
|
+
type AddOptions = {
|
|
11
|
+
cwd: string
|
|
12
|
+
args: { components: Array<string> }
|
|
13
|
+
yes: boolean
|
|
14
|
+
overwrite: boolean
|
|
15
|
+
all: boolean
|
|
16
|
+
path: string
|
|
17
|
+
stylingLibrary: StylingLibrary | undefined
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class Add extends Effect.Service<Add>()("Add", {
|
|
21
|
+
dependencies: [PackageManager.Default],
|
|
22
|
+
effect: Effect.gen(function* () {
|
|
23
|
+
const doctor = yield* Doctor
|
|
24
|
+
const projectConfig = yield* ProjectConfig
|
|
25
|
+
const packageManager = yield* PackageManager
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
run: (options: AddOptions) =>
|
|
29
|
+
Effect.gen(function* () {
|
|
30
|
+
yield* Effect.logDebug(`Add options: ${JSON.stringify(options, null, 2)}`)
|
|
31
|
+
|
|
32
|
+
yield* projectConfig.getComponentJson() // ensure components.json config is valid and prompt if not
|
|
33
|
+
|
|
34
|
+
const components = options.all ? PROJECT_MANIFEST.components : (options.args?.components ?? [])
|
|
35
|
+
|
|
36
|
+
if (components.length === 0) {
|
|
37
|
+
const selectedComponents = yield* Prompt.multiSelect({
|
|
38
|
+
message: "Select components to add",
|
|
39
|
+
choices: PROJECT_MANIFEST.components.map((component) => ({
|
|
40
|
+
title: component,
|
|
41
|
+
value: component
|
|
42
|
+
}))
|
|
43
|
+
})
|
|
44
|
+
for (const component of selectedComponents) {
|
|
45
|
+
components.push(component)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (components.length === 0) {
|
|
50
|
+
yield* Effect.fail(new Error("No components selected."))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
yield* Effect.logDebug(`Selected components: ${components.join(", ")}`)
|
|
54
|
+
|
|
55
|
+
const stylingLibrary = yield* projectConfig.getStylingLibrary()
|
|
56
|
+
|
|
57
|
+
const registry = stylingLibrary === "uniwind" ? "uniwind" : "nativewind"
|
|
58
|
+
|
|
59
|
+
const baseUrl =
|
|
60
|
+
process.env.INTERNAL_ENV === "development"
|
|
61
|
+
? `http://localhost:3000/local/r/${registry}`
|
|
62
|
+
: `https://reactnativereusables.com/r/${registry}`
|
|
63
|
+
|
|
64
|
+
const componentUrls = components.map((component) => {
|
|
65
|
+
const lowerCaseComponent = component.toLocaleLowerCase()
|
|
66
|
+
return lowerCaseComponent.startsWith("http") ? lowerCaseComponent : `${baseUrl}/${lowerCaseComponent}.json`
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const shadcnOptions = toShadcnOptions(options)
|
|
70
|
+
|
|
71
|
+
const binaryRunner = yield* packageManager.getBinaryRunner(options.cwd)
|
|
72
|
+
|
|
73
|
+
const commandArgs = [
|
|
74
|
+
...binaryRunner.slice(1),
|
|
75
|
+
"shadcn@latest",
|
|
76
|
+
"add",
|
|
77
|
+
...shadcnOptions,
|
|
78
|
+
...componentUrls
|
|
79
|
+
].filter((option) => option !== undefined)
|
|
80
|
+
|
|
81
|
+
yield* Effect.logDebug(`Running command: ${binaryRunner[0]} ${commandArgs.join(" ")}`)
|
|
82
|
+
|
|
83
|
+
yield* runCommand(binaryRunner[0], commandArgs, {
|
|
84
|
+
cwd: options.cwd,
|
|
85
|
+
stdio: "inherit"
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
yield* doctor.run({ ...options, summary: true })
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}) {}
|
|
93
|
+
|
|
94
|
+
function make(options: AddOptions) {
|
|
95
|
+
const optionsLayer = Layer.succeed(CliOptions, { ...options, yes: true }) // For the project config
|
|
96
|
+
return Effect.gen(function* () {
|
|
97
|
+
const add = yield* Add
|
|
98
|
+
|
|
99
|
+
return yield* add.run(options)
|
|
100
|
+
}).pipe(
|
|
101
|
+
Effect.provide(Add.Default),
|
|
102
|
+
Effect.provide(Doctor.Default),
|
|
103
|
+
Effect.provide(ProjectConfig.Default),
|
|
104
|
+
Effect.provide(optionsLayer)
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export { make }
|
|
109
|
+
|
|
110
|
+
function toShadcnOptions(options: AddOptions) {
|
|
111
|
+
const shadcnOptions = []
|
|
112
|
+
|
|
113
|
+
if (options.overwrite) {
|
|
114
|
+
shadcnOptions.push("--overwrite")
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (options.yes) {
|
|
118
|
+
shadcnOptions.push("--yes")
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (options.path) {
|
|
122
|
+
shadcnOptions.push("--path")
|
|
123
|
+
shadcnOptions.push(options.path)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return shadcnOptions
|
|
127
|
+
}
|