@react-native-reusables/cli 0.7.0 → 0.7.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.
@@ -1,287 +0,0 @@
1
- import { CliOptions } from "@cli/contexts/cli-options.js"
2
- import { type CustomFileCheck, type FileCheck, type MissingInclude, PROJECT_MANIFEST } from "@cli/project-manifest.js"
3
- import { ProjectConfig } from "@cli/services/project-config.js"
4
- import { RequiredFilesChecker } from "@cli/services/required-files-checker.js"
5
- import { Spinner } from "@cli/services/spinner.js"
6
- import { runCommand } from "@cli/utils/run-command.js"
7
- import { Prompt } from "@effect/cli"
8
- import { FileSystem, Path } from "@effect/platform"
9
- import { Data, Effect, Layer, Schema } from "effect"
10
- import logSymbols from "log-symbols"
11
-
12
- const packageJsonSchema = Schema.Struct({
13
- dependencies: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String })),
14
- devDependencies: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.String }))
15
- })
16
-
17
- class PackageJsonError extends Data.TaggedError("PackageJsonError")<{
18
- cause?: unknown
19
- message?: string
20
- }> {}
21
-
22
- type DoctorOptions = {
23
- cwd: string
24
- summary: boolean
25
- yes: boolean
26
- }
27
-
28
- class Doctor extends Effect.Service<Doctor>()("Doctor", {
29
- dependencies: [RequiredFilesChecker.Default, Spinner.Default],
30
- effect: Effect.gen(function* () {
31
- const options = yield* CliOptions
32
- const fs = yield* FileSystem.FileSystem
33
- const path = yield* Path.Path
34
- const requiredFileChecker = yield* RequiredFilesChecker
35
- const spinner = yield* Spinner
36
- const projectConfig = yield* ProjectConfig
37
-
38
- const stylingLibrary = yield* projectConfig.getStylingLibrary()
39
-
40
- if (stylingLibrary !== "unknown"){
41
- console.log(
42
- `\x1b[2m${logSymbols.info} Styling Library: ${stylingLibrary === "uniwind" ? "Uniwind" : "Nativewind"}\x1b[0m`
43
- )
44
- }
45
-
46
-
47
- const checkRequiredDependencies = ({
48
- dependencies,
49
- devDependencies
50
- }: {
51
- dependencies: Array<string>
52
- devDependencies: Array<string>
53
- }) =>
54
- Effect.gen(function* () {
55
- const packageJsonExists = yield* fs.exists(path.join(options.cwd, "package.json"))
56
- if (!packageJsonExists) {
57
- return yield* Effect.fail(new PackageJsonError({ message: "A package.json was not found and is required." }))
58
- }
59
-
60
- const packageJson = yield* fs.readFileString(path.join(options.cwd, "package.json")).pipe(
61
- Effect.flatMap(Schema.decodeUnknown(Schema.parseJson())),
62
- Effect.flatMap(Schema.decodeUnknown(packageJsonSchema)),
63
- Effect.catchTags({
64
- ParseError: () => Effect.fail(new PackageJsonError({ message: "Failed to parse package.json" }))
65
- })
66
- )
67
-
68
- const uninstalledDependencies: Array<string> = []
69
- const uninstalledDevDependencies: Array<string> = []
70
-
71
- for (const dependency of dependencies) {
72
- if (
73
- !packageJson.dependencies?.[dependency.split("@")[0]] &&
74
- !packageJson.devDependencies?.[dependency.split("@")[0]]
75
- ) {
76
- uninstalledDependencies.push(dependency)
77
- continue
78
- }
79
- yield* Effect.logDebug(
80
- `${logSymbols.success} ${dependency}@${packageJson.dependencies?.[dependency.split("@")[0]]} is installed`
81
- )
82
- }
83
-
84
- for (const devDependency of devDependencies) {
85
- if (
86
- !packageJson.devDependencies?.[devDependency.split("@")[0]] &&
87
- !packageJson.dependencies?.[devDependency.split("@")[0]]
88
- ) {
89
- uninstalledDevDependencies.push(devDependency)
90
- continue
91
- }
92
- yield* Effect.logDebug(
93
- `${logSymbols.success} ${devDependency}@${packageJson.devDependencies?.[devDependency]} is installed`
94
- )
95
- }
96
-
97
- return { uninstalledDependencies, uninstalledDevDependencies }
98
- })
99
-
100
- const registry = stylingLibrary === "uniwind" ? "uniwind" : "nativewind"
101
-
102
- return {
103
- run: (options: DoctorOptions) =>
104
- Effect.gen(function* () {
105
- yield* Effect.logDebug(`Doctor options: ${JSON.stringify(options, null, 2)}`)
106
- const { uninstalledDependencies, uninstalledDevDependencies } = yield* checkRequiredDependencies({
107
- dependencies: PROJECT_MANIFEST.dependencies[registry],
108
- devDependencies: PROJECT_MANIFEST.devDependencies
109
- })
110
-
111
- const { customFileResults, deprecatedFileResults, fileResults } = yield* requiredFileChecker.run({
112
- customFileChecks: PROJECT_MANIFEST.customFileChecks,
113
- deprecatedFromLib: PROJECT_MANIFEST.deprecatedFromLib,
114
- deprecatedFromUi: PROJECT_MANIFEST.deprecatedFromUi,
115
- fileChecks: PROJECT_MANIFEST.fileChecks,
116
- stylingLibrary: registry
117
- })
118
-
119
- const result = {
120
- missingFiles: [...fileResults.missingFiles, ...customFileResults.missingFiles],
121
- uninstalledDependencies,
122
- uninstalledDevDependencies,
123
- missingIncludes: [...fileResults.missingIncludes, ...customFileResults.missingIncludes],
124
- deprecatedFileResults
125
- }
126
-
127
- let total = Object.values(result).reduce((sum, cat) => sum + cat.length, 0)
128
- if (!options.summary) {
129
- const dependenciesToInstall: Array<string> = []
130
- for (const dep of result.uninstalledDependencies) {
131
- const confirmsInstall = options.yes
132
- ? true
133
- : yield* Prompt.confirm({
134
- message: `The ${dep} dependency is missing. Do you want to install it?`,
135
- initial: true
136
- })
137
- if (confirmsInstall) {
138
- if (uninstalledDependencies.includes("expo")) {
139
- continue
140
- }
141
- total--
142
- yield* Effect.logDebug(`Adding ${dep} to dependencies to install`)
143
- dependenciesToInstall.push(dep)
144
- result.uninstalledDependencies = result.uninstalledDependencies.filter((d) => d !== dep)
145
- }
146
- }
147
-
148
- if (dependenciesToInstall.length > 0) {
149
- yield* Effect.logDebug(`Installing ${dependenciesToInstall.join(", ")}`)
150
- if (process.env.INTERNAL_ENV !== "development") {
151
- spinner.start("Installing dependencies")
152
- yield* runCommand("npx", ["expo", "install", ...dependenciesToInstall], {
153
- cwd: options.cwd
154
- })
155
- spinner.stop()
156
- }
157
- }
158
-
159
- const devDependenciesToInstall: Array<string> = []
160
- for (const dep of result.uninstalledDevDependencies) {
161
- const confirmsInstall = options.yes
162
- ? true
163
- : yield* Prompt.confirm({
164
- message: `The ${dep} dependency is missing. Do you want to install it?`,
165
- initial: true
166
- })
167
- if (confirmsInstall) {
168
- if (uninstalledDependencies.includes("expo")) {
169
- continue
170
- }
171
- total--
172
- yield* Effect.logDebug(`Adding ${dep} to devDependencies to install`)
173
- devDependenciesToInstall.push(dep)
174
- result.uninstalledDevDependencies = result.uninstalledDevDependencies.filter((d) => d !== dep)
175
- }
176
- }
177
-
178
- if (devDependenciesToInstall.length > 0) {
179
- yield* Effect.logDebug(`Installing ${devDependenciesToInstall.join(", ")}`)
180
- if (process.env.INTERNAL_ENV !== "development") {
181
- spinner.start("Installing dev dependencies")
182
- yield* runCommand("npx", ["expo", "install", ...devDependenciesToInstall], {
183
- cwd: options.cwd
184
- })
185
- spinner.stop()
186
- }
187
- }
188
- }
189
-
190
- if (total === 0) {
191
- console.log(`\x1b[2m${logSymbols.success} All checks passed.\x1b[0m\n`)
192
- return yield* Effect.succeed(true)
193
- }
194
-
195
- const analysis = analyzeResult(result)
196
- if (options.summary) {
197
- console.log(
198
- `\x1b[2m${logSymbols.warning} ${total} Potential issue${
199
- total > 1 ? "s" : ""
200
- } found. For more info, run: 'npx @react-native-reusables/cli doctor${
201
- options.cwd !== "." ? ` -c ${options.cwd}` : ""
202
- }'\x1b[0m\n`
203
- )
204
- } else {
205
- yield* Effect.log("\n\n🩺 Diagnosis")
206
- for (const item of analysis) {
207
- console.group(`\n${item.title}`)
208
- item.logs.forEach((line) => console.log(line))
209
- console.groupEnd()
210
- }
211
- console.log(`\n`)
212
- }
213
- })
214
- }
215
- })
216
- }) {}
217
-
218
- function make(options: DoctorOptions) {
219
- const optionsLayer = Layer.succeed(CliOptions, { ...options, stylingLibrary: undefined })
220
- return Effect.gen(function* () {
221
- const doctor = yield* Doctor
222
- return yield* doctor.run(options)
223
- }).pipe(Effect.provide(Doctor.Default), Effect.provide(ProjectConfig.Default), Effect.provide(optionsLayer))
224
- }
225
-
226
- export { Doctor, make }
227
-
228
- interface Result {
229
- missingFiles: Array<FileCheck | CustomFileCheck>
230
- missingIncludes: Array<MissingInclude>
231
- uninstalledDependencies: Array<string>
232
- uninstalledDevDependencies: Array<string>
233
- deprecatedFileResults: Array<Omit<FileCheck, "docs" | "stylingLibraries">>
234
- }
235
-
236
- function analyzeResult(result: Result) {
237
- const categories: Array<{ title: string; logs: Array<string>; count: number }> = []
238
-
239
- if (result.missingFiles.length > 0) {
240
- categories.push({
241
- title: `${logSymbols.error} Missing Files (${result.missingFiles.length})`,
242
- count: result.missingFiles.length,
243
- logs: result.missingFiles.flatMap((f) => [`• ${f.name}`, ` 📘 Docs: ${f.docs}`])
244
- })
245
- }
246
-
247
- if (result.missingIncludes.length > 0) {
248
- categories.push({
249
- title: `${logSymbols.error} Potentially Misconfigured Files (${result.missingIncludes.length})`,
250
- count: result.missingIncludes.length,
251
- logs: result.missingIncludes.flatMap((inc) => [
252
- `• ${inc.fileName}`,
253
- ` ↪ ${inc.message}`,
254
- ` 📘 Docs: ${inc.docs}`
255
- ])
256
- })
257
- }
258
-
259
- if (result.uninstalledDependencies.length > 0) {
260
- categories.push({
261
- title: `${logSymbols.error} Missing Dependencies (${result.uninstalledDependencies.length})`,
262
- count: result.uninstalledDependencies.length,
263
- logs: ["• Install with:", ` ↪ npx expo install ${result.uninstalledDependencies.join(" ")}`]
264
- })
265
- }
266
-
267
- if (result.uninstalledDevDependencies.length > 0) {
268
- categories.push({
269
- title: `${logSymbols.error} Missing Dev Dependencies (${result.uninstalledDevDependencies.length})`,
270
- count: result.uninstalledDevDependencies.length,
271
- logs: ["• Install with:", ` ↪ npx expo install -- -D ${result.uninstalledDevDependencies.join(" ")}`]
272
- })
273
- }
274
-
275
- if (result.deprecatedFileResults.length > 0) {
276
- categories.push({
277
- title: `${logSymbols.warning} Deprecated (${result.deprecatedFileResults.length})`,
278
- count: result.deprecatedFileResults.length,
279
- logs: result.deprecatedFileResults.flatMap((deprecatedFile) => [
280
- `• ${deprecatedFile.name}`,
281
- ...deprecatedFile.includes.map((item) => ` ↪ ${item.message}\n 📘 Docs: ${item.docs}`)
282
- ])
283
- })
284
- }
285
-
286
- return categories
287
- }
@@ -1,94 +0,0 @@
1
- import { CliOptions } 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 { ProjectConfig } from "@cli/services/project-config.js"
5
- import { Template } from "@cli/services/template.js"
6
- import { Prompt } from "@effect/cli"
7
- import { FileSystem, Path } from "@effect/platform"
8
- import { Effect, Layer } from "effect"
9
- import logSymbols from "log-symbols"
10
-
11
- type InitOptions = {
12
- cwd: string
13
- template: string
14
- }
15
-
16
- class Init extends Effect.Service<Init>()("Init", {
17
- dependencies: [Template.Default],
18
- effect: Effect.gen(function* () {
19
- const fs = yield* FileSystem.FileSystem
20
- const path = yield* Path.Path
21
- const doctor = yield* Doctor
22
- const template = yield* Template
23
-
24
- return {
25
- run: (options: InitOptions) =>
26
- Effect.gen(function* () {
27
- yield* Effect.logDebug(`Init options: ${JSON.stringify(options, null, 2)}`)
28
-
29
- const packageJsonExists = yield* fs.exists(path.join(options.cwd, "package.json"))
30
-
31
- yield* Effect.logDebug(`Does package.json exist: ${packageJsonExists ? "yes" : "no"}`)
32
-
33
- if (packageJsonExists) {
34
- yield* Effect.logWarning(`${logSymbols.warning} A project already exists in this directory.`)
35
- const choice = yield* Prompt.select({
36
- message: "How would you like to proceed?",
37
- choices: [
38
- { title: "Initialize a new project here anyway", value: "init-new" },
39
- { title: "Inspect project configuration", value: "doctor" },
40
- { title: "Cancel and exit", value: "cancel" }
41
- ]
42
- })
43
- yield* Effect.logDebug(`Init choice: ${choice}`)
44
- if (choice === "cancel") {
45
- return yield* Effect.succeed(true)
46
- }
47
- if (choice === "doctor") {
48
- console.log("")
49
- return yield* doctor.run({ ...options, summary: false, yes: false })
50
- }
51
- }
52
-
53
- const projectName = yield* Prompt.text({
54
- message: "What is the name of your project? (e.g. my-app)",
55
- default: "my-app"
56
- })
57
-
58
- const templateFromFlag = PROJECT_MANIFEST.templates.find((t) => t.subPath === options.template)
59
-
60
- const selectedTemplate = templateFromFlag
61
- ? templateFromFlag
62
- : yield* Prompt.select({
63
- message: "Select a template",
64
- choices: PROJECT_MANIFEST.templates.map((template) => ({
65
- title: template.name,
66
- value: template
67
- }))
68
- })
69
-
70
- yield* template.clone({
71
- cwd: options.cwd,
72
- name: projectName,
73
- repo: selectedTemplate
74
- })
75
- })
76
- }
77
- })
78
- }) {}
79
-
80
- function make(options: InitOptions) {
81
- const optionsLayer = Layer.succeed(CliOptions, { ...options, yes: true, stylingLibrary: undefined })
82
- return Effect.gen(function* () {
83
- const init = yield* Init
84
-
85
- return yield* init.run(options)
86
- }).pipe(
87
- Effect.provide(Init.Default),
88
- Effect.provide(Doctor.Default),
89
- Effect.provide(ProjectConfig.Default),
90
- Effect.provide(optionsLayer)
91
- )
92
- }
93
-
94
- export { make }
@@ -1,39 +0,0 @@
1
- import { Data, Effect } from "effect"
2
- import { Command } from "@effect/platform"
3
- import { Prompt } from "@effect/cli"
4
- import logSymbols from "log-symbols"
5
-
6
- export class GitError extends Data.TaggedError("GitError")<{
7
- cause?: unknown
8
- message?: string
9
- }> {}
10
-
11
- const COMMANDS = {
12
- status: Command.make("git", "status", "--porcelain")
13
- } as const
14
-
15
- export class Git extends Effect.Service<Git>()("Git", {
16
- succeed: {
17
- promptIfDirty: () =>
18
- Effect.gen(function* () {
19
- const gitStatus = yield* COMMANDS.status.pipe(
20
- Command.string,
21
- Effect.catchAll(() => Effect.succeed("")) // Not a git repository
22
- )
23
- const isDirty = gitStatus.trim().length > 0
24
- if (!isDirty) {
25
- return false
26
- }
27
- const result = yield* Prompt.confirm({
28
- message: `${logSymbols.warning} The Git repository is dirty (uncommitted changes). Continue anyway?`,
29
- initial: true
30
- })
31
-
32
- if (!result) {
33
- return yield* Effect.fail(new GitError({ message: "Aborted due to uncommitted changes." }))
34
- }
35
-
36
- return result
37
- })
38
- }
39
- }) {}
@@ -1,48 +0,0 @@
1
- import { Effect } from "effect"
2
- import { detect } from "package-manager-detector"
3
-
4
- const PACKAGE_MANAGERS = ["npm", "bun", "pnpm", "yarn@berry", "yarn"] as const
5
-
6
- const BINARY_RUNNERS = {
7
- npm: ["npx"],
8
- bun: ["bunx", "--bun"],
9
- pnpm: ["pnpm", "dlx"],
10
- yarn: ["npx"],
11
- "yarn@berry": ["npx"]
12
- } as const
13
-
14
- const detectPackageManager = (cwd: string) =>
15
- Effect.tryPromise({
16
- try: () => {
17
- return detect({
18
- cwd,
19
- strategies: ["install-metadata", "lockfile", "packageManager-field", "devEngines-field"]
20
- })
21
- },
22
- catch: () => new Error("Failed to get package manager")
23
- })
24
-
25
- const getPackageManager = (cwd: string) =>
26
- Effect.gen(function* () {
27
- const pm = yield* detectPackageManager(cwd)
28
- if (!pm) {
29
- return "npm"
30
- }
31
- const name = PACKAGE_MANAGERS.find((name) => pm.agent.startsWith(name) || pm.name.startsWith(name)) ?? "npm"
32
- return name
33
- })
34
- const getBinaryRunner = (cwd: string) =>
35
- Effect.gen(function* () {
36
- const pm = yield* getPackageManager(cwd)
37
- return BINARY_RUNNERS[pm]
38
- })
39
-
40
- class PackageManager extends Effect.Service<PackageManager>()("PackageManager", {
41
- succeed: {
42
- detectPackageManager,
43
- getPackageManager,
44
- getBinaryRunner
45
- }
46
- }) {}
47
-
48
- export { PackageManager }