@effect-app/cli 1.29.2 → 2.0.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/CHANGELOG.md +6 -0
- package/dist/extract.d.ts +1 -2
- package/dist/extract.d.ts.map +1 -1
- package/dist/extract.js +3 -4
- package/dist/gist.d.ts +180 -103
- package/dist/gist.d.ts.map +1 -1
- package/dist/gist.js +43 -57
- package/dist/index.js +21 -28
- package/dist/os-command.d.ts +12 -9
- package/dist/os-command.d.ts.map +1 -1
- package/dist/os-command.js +13 -17
- package/package.json +9 -8
- package/src/extract.ts +2 -3
- package/src/gist.ts +88 -99
- package/src/index.ts +636 -650
- package/src/os-command.ts +14 -25
- package/tsconfig.json +1 -29
package/src/index.ts
CHANGED
|
@@ -1,738 +1,724 @@
|
|
|
1
1
|
/* eslint-disable no-constant-binary-expression */
|
|
2
2
|
/* eslint-disable no-empty-pattern */
|
|
3
3
|
// import necessary modules from the libraries
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { NodeContext, NodeRuntime } from "@effect/platform-node"
|
|
4
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node"
|
|
5
|
+
import { Argument, Command, Flag, Prompt } from "effect/unstable/cli"
|
|
7
6
|
|
|
8
|
-
import {
|
|
9
|
-
import { type PlatformError } from "@effect/platform/Error"
|
|
10
|
-
import { Effect, Layer, Option, Stream, type Types } from "effect"
|
|
7
|
+
import { Effect, FileSystem, Layer, Option, Path, Stream } from "effect"
|
|
11
8
|
import { ExtractExportMappingsService } from "./extract.js"
|
|
12
9
|
import { GistHandler } from "./gist.js"
|
|
13
10
|
import { RunCommandService } from "./os-command.js"
|
|
14
11
|
import { packages } from "./shared.js"
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
13
|
+
NodeRuntime.runMain(
|
|
14
|
+
Effect
|
|
15
|
+
.fn("effa-cli")(function*() {
|
|
16
|
+
const fs = yield* FileSystem.FileSystem
|
|
17
|
+
const path = yield* Path.Path
|
|
18
|
+
const extractExportMappings = yield* ExtractExportMappingsService
|
|
19
|
+
const { runGetExitCode } = yield* RunCommandService
|
|
20
|
+
|
|
21
|
+
yield* Effect.addFinalizer(() => Effect.logInfo(`CLI has finished executing`))
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Updates effect-app packages to their latest versions using npm-check-updates.
|
|
25
|
+
* Runs both at workspace root and recursively in all workspace packages.
|
|
26
|
+
*/
|
|
27
|
+
const updateEffectAppPackages = Effect.fn("effa-cli.ue.updateEffectAppPackages")(function*() {
|
|
28
|
+
const filters = ["effect-app", "@effect-app/*"]
|
|
29
|
+
for (const filter of filters) {
|
|
30
|
+
yield* runGetExitCode(`pnpm exec ncu -u --filter "${filter}"`)
|
|
31
|
+
yield* runGetExitCode(`pnpm -r exec ncu -u --filter "${filter}"`)
|
|
32
|
+
}
|
|
33
|
+
})()
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Updates Effect ecosystem packages to their latest versions using npm-check-updates.
|
|
37
|
+
* Covers core Effect packages, Effect ecosystem packages, and Effect Atom packages.
|
|
38
|
+
* Runs both at workspace root and recursively in all workspace packages.
|
|
39
|
+
*/
|
|
40
|
+
const updateEffectPackages = Effect.fn("effa-cli.ue.updateEffectPackages")(function*() {
|
|
41
|
+
const effectFilters = ["effect", "@effect/*", "@effect-atom/*"]
|
|
42
|
+
for (const filter of effectFilters) {
|
|
43
|
+
yield* runGetExitCode(`pnpm exec ncu -u --filter "${filter}"`)
|
|
44
|
+
yield* runGetExitCode(`pnpm -r exec ncu -u --filter "${filter}"`)
|
|
45
|
+
}
|
|
46
|
+
})()
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Updates all packages except Effect and Effect-App ecosystem packages to their latest versions using npm-check-updates.
|
|
50
|
+
* Excludes core Effect packages, Effect ecosystem packages, Effect Atom packages, and Effect-App packages.
|
|
51
|
+
* Preserves existing rejections from .ncurc.json configuration.
|
|
52
|
+
* Runs both at workspace root and recursively in all workspace packages.
|
|
53
|
+
*/
|
|
54
|
+
const updatePackages = Effect.fn("effa-cli.update-packages.updatePackages")(function*() {
|
|
55
|
+
const effectFilters = ["effect", "@effect/*", "@effect-atom/*", "effect-app", "@effect-app/*"]
|
|
56
|
+
|
|
57
|
+
// read existing .ncurc.json to preserve existing reject patterns
|
|
58
|
+
let existingRejects: string[] = []
|
|
59
|
+
const ncurcPath = "./.ncurc.json"
|
|
60
|
+
|
|
61
|
+
if (yield* fs.exists(ncurcPath)) {
|
|
62
|
+
const ncurcContent = yield* fs.readFileString(ncurcPath)
|
|
63
|
+
const ncurc = JSON.parse(ncurcContent)
|
|
64
|
+
if (ncurc.reject && Array.isArray(ncurc.reject)) {
|
|
65
|
+
existingRejects = ncurc.reject
|
|
66
|
+
}
|
|
67
|
+
}
|
|
49
68
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
const allRejects = [...existingRejects, ...effectFilters]
|
|
70
|
+
yield* Effect.logInfo(`Excluding packages from update: ${allRejects.join(", ")}`)
|
|
71
|
+
const rejectArgs = allRejects.map((filter) => `--reject "${filter}"`).join(" ")
|
|
72
|
+
|
|
73
|
+
yield* runGetExitCode(`pnpm exec ncu -u ${rejectArgs}`)
|
|
74
|
+
yield* runGetExitCode(`pnpm -r exec ncu -u ${rejectArgs}`)
|
|
75
|
+
})()
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Links local effect-app packages by adding file resolutions to package.json.
|
|
79
|
+
* Updates the package.json with file: protocol paths pointing to the local effect-app-libs directory,
|
|
80
|
+
* then runs pnpm install to apply the changes.
|
|
81
|
+
*
|
|
82
|
+
* @param effectAppLibsPath - Path to the local effect-app-libs directory
|
|
83
|
+
* @returns An Effect that succeeds when linking is complete
|
|
84
|
+
*/
|
|
85
|
+
const linkPackages = Effect.fnUntraced(function*(effectAppLibsPath: string) {
|
|
86
|
+
yield* Effect.logInfo("Linking local effect-app packages...")
|
|
87
|
+
|
|
88
|
+
const packageJsonPath = "./package.json"
|
|
89
|
+
const packageJsonContent = yield* fs.readFileString(packageJsonPath)
|
|
90
|
+
const pj = JSON.parse(packageJsonContent)
|
|
91
|
+
|
|
92
|
+
const resolutions = {
|
|
93
|
+
...pj.resolutions,
|
|
94
|
+
"@effect-app/eslint-codegen-model": "file:" + effectAppLibsPath + "/packages/eslint-codegen-model",
|
|
95
|
+
"effect-app": "file:" + effectAppLibsPath + "/packages/effect-app",
|
|
96
|
+
"@effect-app/infra": "file:" + effectAppLibsPath + "/packages/infra",
|
|
97
|
+
"@effect-app/vue": "file:" + effectAppLibsPath + "/packages/vue",
|
|
98
|
+
"@effect-app/vue-components": "file:" + effectAppLibsPath + "/packages/vue-components",
|
|
99
|
+
"@effect-app/eslint-shared-config": "file:" + effectAppLibsPath + "/packages/eslint-shared-config",
|
|
100
|
+
...packages.reduce((acc, p) => ({ ...acc, [p]: `file:${effectAppLibsPath}/node_modules/${p}` }), {})
|
|
68
101
|
}
|
|
69
|
-
}
|
|
70
102
|
|
|
71
|
-
|
|
72
|
-
yield* Effect.logInfo(`Excluding packages from update: ${allRejects.join(", ")}`)
|
|
73
|
-
const rejectArgs = allRejects.map((filter) => `--reject "${filter}"`).join(" ")
|
|
103
|
+
pj.resolutions = resolutions
|
|
74
104
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
})()
|
|
105
|
+
yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2))
|
|
106
|
+
yield* Effect.logInfo("Updated package.json with local file resolutions")
|
|
78
107
|
|
|
79
|
-
|
|
80
|
-
* Links local effect-app packages by adding file resolutions to package.json.
|
|
81
|
-
* Updates the package.json with file: protocol paths pointing to the local effect-app-libs directory,
|
|
82
|
-
* then runs pnpm install to apply the changes.
|
|
83
|
-
*
|
|
84
|
-
* @param effectAppLibsPath - Path to the local effect-app-libs directory
|
|
85
|
-
* @returns An Effect that succeeds when linking is complete
|
|
86
|
-
*/
|
|
87
|
-
const linkPackages = Effect.fnUntraced(function*(effectAppLibsPath: string) {
|
|
88
|
-
yield* Effect.logInfo("Linking local effect-app packages...")
|
|
89
|
-
|
|
90
|
-
const packageJsonPath = "./package.json"
|
|
91
|
-
const packageJsonContent = yield* fs.readFileString(packageJsonPath)
|
|
92
|
-
const pj = JSON.parse(packageJsonContent)
|
|
93
|
-
|
|
94
|
-
const resolutions = {
|
|
95
|
-
...pj.resolutions,
|
|
96
|
-
"@effect-app/eslint-codegen-model": "file:" + effectAppLibsPath + "/packages/eslint-codegen-model",
|
|
97
|
-
"effect-app": "file:" + effectAppLibsPath + "/packages/effect-app",
|
|
98
|
-
"@effect-app/infra": "file:" + effectAppLibsPath + "/packages/infra",
|
|
99
|
-
"@effect-app/vue": "file:" + effectAppLibsPath + "/packages/vue",
|
|
100
|
-
"@effect-app/vue-components": "file:" + effectAppLibsPath + "/packages/vue-components",
|
|
101
|
-
"@effect-app/eslint-shared-config": "file:" + effectAppLibsPath + "/packages/eslint-shared-config",
|
|
102
|
-
...packages.reduce((acc, p) => ({ ...acc, [p]: `file:${effectAppLibsPath}/node_modules/${p}` }), {})
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
pj.resolutions = resolutions
|
|
106
|
-
|
|
107
|
-
yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2))
|
|
108
|
-
yield* Effect.logInfo("Updated package.json with local file resolutions")
|
|
109
|
-
|
|
110
|
-
yield* runGetExitCode("pnpm i")
|
|
111
|
-
|
|
112
|
-
yield* Effect.logInfo("Successfully linked local packages")
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Unlinks local effect-app packages by removing file resolutions from package.json.
|
|
117
|
-
* Filters out all effect-app related file: protocol resolutions from package.json,
|
|
118
|
-
* then runs pnpm install to restore registry packages.
|
|
119
|
-
*
|
|
120
|
-
* @returns An Effect that succeeds when unlinking is complete
|
|
121
|
-
*/
|
|
122
|
-
const unlinkPackages = Effect.fnUntraced(function*() {
|
|
123
|
-
yield* Effect.logInfo("Unlinking local effect-app packages...")
|
|
124
|
-
|
|
125
|
-
const packageJsonPath = "./package.json"
|
|
126
|
-
const packageJsonContent = yield* fs.readFileString(packageJsonPath)
|
|
127
|
-
const pj = JSON.parse(packageJsonContent)
|
|
128
|
-
|
|
129
|
-
const filteredResolutions = Object.entries(pj.resolutions as Record<string, string>).reduce(
|
|
130
|
-
(acc, [k, v]) => {
|
|
131
|
-
if (k.startsWith("@effect-app/") || k === "effect-app" || packages.includes(k)) return acc
|
|
132
|
-
acc[k] = v
|
|
133
|
-
return acc
|
|
134
|
-
},
|
|
135
|
-
{} as Record<string, string>
|
|
136
|
-
)
|
|
108
|
+
yield* runGetExitCode("pnpm i")
|
|
137
109
|
|
|
138
|
-
|
|
110
|
+
yield* Effect.logInfo("Successfully linked local packages")
|
|
111
|
+
})
|
|
139
112
|
|
|
140
|
-
|
|
141
|
-
|
|
113
|
+
/**
|
|
114
|
+
* Unlinks local effect-app packages by removing file resolutions from package.json.
|
|
115
|
+
* Filters out all effect-app related file: protocol resolutions from package.json,
|
|
116
|
+
* then runs pnpm install to restore registry packages.
|
|
117
|
+
*
|
|
118
|
+
* @returns An Effect that succeeds when unlinking is complete
|
|
119
|
+
*/
|
|
120
|
+
const unlinkPackages = Effect.fnUntraced(function*() {
|
|
121
|
+
yield* Effect.logInfo("Unlinking local effect-app packages...")
|
|
122
|
+
|
|
123
|
+
const packageJsonPath = "./package.json"
|
|
124
|
+
const packageJsonContent = yield* fs.readFileString(packageJsonPath)
|
|
125
|
+
const pj = JSON.parse(packageJsonContent)
|
|
126
|
+
|
|
127
|
+
const filteredResolutions = Object.entries(pj.resolutions as Record<string, string>).reduce(
|
|
128
|
+
(acc, [k, v]) => {
|
|
129
|
+
if (k.startsWith("@effect-app/") || k === "effect-app" || packages.includes(k)) return acc
|
|
130
|
+
acc[k] = v
|
|
131
|
+
return acc
|
|
132
|
+
},
|
|
133
|
+
{} as Record<string, string>
|
|
134
|
+
)
|
|
142
135
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
pj.resolutions = filteredResolutions
|
|
137
|
+
|
|
138
|
+
yield* fs.writeFileString(packageJsonPath, JSON.stringify(pj, null, 2))
|
|
139
|
+
yield* Effect.logInfo("Removed effect-app file resolutions from package.json")
|
|
140
|
+
|
|
141
|
+
yield* runGetExitCode("pnpm i")
|
|
142
|
+
yield* Effect.logInfo("Successfully unlinked local packages")
|
|
143
|
+
})()
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Monitors controller files for changes and runs eslint on related controllers.ts/routes.ts files.
|
|
147
|
+
* Watches for .controllers. files and triggers eslint fixes on parent directory's controller files.
|
|
148
|
+
*
|
|
149
|
+
* @param watchPath - The path to watch for controller changes
|
|
150
|
+
* @param debug - Whether to enable debug logging
|
|
151
|
+
* @returns An Effect that sets up controller file monitoring
|
|
152
|
+
*/
|
|
153
|
+
const monitorChildIndexes = Effect.fn("effa-cli.index-multi.monitorChildIndexes")(
|
|
154
|
+
function*(watchPath: string) {
|
|
155
|
+
yield* Effect.logInfo(`Starting controller monitoring for: ${watchPath}`)
|
|
156
|
+
|
|
157
|
+
const watchStream = fs.watch(watchPath)
|
|
158
|
+
|
|
159
|
+
yield* watchStream
|
|
160
|
+
.pipe(
|
|
161
|
+
Stream.runForEach(
|
|
162
|
+
Effect.fn("effa-cli.monitorChildIndexes.handleEvent")(function*(event) {
|
|
163
|
+
const pathParts = event.path.split("/")
|
|
164
|
+
const fileName = pathParts[pathParts.length - 1]
|
|
165
|
+
const isController = fileName?.toLowerCase().includes(".controllers.")
|
|
166
|
+
|
|
167
|
+
if (!isController) return
|
|
168
|
+
|
|
169
|
+
let i = 1
|
|
170
|
+
const reversedParts = pathParts.toReversed()
|
|
171
|
+
|
|
172
|
+
while (i < reversedParts.length) {
|
|
173
|
+
const candidateFiles = ["controllers.ts", "routes.ts"]
|
|
174
|
+
.map((f) => [...pathParts.slice(0, pathParts.length - i), f].join("/"))
|
|
175
|
+
|
|
176
|
+
const existingFiles: string[] = []
|
|
177
|
+
for (const file of candidateFiles) {
|
|
178
|
+
const exists = yield* fs.exists(file)
|
|
179
|
+
if (exists) existingFiles.push(file)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (existingFiles.length > 0) {
|
|
183
|
+
yield* Effect.logInfo(
|
|
184
|
+
`Controller change detected: ${event.path}, fixing files: ${existingFiles.join(", ")}`
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
const eslintArgs = existingFiles.map((f) => `"../${f}"`).join(" ")
|
|
188
|
+
yield* runGetExitCode(`cd api && pnpm eslint --fix ${eslintArgs}`)
|
|
189
|
+
break
|
|
190
|
+
}
|
|
191
|
+
i++
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
),
|
|
195
|
+
Effect.andThen(
|
|
196
|
+
Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring child indexes in: ${watchPath}`))
|
|
197
|
+
),
|
|
198
|
+
Effect.forkScoped
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
)
|
|
146
202
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
203
|
+
/**
|
|
204
|
+
* Monitors a directory for changes and runs eslint on the specified index file.
|
|
205
|
+
* Triggers eslint fixes when any file in the directory changes (except the index file itself).
|
|
206
|
+
*
|
|
207
|
+
* @param watchPath - The path to watch for changes
|
|
208
|
+
* @param indexFile - The index file to run eslint on when changes occur
|
|
209
|
+
* @param debug - Whether to enable debug logging
|
|
210
|
+
* @returns An Effect that sets up root index monitoring
|
|
211
|
+
*/
|
|
212
|
+
const monitorRootIndexes = Effect.fn("effa-cli.index-multi.monitorRootIndexes")(
|
|
213
|
+
function*(watchPath: string, indexFile: string) {
|
|
214
|
+
yield* Effect.logInfo(`Starting root index monitoring for: ${watchPath} -> ${indexFile}`)
|
|
215
|
+
|
|
216
|
+
const watchStream = fs.watch(watchPath)
|
|
217
|
+
|
|
218
|
+
yield* watchStream
|
|
219
|
+
.pipe(
|
|
220
|
+
Stream.runForEach(
|
|
221
|
+
Effect.fn("effa-cli.index-multi.monitorRootIndexes.handleEvent")(function*(event) {
|
|
222
|
+
if (event.path.endsWith(indexFile)) return
|
|
223
|
+
|
|
224
|
+
yield* Effect.logInfo(`Root change detected: ${event.path}, fixing: ${indexFile}`)
|
|
225
|
+
|
|
226
|
+
yield* runGetExitCode(`pnpm eslint --fix "${indexFile}"`)
|
|
227
|
+
})
|
|
228
|
+
),
|
|
229
|
+
Effect.andThen(
|
|
230
|
+
Effect.addFinalizer(() =>
|
|
231
|
+
Effect.logInfo(`Stopped monitoring root indexes in: ${watchPath} -> ${indexFile}`)
|
|
232
|
+
)
|
|
233
|
+
),
|
|
234
|
+
Effect.forkScoped
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
)
|
|
168
238
|
|
|
169
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Sets up comprehensive index monitoring for a given path.
|
|
241
|
+
* Combines both child controller monitoring and root index monitoring.
|
|
242
|
+
*
|
|
243
|
+
* @param watchPath - The path to monitor
|
|
244
|
+
* @param debug - Whether to enable debug logging
|
|
245
|
+
* @returns An Effect that sets up all index monitoring for the path
|
|
246
|
+
*/
|
|
247
|
+
const monitorIndexes = Effect.fn("effa-cli.index-multi.monitorIndexes")(
|
|
248
|
+
function*(watchPath: string) {
|
|
249
|
+
yield* Effect.logInfo(`Setting up index monitoring for path: ${watchPath}`)
|
|
170
250
|
|
|
171
|
-
|
|
172
|
-
const reversedParts = pathParts.toReversed()
|
|
251
|
+
const indexFile = watchPath + "/index.ts"
|
|
173
252
|
|
|
174
|
-
|
|
175
|
-
const candidateFiles = ["controllers.ts", "routes.ts"]
|
|
176
|
-
.map((f) => [...pathParts.slice(0, pathParts.length - i), f].join("/"))
|
|
253
|
+
const monitors = [monitorChildIndexes(watchPath)]
|
|
177
254
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
255
|
+
if (yield* fs.exists(indexFile)) {
|
|
256
|
+
monitors.push(monitorRootIndexes(watchPath, indexFile))
|
|
257
|
+
} else {
|
|
258
|
+
yield* Effect.logWarning(`Index file ${indexFile} does not exist`)
|
|
259
|
+
}
|
|
183
260
|
|
|
184
|
-
|
|
185
|
-
yield* Effect.logInfo(
|
|
186
|
-
`Controller change detected: ${event.path}, fixing files: ${existingFiles.join(", ")}`
|
|
187
|
-
)
|
|
261
|
+
yield* Effect.logInfo(`Starting ${monitors.length} monitor(s) for ${watchPath}`)
|
|
188
262
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
263
|
+
yield* Effect.all(monitors, { concurrency: monitors.length })
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Updates a package.json file with generated exports mappings for TypeScript modules.
|
|
269
|
+
* Scans TypeScript source files and creates export entries that map module paths
|
|
270
|
+
* to their compiled JavaScript and TypeScript declaration files.
|
|
271
|
+
*
|
|
272
|
+
* @param startDir - The starting directory path for resolving relative paths
|
|
273
|
+
* @param p - The package directory path to process
|
|
274
|
+
* @param levels - Optional depth limit for export filtering (0 = no limit)
|
|
275
|
+
* @returns An Effect that succeeds when the package.json is updated
|
|
276
|
+
*/
|
|
277
|
+
const packagejsonUpdater = Effect.fn("effa-cli.packagejsonUpdater")(
|
|
278
|
+
function*(startDir: string, p: string, levels = 0) {
|
|
279
|
+
yield* Effect.logInfo(`Generating exports for ${p}`)
|
|
280
|
+
|
|
281
|
+
const exportMappings = yield* extractExportMappings(path.resolve(startDir, p))
|
|
282
|
+
|
|
283
|
+
// if exportMappings is empty skip export generation
|
|
284
|
+
if (exportMappings === "") {
|
|
285
|
+
yield* Effect.logInfo(`No src directory found for ${p}, skipping export generation`)
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const sortedExportEntries = JSON.parse(
|
|
290
|
+
`{ ${exportMappings} }`
|
|
291
|
+
) as Record<
|
|
292
|
+
string,
|
|
293
|
+
unknown
|
|
294
|
+
>
|
|
295
|
+
|
|
296
|
+
const filteredExportEntries = levels
|
|
297
|
+
? Object
|
|
298
|
+
.keys(sortedExportEntries)
|
|
299
|
+
// filter exports by directory depth - only include paths up to specified levels deep
|
|
300
|
+
.filter((_) => _.split("/").length <= (levels + 1 /* `./` */))
|
|
301
|
+
.reduce(
|
|
302
|
+
(prev, cur) => ({ ...prev, [cur]: sortedExportEntries[cur] }),
|
|
303
|
+
{} as Record<string, unknown>
|
|
304
|
+
)
|
|
305
|
+
: sortedExportEntries
|
|
306
|
+
|
|
307
|
+
const packageExports = {
|
|
308
|
+
...((yield* fs.exists(p + "/src/index.ts"))
|
|
309
|
+
&& {
|
|
310
|
+
".": {
|
|
311
|
+
"types": "./dist/index.d.ts",
|
|
312
|
+
"default": "./dist/index.js"
|
|
194
313
|
}
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
314
|
+
}),
|
|
315
|
+
...Object
|
|
316
|
+
.keys(filteredExportEntries)
|
|
317
|
+
.reduce(
|
|
318
|
+
(prev, cur) => ({
|
|
319
|
+
...prev,
|
|
320
|
+
// exclude index files and internal modules from package exports:
|
|
321
|
+
// - skip "./index" to avoid conflicts with the main "." export
|
|
322
|
+
// - skip "/internal/" paths to keep internal modules private
|
|
323
|
+
...cur !== "./index" && !cur.includes("/internal/") && { [cur]: filteredExportEntries[cur] }
|
|
324
|
+
}),
|
|
325
|
+
{} as Record<string, unknown>
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const pkgJson = JSON.parse(yield* fs.readFileString(p + "/package.json", "utf-8"))
|
|
330
|
+
|
|
331
|
+
pkgJson.exports = packageExports
|
|
332
|
+
|
|
333
|
+
yield* Effect.logInfo(`Writing updated package.json for ${p}`)
|
|
334
|
+
|
|
335
|
+
return yield* fs.writeFileString(
|
|
336
|
+
p + "/package.json",
|
|
337
|
+
JSON.stringify(pkgJson, null, 2)
|
|
201
338
|
)
|
|
202
|
-
|
|
203
|
-
|
|
339
|
+
}
|
|
340
|
+
)
|
|
204
341
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
Effect.fn("effa-cli.index-multi.monitorRootIndexes.handleEvent")(function*(event) {
|
|
224
|
-
if (event.path.endsWith(indexFile)) return
|
|
342
|
+
/**
|
|
343
|
+
* Monitors a directory for TypeScript file changes and automatically updates package.json exports.
|
|
344
|
+
* Generates initial package.json exports, then watches the src directory for changes to regenerate exports.
|
|
345
|
+
*
|
|
346
|
+
* @param watchPath - The directory path containing the package.json and src to monitor
|
|
347
|
+
* @param levels - Optional depth limit for export filtering (0 = no limit)
|
|
348
|
+
* @returns An Effect that sets up package.json monitoring
|
|
349
|
+
*/
|
|
350
|
+
const monitorPackageJson = Effect.fn("effa-cli.monitorPackageJson")(
|
|
351
|
+
function*(startDir: string, watchPath: string, levels = 0) {
|
|
352
|
+
yield* packagejsonUpdater(startDir, watchPath, levels)
|
|
353
|
+
|
|
354
|
+
const srcPath = watchPath === "." ? "./src" : `${watchPath}/src`
|
|
355
|
+
|
|
356
|
+
if (!(yield* fs.exists(srcPath))) {
|
|
357
|
+
yield* Effect.logWarning(`Source directory ${srcPath} does not exist - skipping monitoring`)
|
|
358
|
+
return
|
|
359
|
+
}
|
|
225
360
|
|
|
226
|
-
|
|
361
|
+
const watchStream = fs.watch(srcPath)
|
|
227
362
|
|
|
228
|
-
|
|
363
|
+
yield* watchStream.pipe(
|
|
364
|
+
Stream.runForEach(
|
|
365
|
+
Effect.fn("effa-cli.monitorPackageJson.handleEvent")(function*(_) {
|
|
366
|
+
yield* packagejsonUpdater(startDir, watchPath, levels)
|
|
229
367
|
})
|
|
230
368
|
),
|
|
231
369
|
Effect.andThen(
|
|
232
|
-
Effect.addFinalizer(() =>
|
|
233
|
-
Effect.logInfo(`Stopped monitoring root indexes in: ${watchPath} -> ${indexFile}`)
|
|
234
|
-
)
|
|
370
|
+
Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring package.json for: ${watchPath}`))
|
|
235
371
|
),
|
|
236
372
|
Effect.forkScoped
|
|
237
373
|
)
|
|
238
|
-
}
|
|
239
|
-
)
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Sets up comprehensive index monitoring for a given path.
|
|
243
|
-
* Combines both child controller monitoring and root index monitoring.
|
|
244
|
-
*
|
|
245
|
-
* @param watchPath - The path to monitor
|
|
246
|
-
* @param debug - Whether to enable debug logging
|
|
247
|
-
* @returns An Effect that sets up all index monitoring for the path
|
|
248
|
-
*/
|
|
249
|
-
const monitorIndexes = Effect.fn("effa-cli.index-multi.monitorIndexes")(
|
|
250
|
-
function*(watchPath: string) {
|
|
251
|
-
yield* Effect.logInfo(`Setting up index monitoring for path: ${watchPath}`)
|
|
252
|
-
|
|
253
|
-
const indexFile = watchPath + "/index.ts"
|
|
254
|
-
|
|
255
|
-
const monitors = [monitorChildIndexes(watchPath)]
|
|
256
|
-
|
|
257
|
-
if (yield* fs.exists(indexFile)) {
|
|
258
|
-
monitors.push(monitorRootIndexes(watchPath, indexFile))
|
|
259
|
-
} else {
|
|
260
|
-
yield* Effect.logWarning(`Index file ${indexFile} does not exist`)
|
|
261
374
|
}
|
|
375
|
+
)
|
|
262
376
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
)
|
|
377
|
+
/*
|
|
378
|
+
* CLI
|
|
379
|
+
*/
|
|
268
380
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
* @param levels - Optional depth limit for export filtering (0 = no limit)
|
|
277
|
-
* @returns An Effect that succeeds when the package.json is updated
|
|
278
|
-
*/
|
|
279
|
-
const packagejsonUpdater = Effect.fn("effa-cli.packagejsonUpdater")(
|
|
280
|
-
function*(startDir: string, p: string, levels = 0) {
|
|
281
|
-
yield* Effect.logInfo(`Generating exports for ${p}`)
|
|
282
|
-
|
|
283
|
-
const exportMappings = yield* extractExportMappings(path.resolve(startDir, p))
|
|
284
|
-
|
|
285
|
-
// if exportMappings is empty skip export generation
|
|
286
|
-
if (exportMappings === "") {
|
|
287
|
-
yield* Effect.logInfo(`No src directory found for ${p}, skipping export generation`)
|
|
288
|
-
return
|
|
289
|
-
}
|
|
381
|
+
const WrapAsOption = Flag.string("wrap").pipe(
|
|
382
|
+
Flag.withAlias("w"),
|
|
383
|
+
Flag.optional,
|
|
384
|
+
Flag.withDescription(
|
|
385
|
+
"Wrap child bash command: the lifetime of the CLI command will be tied to the child process"
|
|
386
|
+
)
|
|
387
|
+
)
|
|
290
388
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
// filter exports by directory depth - only include paths up to specified levels deep
|
|
302
|
-
.filter((_) => _.split("/").length <= (levels + 1 /* `./` */))
|
|
303
|
-
.reduce(
|
|
304
|
-
(prev, cur) => ({ ...prev, [cur]: sortedExportEntries[cur] }),
|
|
305
|
-
{} as Record<string, unknown>
|
|
306
|
-
)
|
|
307
|
-
: sortedExportEntries
|
|
308
|
-
|
|
309
|
-
const packageExports = {
|
|
310
|
-
...((yield* fs.exists(p + "/src/index.ts"))
|
|
311
|
-
&& {
|
|
312
|
-
".": {
|
|
313
|
-
"types": "./dist/index.d.ts",
|
|
314
|
-
"default": "./dist/index.js"
|
|
315
|
-
}
|
|
316
|
-
}),
|
|
317
|
-
...Object
|
|
318
|
-
.keys(filteredExportEntries)
|
|
319
|
-
.reduce(
|
|
320
|
-
(prev, cur) => ({
|
|
321
|
-
...prev,
|
|
322
|
-
// exclude index files and internal modules from package exports:
|
|
323
|
-
// - skip "./index" to avoid conflicts with the main "." export
|
|
324
|
-
// - skip "/internal/" paths to keep internal modules private
|
|
325
|
-
...cur !== "./index" && !cur.includes("/internal/") && { [cur]: filteredExportEntries[cur] }
|
|
326
|
-
}),
|
|
327
|
-
{} as Record<string, unknown>
|
|
328
|
-
)
|
|
329
|
-
}
|
|
389
|
+
// has prio over WrapAsOption
|
|
390
|
+
const WrapAsArg = Argument
|
|
391
|
+
.string("wrap")
|
|
392
|
+
.pipe(
|
|
393
|
+
Argument.atLeast(1),
|
|
394
|
+
Argument.optional,
|
|
395
|
+
Argument.withDescription(
|
|
396
|
+
"Wrap child bash command: the lifetime of the CLI command will be tied to the child process"
|
|
397
|
+
)
|
|
398
|
+
)
|
|
330
399
|
|
|
331
|
-
|
|
400
|
+
/**
|
|
401
|
+
* Creates a command that automatically includes wrap functionality for executing child bash commands.
|
|
402
|
+
* Combines both option-based (--wrap) and argument-based wrap parameters, giving priority to arguments.
|
|
403
|
+
* If a wrap command is provided, it will be executed **after** the main command handler.
|
|
404
|
+
*
|
|
405
|
+
* @param name - The command name
|
|
406
|
+
* @param config - The command configuration (options, args, etc.)
|
|
407
|
+
* @param handler - The main command handler function
|
|
408
|
+
* @param completionMessage - Optional message to log when the command completes
|
|
409
|
+
* @returns A Command with integrated wrap functionality
|
|
410
|
+
*/
|
|
411
|
+
const makeCommandWithWrap = <Name extends string, const Config, R, E>(
|
|
412
|
+
name: Name,
|
|
413
|
+
config: Config,
|
|
414
|
+
handler: (_: any) => Effect.Effect<void, E, R>,
|
|
415
|
+
completionMessage?: string
|
|
416
|
+
) =>
|
|
417
|
+
Command.make(
|
|
418
|
+
name,
|
|
419
|
+
{ ...config, wo: WrapAsOption, wa: WrapAsArg },
|
|
420
|
+
Effect.fn("effa-cli.withWrapHandler")(function*(_) {
|
|
421
|
+
const { wa, wo, ...cfg } = _ as unknown as {
|
|
422
|
+
wo: Option.Option<string>
|
|
423
|
+
wa: Option.Option<ReadonlyArray<string>>
|
|
424
|
+
}
|
|
332
425
|
|
|
333
|
-
|
|
426
|
+
if (completionMessage) {
|
|
427
|
+
yield* Effect.addFinalizer(() => Effect.logInfo(completionMessage))
|
|
428
|
+
}
|
|
334
429
|
|
|
335
|
-
|
|
430
|
+
const wrapOption = Option.orElse(wa, () => wo)
|
|
336
431
|
|
|
337
|
-
|
|
338
|
-
p + "/package.json",
|
|
339
|
-
JSON.stringify(pkgJson, null, 2)
|
|
340
|
-
)
|
|
341
|
-
}
|
|
342
|
-
)
|
|
432
|
+
yield* handler(cfg as any)
|
|
343
433
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
* @param watchPath - The directory path containing the package.json and src to monitor
|
|
349
|
-
* @param levels - Optional depth limit for export filtering (0 = no limit)
|
|
350
|
-
* @returns An Effect that sets up package.json monitoring
|
|
351
|
-
*/
|
|
352
|
-
const monitorPackageJson = Effect.fn("effa-cli.monitorPackageJson")(
|
|
353
|
-
function*(startDir: string, watchPath: string, levels = 0) {
|
|
354
|
-
yield* packagejsonUpdater(startDir, watchPath, levels)
|
|
355
|
-
|
|
356
|
-
const srcPath = watchPath === "." ? "./src" : `${watchPath}/src`
|
|
357
|
-
|
|
358
|
-
if (!(yield* fs.exists(srcPath))) {
|
|
359
|
-
yield* Effect.logWarning(`Source directory ${srcPath} does not exist - skipping monitoring`)
|
|
360
|
-
return
|
|
361
|
-
}
|
|
434
|
+
if (Option.isSome(wrapOption)) {
|
|
435
|
+
const val: string = Array.isArray(wrapOption.value)
|
|
436
|
+
? wrapOption.value.join(" ")
|
|
437
|
+
: wrapOption.value as string
|
|
362
438
|
|
|
363
|
-
|
|
439
|
+
yield* Effect.logInfo(`Spawning child command: ${val}`)
|
|
440
|
+
const exitCode = yield* runGetExitCode(val)
|
|
441
|
+
if (exitCode !== 0) {
|
|
442
|
+
return yield* Effect.sync(() => process.exit(exitCode))
|
|
443
|
+
}
|
|
444
|
+
}
|
|
364
445
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
Effect.fn("effa-cli.monitorPackageJson.handleEvent")(function*(_) {
|
|
368
|
-
yield* packagejsonUpdater(startDir, watchPath, levels)
|
|
369
|
-
})
|
|
370
|
-
),
|
|
371
|
-
Effect.andThen(
|
|
372
|
-
Effect.addFinalizer(() => Effect.logInfo(`Stopped monitoring package.json for: ${watchPath}`))
|
|
373
|
-
),
|
|
374
|
-
Effect.forkScoped
|
|
446
|
+
return
|
|
447
|
+
}, (_) => Effect.scoped(_))
|
|
375
448
|
)
|
|
376
|
-
}
|
|
377
|
-
)
|
|
378
|
-
|
|
379
|
-
/*
|
|
380
|
-
* CLI
|
|
381
|
-
*/
|
|
382
|
-
|
|
383
|
-
const WrapAsOption = Options.text("wrap").pipe(
|
|
384
|
-
Options.withAlias("w"),
|
|
385
|
-
Options.optional,
|
|
386
|
-
Options.withDescription(
|
|
387
|
-
"Wrap child bash command: the lifetime of the CLI command will be tied to the child process"
|
|
388
|
-
)
|
|
389
|
-
)
|
|
390
449
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
.pipe(
|
|
397
|
-
Args.atLeast(1),
|
|
398
|
-
Args.optional,
|
|
399
|
-
Args.withDescription(
|
|
400
|
-
"Wrap child bash command: the lifetime of the CLI command will be tied to the child process"
|
|
450
|
+
const EffectAppLibsPath = Argument
|
|
451
|
+
.directory("effect-app-libs-path", { mustExist: true })
|
|
452
|
+
.pipe(
|
|
453
|
+
Argument.withDefault("../../effect-app/libs"),
|
|
454
|
+
Argument.withDescription("Path to the effect-app-libs directory")
|
|
401
455
|
)
|
|
402
|
-
)
|
|
403
456
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
457
|
+
const link = Command
|
|
458
|
+
.make(
|
|
459
|
+
"link",
|
|
460
|
+
{ effectAppLibsPath: EffectAppLibsPath },
|
|
461
|
+
Effect.fn("effa-cli.link")(function*({ effectAppLibsPath }) {
|
|
462
|
+
return yield* linkPackages(effectAppLibsPath)
|
|
463
|
+
})
|
|
464
|
+
)
|
|
465
|
+
.pipe(Command.withDescription("Link local effect-app packages using file resolutions"))
|
|
466
|
+
|
|
467
|
+
const unlink = Command
|
|
468
|
+
.make(
|
|
469
|
+
"unlink",
|
|
470
|
+
{},
|
|
471
|
+
Effect.fn("effa-cli.unlink")(function*({}) {
|
|
472
|
+
return yield* unlinkPackages
|
|
473
|
+
})
|
|
474
|
+
)
|
|
475
|
+
.pipe(Command.withDescription("Remove effect-app file resolutions and restore npm registry packages"))
|
|
476
|
+
|
|
477
|
+
const ue = Command
|
|
478
|
+
.make(
|
|
479
|
+
"ue",
|
|
480
|
+
{},
|
|
481
|
+
Effect.fn("effa-cli.ue")(function*({}) {
|
|
482
|
+
yield* Effect.logInfo("Update effect-app and/or effect packages")
|
|
483
|
+
|
|
484
|
+
const prompted = yield* Prompt.select({
|
|
485
|
+
choices: [
|
|
486
|
+
{
|
|
487
|
+
title: "effect-app",
|
|
488
|
+
description: "Update only effect-app packages",
|
|
489
|
+
value: "effect-app"
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
title: "effect",
|
|
493
|
+
description: "Update only effect packages",
|
|
494
|
+
value: "effect"
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
title: "both",
|
|
498
|
+
description: "Update both effect-app and effect packages",
|
|
499
|
+
value: "both"
|
|
500
|
+
}
|
|
501
|
+
],
|
|
502
|
+
message: "Select an option"
|
|
503
|
+
})
|
|
438
504
|
|
|
439
|
-
|
|
505
|
+
switch (prompted) {
|
|
506
|
+
case "effect-app":
|
|
507
|
+
return yield* updateEffectAppPackages.pipe(
|
|
508
|
+
Effect.andThen(runGetExitCode("pnpm i"))
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
case "effect":
|
|
512
|
+
return yield* updateEffectPackages.pipe(
|
|
513
|
+
Effect.andThen(runGetExitCode("pnpm i"))
|
|
514
|
+
)
|
|
515
|
+
case "both":
|
|
516
|
+
return yield* updateEffectPackages.pipe(
|
|
517
|
+
Effect.andThen(updateEffectAppPackages),
|
|
518
|
+
Effect.andThen(runGetExitCode("pnpm i"))
|
|
519
|
+
)
|
|
520
|
+
}
|
|
521
|
+
})
|
|
522
|
+
)
|
|
523
|
+
.pipe(Command.withDescription("Update effect-app and/or effect packages"))
|
|
440
524
|
|
|
441
|
-
|
|
525
|
+
const up = Command
|
|
526
|
+
.make(
|
|
527
|
+
"up",
|
|
528
|
+
{},
|
|
529
|
+
Effect.fn("effa-cli.update-packages")(function*({}) {
|
|
530
|
+
yield* Effect.logInfo("Updating all packages except Effect/Effect-App ecosystem packages...")
|
|
442
531
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
532
|
+
return yield* updatePackages.pipe(
|
|
533
|
+
Effect.andThen(runGetExitCode("pnpm i"))
|
|
534
|
+
)
|
|
535
|
+
})
|
|
536
|
+
)
|
|
537
|
+
.pipe(Command.withDescription("Update all packages except Effect/Effect-App ecosystem packages"))
|
|
447
538
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
539
|
+
const indexMulti = makeCommandWithWrap(
|
|
540
|
+
"index-multi",
|
|
541
|
+
{},
|
|
542
|
+
Effect.fn("effa-cli.index-multi")(function*({}) {
|
|
543
|
+
yield* Effect.logInfo("Starting multi-index monitoring")
|
|
544
|
+
|
|
545
|
+
const dirs = ["./api/src"]
|
|
546
|
+
|
|
547
|
+
const existingDirs: string[] = []
|
|
548
|
+
for (const dir of dirs) {
|
|
549
|
+
const dirExists = yield* fs.exists(dir)
|
|
550
|
+
if (dirExists) {
|
|
551
|
+
existingDirs.push(dir)
|
|
552
|
+
} else {
|
|
553
|
+
yield* Effect.logWarning(`Directory ${dir} does not exist - skipping`)
|
|
452
554
|
}
|
|
453
555
|
}
|
|
454
556
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
const EffectAppLibsPath = Args
|
|
460
|
-
.directory({
|
|
461
|
-
exists: "yes",
|
|
462
|
-
name: "effect-app-libs-path"
|
|
463
|
-
})
|
|
464
|
-
.pipe(
|
|
465
|
-
Args.withDefault("../../effect-app/libs"),
|
|
466
|
-
Args.withDescription("Path to the effect-app-libs directory")
|
|
467
|
-
)
|
|
468
|
-
|
|
469
|
-
const link = Command
|
|
470
|
-
.make(
|
|
471
|
-
"link",
|
|
472
|
-
{ effectAppLibsPath: EffectAppLibsPath },
|
|
473
|
-
Effect.fn("effa-cli.link")(function*({ effectAppLibsPath }) {
|
|
474
|
-
return yield* linkPackages(effectAppLibsPath)
|
|
475
|
-
})
|
|
557
|
+
const monitors = existingDirs.map((dir) => monitorIndexes(dir))
|
|
558
|
+
yield* Effect.all(monitors, { concurrency: monitors.length })
|
|
559
|
+
}),
|
|
560
|
+
"Stopped multi-index monitoring"
|
|
476
561
|
)
|
|
477
|
-
|
|
562
|
+
.pipe(
|
|
563
|
+
Command.withDescription(
|
|
564
|
+
"Monitor multiple directories for index and controller file changes"
|
|
565
|
+
)
|
|
566
|
+
)
|
|
478
567
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
"unlink",
|
|
568
|
+
const packagejson = makeCommandWithWrap(
|
|
569
|
+
"packagejson",
|
|
482
570
|
{},
|
|
483
|
-
Effect.fn("effa-cli.
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
)
|
|
487
|
-
.pipe(Command.withDescription("Remove effect-app file resolutions and restore npm registry packages"))
|
|
571
|
+
Effect.fn("effa-cli.packagejson")(function*({}) {
|
|
572
|
+
// https://nodejs.org/api/path.html#pathresolvepaths
|
|
573
|
+
const startDir = path.resolve()
|
|
488
574
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
"
|
|
492
|
-
{},
|
|
493
|
-
Effect.fn("effa-cli.ue")(function*({}) {
|
|
494
|
-
yield* Effect.logInfo("Update effect-app and/or effect packages")
|
|
495
|
-
|
|
496
|
-
const prompted = yield* Prompt.select({
|
|
497
|
-
choices: [
|
|
498
|
-
{
|
|
499
|
-
title: "effect-app",
|
|
500
|
-
description: "Update only effect-app packages",
|
|
501
|
-
value: "effect-app"
|
|
502
|
-
},
|
|
503
|
-
{
|
|
504
|
-
title: "effect",
|
|
505
|
-
description: "Update only effect packages",
|
|
506
|
-
value: "effect"
|
|
507
|
-
},
|
|
508
|
-
{
|
|
509
|
-
title: "both",
|
|
510
|
-
description: "Update both effect-app and effect packages",
|
|
511
|
-
value: "both"
|
|
512
|
-
}
|
|
513
|
-
],
|
|
514
|
-
message: "Select an option"
|
|
515
|
-
})
|
|
516
|
-
|
|
517
|
-
switch (prompted) {
|
|
518
|
-
case "effect-app":
|
|
519
|
-
return yield* updateEffectAppPackages.pipe(
|
|
520
|
-
Effect.andThen(runGetExitCode("pnpm i"))
|
|
521
|
-
)
|
|
522
|
-
|
|
523
|
-
case "effect":
|
|
524
|
-
return yield* updateEffectPackages.pipe(
|
|
525
|
-
Effect.andThen(runGetExitCode("pnpm i"))
|
|
526
|
-
)
|
|
527
|
-
case "both":
|
|
528
|
-
return yield* updateEffectPackages.pipe(
|
|
529
|
-
Effect.andThen(updateEffectAppPackages),
|
|
530
|
-
Effect.andThen(runGetExitCode("pnpm i"))
|
|
531
|
-
)
|
|
532
|
-
}
|
|
533
|
-
})
|
|
575
|
+
return yield* monitorPackageJson(startDir, ".")
|
|
576
|
+
}),
|
|
577
|
+
"Stopped monitoring root package.json exports"
|
|
534
578
|
)
|
|
535
|
-
|
|
579
|
+
.pipe(
|
|
580
|
+
Command.withDescription("Generate and update root-level package.json exports mappings for TypeScript modules")
|
|
581
|
+
)
|
|
536
582
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
"up",
|
|
583
|
+
const packagejsonPackages = makeCommandWithWrap(
|
|
584
|
+
"packagejson-packages",
|
|
540
585
|
{},
|
|
541
|
-
Effect.fn("effa-cli.
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
return yield* updatePackages.pipe(
|
|
545
|
-
Effect.andThen(runGetExitCode("pnpm i"))
|
|
546
|
-
)
|
|
547
|
-
})
|
|
548
|
-
)
|
|
549
|
-
.pipe(Command.withDescription("Update all packages except Effect/Effect-App ecosystem packages"))
|
|
586
|
+
Effect.fn("effa-cli.packagejson-packages")(function*({}) {
|
|
587
|
+
// https://nodejs.org/api/path.html#pathresolvepaths
|
|
588
|
+
const startDir = path.resolve()
|
|
550
589
|
|
|
551
|
-
|
|
552
|
-
"index-multi",
|
|
553
|
-
{},
|
|
554
|
-
Effect.fn("effa-cli.index-multi")(function*({}) {
|
|
555
|
-
yield* Effect.logInfo("Starting multi-index monitoring")
|
|
590
|
+
const packagesDir = path.join(startDir, "packages")
|
|
556
591
|
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
for (const dir of dirs) {
|
|
561
|
-
const dirExists = yield* fs.exists(dir)
|
|
562
|
-
if (dirExists) {
|
|
563
|
-
existingDirs.push(dir)
|
|
564
|
-
} else {
|
|
565
|
-
yield* Effect.logWarning(`Directory ${dir} does not exist - skipping`)
|
|
592
|
+
const packagesExists = yield* fs.exists(packagesDir)
|
|
593
|
+
if (!packagesExists) {
|
|
594
|
+
return yield* Effect.logWarning("No packages directory found")
|
|
566
595
|
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
const monitors = existingDirs.map((dir) => monitorIndexes(dir))
|
|
570
|
-
yield* Effect.all(monitors, { concurrency: monitors.length })
|
|
571
|
-
}),
|
|
572
|
-
"Stopped multi-index monitoring"
|
|
573
|
-
)
|
|
574
|
-
.pipe(
|
|
575
|
-
Command.withDescription(
|
|
576
|
-
"Monitor multiple directories for index and controller file changes"
|
|
577
|
-
)
|
|
578
|
-
)
|
|
579
596
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
{},
|
|
583
|
-
Effect.fn("effa-cli.packagejson")(function*({}) {
|
|
584
|
-
// https://nodejs.org/api/path.html#pathresolvepaths
|
|
585
|
-
const startDir = path.resolve()
|
|
597
|
+
// get all package directories
|
|
598
|
+
const packageDirs = yield* fs.readDirectory(packagesDir)
|
|
586
599
|
|
|
587
|
-
|
|
588
|
-
}),
|
|
589
|
-
"Stopped monitoring root package.json exports"
|
|
590
|
-
)
|
|
591
|
-
.pipe(
|
|
592
|
-
Command.withDescription("Generate and update root-level package.json exports mappings for TypeScript modules")
|
|
593
|
-
)
|
|
600
|
+
const validPackages: string[] = []
|
|
594
601
|
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const startDir = path.resolve()
|
|
602
|
+
// filter packages that have package.json and src directory
|
|
603
|
+
for (const packageName of packageDirs) {
|
|
604
|
+
const packagePath = path.join(packagesDir, packageName)
|
|
605
|
+
const packageJsonExists = yield* fs.exists(path.join(packagePath, "package.json"))
|
|
606
|
+
const srcExists = yield* fs.exists(path.join(packagePath, "src"))
|
|
601
607
|
|
|
602
|
-
|
|
608
|
+
const shouldExclude = false
|
|
609
|
+
|| packageName.endsWith("eslint-codegen-model")
|
|
610
|
+
|| packageName.endsWith("eslint-shared-config")
|
|
611
|
+
|| packageName.endsWith("vue-components")
|
|
603
612
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// get all package directories
|
|
610
|
-
const packageDirs = yield* fs.readDirectory(packagesDir)
|
|
611
|
-
|
|
612
|
-
const validPackages: string[] = []
|
|
613
|
-
|
|
614
|
-
// filter packages that have package.json and src directory
|
|
615
|
-
for (const packageName of packageDirs) {
|
|
616
|
-
const packagePath = path.join(packagesDir, packageName)
|
|
617
|
-
const packageJsonExists = yield* fs.exists(path.join(packagePath, "package.json"))
|
|
618
|
-
const srcExists = yield* fs.exists(path.join(packagePath, "src"))
|
|
619
|
-
|
|
620
|
-
const shouldExclude = false
|
|
621
|
-
|| packageName.endsWith("eslint-codegen-model")
|
|
622
|
-
|| packageName.endsWith("eslint-shared-config")
|
|
623
|
-
|| packageName.endsWith("vue-components")
|
|
624
|
-
|
|
625
|
-
if (packageJsonExists && srcExists && !shouldExclude) {
|
|
626
|
-
validPackages.push(packagePath)
|
|
613
|
+
if (packageJsonExists && srcExists && !shouldExclude) {
|
|
614
|
+
validPackages.push(packagePath)
|
|
615
|
+
}
|
|
627
616
|
}
|
|
628
|
-
}
|
|
629
617
|
|
|
630
|
-
|
|
618
|
+
yield* Effect.logInfo(`Found ${validPackages.length} packages to update`)
|
|
631
619
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
620
|
+
// update each package sequentially
|
|
621
|
+
yield* Effect.all(
|
|
622
|
+
validPackages.map(
|
|
623
|
+
Effect.fnUntraced(function*(packagePath) {
|
|
624
|
+
const relativePackagePath = path.relative(startDir, packagePath)
|
|
625
|
+
yield* Effect.logInfo(`Updating ${relativePackagePath}`)
|
|
626
|
+
return yield* monitorPackageJson(startDir, relativePackagePath)
|
|
627
|
+
})
|
|
628
|
+
)
|
|
640
629
|
)
|
|
641
|
-
)
|
|
642
630
|
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
)
|
|
647
|
-
.pipe(
|
|
648
|
-
Command.withDescription("Generate and update package.json exports mappings for all packages in monorepo")
|
|
649
|
-
)
|
|
650
|
-
|
|
651
|
-
const gist = Command
|
|
652
|
-
.make(
|
|
653
|
-
"gist",
|
|
654
|
-
{
|
|
655
|
-
config: Options.file("config").pipe(
|
|
656
|
-
Options.withDefault("gists.yaml"),
|
|
657
|
-
Options.withDescription("Path to YAML configuration file")
|
|
658
|
-
)
|
|
659
|
-
},
|
|
660
|
-
Effect.fn("effa-cli.gist")(function*({ config }) {
|
|
661
|
-
return yield* GistHandler.handler({
|
|
662
|
-
YAMLPath: config
|
|
663
|
-
})
|
|
664
|
-
})
|
|
631
|
+
yield* Effect.logInfo("All packages updated successfully")
|
|
632
|
+
}),
|
|
633
|
+
"Stopped monitoring package.json exports for all packages"
|
|
665
634
|
)
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
.make(
|
|
670
|
-
"nuke",
|
|
671
|
-
{
|
|
672
|
-
dryRun: Options.boolean("dry-run").pipe(
|
|
673
|
-
Options.withDescription("Show what would be done without making changes")
|
|
674
|
-
),
|
|
675
|
-
storePrune: Options.boolean("store-prune").pipe(
|
|
676
|
-
Options.withDescription("Prune the package manager store")
|
|
677
|
-
)
|
|
678
|
-
},
|
|
679
|
-
Effect.fn("effa-cli.nuke")(function*({ dryRun, storePrune }) {
|
|
680
|
-
yield* Effect.logInfo(dryRun ? "Performing dry run cleanup..." : "Performing nuclear cleanup...")
|
|
635
|
+
.pipe(
|
|
636
|
+
Command.withDescription("Generate and update package.json exports mappings for all packages in monorepo")
|
|
637
|
+
)
|
|
681
638
|
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
639
|
+
const gist = Command
|
|
640
|
+
.make(
|
|
641
|
+
"gist",
|
|
642
|
+
{
|
|
643
|
+
config: Flag.file("config").pipe(
|
|
644
|
+
Flag.withDefault("gists.yaml"),
|
|
645
|
+
Flag.withDescription("Path to YAML configuration file")
|
|
685
646
|
)
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
|
|
647
|
+
},
|
|
648
|
+
Effect.fn("effa-cli.gist")(function*({ config }) {
|
|
649
|
+
const gh = yield* GistHandler
|
|
650
|
+
return yield* gh.handler({
|
|
651
|
+
YAMLPath: config
|
|
652
|
+
})
|
|
653
|
+
})
|
|
654
|
+
)
|
|
655
|
+
.pipe(Command.withDescription("Create GitHub gists from files specified in YAML configuration"))
|
|
656
|
+
|
|
657
|
+
const nuke = Command
|
|
658
|
+
.make(
|
|
659
|
+
"nuke",
|
|
660
|
+
{
|
|
661
|
+
dryRun: Flag.boolean("dry-run").pipe(
|
|
662
|
+
Flag.withDescription("Show what would be done without making changes")
|
|
663
|
+
),
|
|
664
|
+
storePrune: Flag.boolean("store-prune").pipe(
|
|
665
|
+
Flag.withDescription("Prune the package manager store")
|
|
689
666
|
)
|
|
667
|
+
},
|
|
668
|
+
Effect.fn("effa-cli.nuke")(function*({ dryRun, storePrune }) {
|
|
669
|
+
yield* Effect.logInfo(dryRun ? "Performing dry run cleanup..." : "Performing nuclear cleanup...")
|
|
690
670
|
|
|
691
|
-
if (
|
|
671
|
+
if (dryRun) {
|
|
692
672
|
yield* runGetExitCode(
|
|
693
|
-
"
|
|
673
|
+
"find . -depth \\( -type d \\( -name 'node_modules' -o -name '.nuxt' -o -name 'dist' -o -name '.output' -o -name '.nitro' -o -name '.cache' -o -name 'test-results' -o -name 'test-out' -o -name 'coverage' \\) -print \\) -o \\( -type f \\( -name '*.log' -o -name '*.tsbuildinfo' \\) -print \\)"
|
|
694
674
|
)
|
|
675
|
+
} else {
|
|
676
|
+
yield* runGetExitCode(
|
|
677
|
+
"find . -depth \\( -type d \\( -name 'node_modules' -o -name '.nuxt' -o -name 'dist' -o -name '.output' -o -name '.nitro' -o -name '.cache' -o -name 'test-results' -o -name 'test-out' -o -name 'coverage' \\) -exec rm -rf -- {} + \\) -o \\( -type f \\( -name '*.log' -o -name '*.tsbuildinfo' \\) -delete \\)"
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
if (storePrune) {
|
|
681
|
+
yield* runGetExitCode(
|
|
682
|
+
"pnpm store prune"
|
|
683
|
+
)
|
|
684
|
+
}
|
|
695
685
|
}
|
|
696
|
-
}
|
|
697
686
|
|
|
698
|
-
|
|
699
|
-
|
|
687
|
+
yield* Effect.logInfo("Cleanup operation completed")
|
|
688
|
+
})
|
|
689
|
+
)
|
|
690
|
+
.pipe(Command.withDescription("Nuclear cleanup command: removes all generated files and cleans the workspace"))
|
|
691
|
+
|
|
692
|
+
// configure CLI
|
|
693
|
+
return yield* Command.run(
|
|
694
|
+
Command
|
|
695
|
+
.make("effa")
|
|
696
|
+
.pipe(Command.withSubcommands([
|
|
697
|
+
ue,
|
|
698
|
+
up,
|
|
699
|
+
link,
|
|
700
|
+
unlink,
|
|
701
|
+
indexMulti,
|
|
702
|
+
packagejson,
|
|
703
|
+
packagejsonPackages,
|
|
704
|
+
gist,
|
|
705
|
+
nuke
|
|
706
|
+
])),
|
|
707
|
+
{
|
|
708
|
+
version: "v1.0.0"
|
|
709
|
+
}
|
|
700
710
|
)
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
indexMulti,
|
|
713
|
-
packagejson,
|
|
714
|
-
packagejsonPackages,
|
|
715
|
-
gist,
|
|
716
|
-
nuke
|
|
717
|
-
])),
|
|
718
|
-
{
|
|
719
|
-
name: "Effect-App CLI by jfet97 ❤️",
|
|
720
|
-
version: "v1.0.0"
|
|
721
|
-
}
|
|
722
|
-
)
|
|
723
|
-
|
|
724
|
-
return yield* cli(process.argv)
|
|
725
|
-
})()
|
|
726
|
-
.pipe(
|
|
727
|
-
Effect.scoped,
|
|
728
|
-
Effect.provide(
|
|
729
|
-
Layer.provideMerge(
|
|
730
|
-
Layer.merge(
|
|
731
|
-
GistHandler.Default,
|
|
732
|
-
RunCommandService.Default
|
|
733
|
-
),
|
|
734
|
-
NodeContext.layer
|
|
711
|
+
})()
|
|
712
|
+
.pipe(
|
|
713
|
+
Effect.scoped,
|
|
714
|
+
Effect.provide(
|
|
715
|
+
Layer.provideMerge(
|
|
716
|
+
Layer.merge(
|
|
717
|
+
GistHandler.Default,
|
|
718
|
+
RunCommandService.Default
|
|
719
|
+
),
|
|
720
|
+
NodeServices.layer
|
|
721
|
+
)
|
|
735
722
|
)
|
|
736
|
-
)
|
|
737
|
-
|
|
738
|
-
)
|
|
723
|
+
)
|
|
724
|
+
)
|