jsrepo 1.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/LICENSE +21 -0
- package/README.md +5 -0
- package/bin.mjs +2 -0
- package/dist/index.js +2179 -0
- package/package.json +70 -0
- package/pnpm-lock.yaml +3185 -0
- package/schema.json +28 -0
- package/src/blocks/types/result.ts +736 -0
- package/src/blocks/utilities/array-sum.ts +31 -0
- package/src/blocks/utilities/lines.ts +73 -0
- package/src/blocks/utilities/map-to-array.ts +32 -0
- package/src/blocks/utilities/pad.ts +85 -0
- package/src/blocks/utilities/strip-ansi.ts +25 -0
- package/src/commands/add.ts +495 -0
- package/src/commands/build.ts +79 -0
- package/src/commands/diff.ts +218 -0
- package/src/commands/index.ts +7 -0
- package/src/commands/init.ts +117 -0
- package/src/commands/test.ts +369 -0
- package/src/config/index.ts +31 -0
- package/src/index.ts +40 -0
- package/src/utils/build.ts +189 -0
- package/src/utils/context.ts +19 -0
- package/src/utils/diff.ts +184 -0
- package/src/utils/get-installed-blocks.ts +38 -0
- package/src/utils/get-watermark.ts +7 -0
- package/src/utils/git-providers.ts +140 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/language-support.ts +198 -0
- package/src/utils/package.ts +16 -0
- package/src/utils/prompts.ts +72 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2179 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import fs11 from "node:fs";
|
|
3
|
+
import path9 from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { program as program5 } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/commands/add.ts
|
|
8
|
+
import fs6 from "node:fs";
|
|
9
|
+
import path5 from "node:path";
|
|
10
|
+
import { cancel, confirm, isCancel, multiselect, outro, spinner as spinner2 } from "@clack/prompts";
|
|
11
|
+
import color4 from "chalk";
|
|
12
|
+
import { Command, program as program2 } from "commander";
|
|
13
|
+
import { execa } from "execa";
|
|
14
|
+
import { resolveCommand } from "package-manager-detector/commands";
|
|
15
|
+
import { detect } from "package-manager-detector/detect";
|
|
16
|
+
import * as v4 from "valibot";
|
|
17
|
+
|
|
18
|
+
// src/blocks/utilities/map-to-array.ts
|
|
19
|
+
var mapToArray = (map, fn) => {
|
|
20
|
+
const items = [];
|
|
21
|
+
for (const [key, value] of map) {
|
|
22
|
+
items.push(fn(key, value));
|
|
23
|
+
}
|
|
24
|
+
return items;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/config/index.ts
|
|
28
|
+
import fs from "node:fs";
|
|
29
|
+
import * as v from "valibot";
|
|
30
|
+
|
|
31
|
+
// src/blocks/types/result.ts
|
|
32
|
+
var Result = class {
|
|
33
|
+
_result;
|
|
34
|
+
constructor(result) {
|
|
35
|
+
this._result = result;
|
|
36
|
+
}
|
|
37
|
+
/** Allows you to run callbacks based on the result.
|
|
38
|
+
*
|
|
39
|
+
* @param success callback to be run when result is success
|
|
40
|
+
* @param failure callback to be run when result is failure
|
|
41
|
+
* @returns
|
|
42
|
+
*
|
|
43
|
+
* ## Usage
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* result.match(
|
|
47
|
+
* (val) => val,
|
|
48
|
+
* () => {
|
|
49
|
+
* throw new Error('oops!')
|
|
50
|
+
* }
|
|
51
|
+
* );
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* ## Examples
|
|
55
|
+
*
|
|
56
|
+
* ```ts
|
|
57
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello, World!");
|
|
58
|
+
*
|
|
59
|
+
* const result = functionThatMightFail();
|
|
60
|
+
*
|
|
61
|
+
* const val = result.match(
|
|
62
|
+
* (val) => val,
|
|
63
|
+
* () => {
|
|
64
|
+
* throw new Error('oops!')
|
|
65
|
+
* }
|
|
66
|
+
* );
|
|
67
|
+
*
|
|
68
|
+
* console.log(val); // "Hello, World!"
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
match(success, failure) {
|
|
72
|
+
if (!this._result.ok) {
|
|
73
|
+
return failure(this._result.err);
|
|
74
|
+
}
|
|
75
|
+
return success(this._result.val);
|
|
76
|
+
}
|
|
77
|
+
/** Maps `Result<T, E>` to `Result<A, E>` using the passed mapping function
|
|
78
|
+
*
|
|
79
|
+
* @param fn Mapping function
|
|
80
|
+
* @returns
|
|
81
|
+
*
|
|
82
|
+
* ## Usage
|
|
83
|
+
*
|
|
84
|
+
* ```ts
|
|
85
|
+
* result.map((val) => val.length);
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* ## Examples
|
|
89
|
+
*
|
|
90
|
+
* ```ts
|
|
91
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello, World!");
|
|
92
|
+
*
|
|
93
|
+
* const result = functionThatMightFail();
|
|
94
|
+
*
|
|
95
|
+
* const hello = result.map((val) => val.slice(0, 5));
|
|
96
|
+
*
|
|
97
|
+
* console.log(hello.unwrap()); // "Hello"
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
map(fn) {
|
|
101
|
+
return this.match(
|
|
102
|
+
(val) => Ok(fn(val)),
|
|
103
|
+
(err) => Err(err)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
/** In the `Ok` case returns the mapped value using the function else returns `defaultVal`
|
|
107
|
+
*
|
|
108
|
+
* @param defaultVal Value to be returned when `Err`
|
|
109
|
+
* @param fn Mapping function to map in case of `Ok`
|
|
110
|
+
* @returns
|
|
111
|
+
*
|
|
112
|
+
* ## Usage
|
|
113
|
+
*
|
|
114
|
+
* ```ts
|
|
115
|
+
* result.mapOr(1, (val) => val.length);
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* ## Examples
|
|
119
|
+
*
|
|
120
|
+
* ### When `Ok`
|
|
121
|
+
*
|
|
122
|
+
* ```ts
|
|
123
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("foo");
|
|
124
|
+
*
|
|
125
|
+
* const result = functionThatMightFail();
|
|
126
|
+
*
|
|
127
|
+
* const length = result.mapOr(1, (val) => val.length);
|
|
128
|
+
*
|
|
129
|
+
* console.log(length); // 3
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* ### When `Err`
|
|
133
|
+
*
|
|
134
|
+
* ```ts
|
|
135
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
136
|
+
*
|
|
137
|
+
* const result = functionThatMightFail();
|
|
138
|
+
*
|
|
139
|
+
* const length = result.mapOr(1, (val) => val.length);
|
|
140
|
+
*
|
|
141
|
+
* console.log(length); // 1
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
mapOr(defaultVal, fn) {
|
|
145
|
+
return this.match(
|
|
146
|
+
(val) => fn(val),
|
|
147
|
+
(_) => defaultVal
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
/** In the `Ok` case returns the mapped value using `fn` else returns value of `def`
|
|
151
|
+
*
|
|
152
|
+
* @param def Mapping function called when `Err`
|
|
153
|
+
* @param fn Mapping function called when `Ok`
|
|
154
|
+
* @returns
|
|
155
|
+
*
|
|
156
|
+
* ## Usage
|
|
157
|
+
*
|
|
158
|
+
* ```ts
|
|
159
|
+
* result.mapOrElse(() => 1, (val) => val.length);
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
* ## Examples
|
|
163
|
+
*
|
|
164
|
+
* ### When `Ok`
|
|
165
|
+
*
|
|
166
|
+
* ```ts
|
|
167
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("foo");
|
|
168
|
+
*
|
|
169
|
+
* const result = functionThatMightFail();
|
|
170
|
+
*
|
|
171
|
+
* const length = result.mapOrElse(() => 1, (val) => val.length);
|
|
172
|
+
*
|
|
173
|
+
* console.log(length); // 3
|
|
174
|
+
* ```
|
|
175
|
+
*
|
|
176
|
+
* ### When `Err`
|
|
177
|
+
*
|
|
178
|
+
* ```ts
|
|
179
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
180
|
+
*
|
|
181
|
+
* const result = functionThatMightFail();
|
|
182
|
+
*
|
|
183
|
+
* const length = result.mapOr(() => 1, (val) => val.length);
|
|
184
|
+
*
|
|
185
|
+
* console.log(length); // 1
|
|
186
|
+
* ```
|
|
187
|
+
*/
|
|
188
|
+
mapOrElse(def, fn) {
|
|
189
|
+
return this.match(
|
|
190
|
+
(val) => fn(val),
|
|
191
|
+
(err) => def(err)
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
/** Maps `Result<T, E>` to `Result<T, A>` using the passed mapping function
|
|
195
|
+
*
|
|
196
|
+
* @param fn Mapping function
|
|
197
|
+
* @returns
|
|
198
|
+
*
|
|
199
|
+
* ## Usage
|
|
200
|
+
*
|
|
201
|
+
* ```ts
|
|
202
|
+
* result.mapErr((err) => getCodeMsg(err));
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* ## Examples
|
|
206
|
+
*
|
|
207
|
+
* ```ts
|
|
208
|
+
* const functionThatMightFail = (): Result<string, string> => Err(10);
|
|
209
|
+
*
|
|
210
|
+
* const result = functionThatMightFail();
|
|
211
|
+
*
|
|
212
|
+
* const message = result.mapErr(() => "Error");
|
|
213
|
+
*
|
|
214
|
+
* console.log(message); // "Error"
|
|
215
|
+
* ```
|
|
216
|
+
*/
|
|
217
|
+
mapErr(fn) {
|
|
218
|
+
return this.match(
|
|
219
|
+
(val) => Ok(val),
|
|
220
|
+
(err) => Err(fn(err))
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
/** In the `Err` case returns the mapped value using the function else returns `defaultVal`
|
|
224
|
+
*
|
|
225
|
+
* @param defaultVal Value to be returned when `Ok`
|
|
226
|
+
* @param fn Mapping function to map in case of `Err`
|
|
227
|
+
* @returns
|
|
228
|
+
*
|
|
229
|
+
* ## Usage
|
|
230
|
+
*
|
|
231
|
+
* ```ts
|
|
232
|
+
* result.mapErrOr("Should've been error", (err) => getCodeMsg(err));
|
|
233
|
+
* ```
|
|
234
|
+
*
|
|
235
|
+
* ## Examples
|
|
236
|
+
*
|
|
237
|
+
* ### When `Ok`
|
|
238
|
+
*
|
|
239
|
+
* ```ts
|
|
240
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("foo");
|
|
241
|
+
*
|
|
242
|
+
* const result = functionThatMightFail();
|
|
243
|
+
*
|
|
244
|
+
* const message = result.mapErrOr("Should've been error", () => "Error");
|
|
245
|
+
*
|
|
246
|
+
* console.log(message); // "Should've been error"
|
|
247
|
+
* ```
|
|
248
|
+
*
|
|
249
|
+
* ### When `Err`
|
|
250
|
+
*
|
|
251
|
+
* ```ts
|
|
252
|
+
* const functionThatMightFail = (): Result<string, string> => Err(10);
|
|
253
|
+
*
|
|
254
|
+
* const result = functionThatMightFail();
|
|
255
|
+
*
|
|
256
|
+
* const message = result.mapErrOr("Should've been error", () => "Error");
|
|
257
|
+
*
|
|
258
|
+
* console.log(message); // "Error"
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
mapErrOr(defaultVal, fn) {
|
|
262
|
+
return this.match(
|
|
263
|
+
(_) => defaultVal,
|
|
264
|
+
(err) => fn(err)
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
/** In the `Err` case returns the mapped value using the function else returns value of `def`
|
|
268
|
+
*
|
|
269
|
+
* @param def Mapping function called when `Ok`
|
|
270
|
+
* @param fn Mapping function called when `Err`
|
|
271
|
+
* @returns
|
|
272
|
+
*
|
|
273
|
+
* ## Usage
|
|
274
|
+
*
|
|
275
|
+
* ```ts
|
|
276
|
+
* result.mapErrOrElse(() => "Value", (_) => "Error!");
|
|
277
|
+
* ```
|
|
278
|
+
*
|
|
279
|
+
* ## Examples
|
|
280
|
+
*
|
|
281
|
+
* ### When `Ok`
|
|
282
|
+
*
|
|
283
|
+
* ```ts
|
|
284
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("foo");
|
|
285
|
+
*
|
|
286
|
+
* const result = functionThatMightFail();
|
|
287
|
+
*
|
|
288
|
+
* const length = result.mapErrOrElse(() => 1, (val) => val.length);
|
|
289
|
+
*
|
|
290
|
+
* console.log(length); // 1
|
|
291
|
+
* ```
|
|
292
|
+
*
|
|
293
|
+
* ### When `Err`
|
|
294
|
+
*
|
|
295
|
+
* ```ts
|
|
296
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
297
|
+
*
|
|
298
|
+
* const result = functionThatMightFail();
|
|
299
|
+
*
|
|
300
|
+
* const length = result.mapOr(() => 1, (val) => val.length);
|
|
301
|
+
*
|
|
302
|
+
* console.log(length); // 4
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
305
|
+
mapErrOrElse(def, fn) {
|
|
306
|
+
return this.match(
|
|
307
|
+
(val) => def(val),
|
|
308
|
+
(err) => fn(err)
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
/** Returns true if result is `Ok`
|
|
312
|
+
*
|
|
313
|
+
* @returns
|
|
314
|
+
*
|
|
315
|
+
* ## Usage
|
|
316
|
+
*
|
|
317
|
+
* ```ts
|
|
318
|
+
* result.isOk();
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
isOk() {
|
|
322
|
+
return this.match(
|
|
323
|
+
() => true,
|
|
324
|
+
() => false
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
/** Returns true if result is `Err`
|
|
328
|
+
*
|
|
329
|
+
* @returns
|
|
330
|
+
*
|
|
331
|
+
* ## Usage
|
|
332
|
+
*
|
|
333
|
+
* ```ts
|
|
334
|
+
* result.isErr();
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
isErr() {
|
|
338
|
+
return this.match(
|
|
339
|
+
() => false,
|
|
340
|
+
() => true
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
/** Tries to return value if value is `Err` throws generic error message.
|
|
344
|
+
*
|
|
345
|
+
* @returns
|
|
346
|
+
*
|
|
347
|
+
* ## Usage
|
|
348
|
+
*
|
|
349
|
+
* ```ts
|
|
350
|
+
* result.unwrap();
|
|
351
|
+
* ```
|
|
352
|
+
*
|
|
353
|
+
* ## Examples
|
|
354
|
+
*
|
|
355
|
+
* ### When `Ok`
|
|
356
|
+
*
|
|
357
|
+
* ```ts
|
|
358
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello!");
|
|
359
|
+
*
|
|
360
|
+
* const result = functionThatMightFail();
|
|
361
|
+
*
|
|
362
|
+
* console.log(result.unwrap()); // "Hello!"
|
|
363
|
+
* ```
|
|
364
|
+
*
|
|
365
|
+
* ### When `Err`
|
|
366
|
+
*
|
|
367
|
+
* ```ts
|
|
368
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
369
|
+
*
|
|
370
|
+
* const result = functionThatMightFail();
|
|
371
|
+
*
|
|
372
|
+
* result.unwrap(); // Error: Attempted to call `.unwrap()` on a non `Ok` value.
|
|
373
|
+
* ```
|
|
374
|
+
*/
|
|
375
|
+
unwrap() {
|
|
376
|
+
return this.match(
|
|
377
|
+
(val) => val,
|
|
378
|
+
() => {
|
|
379
|
+
throw new Error("Attempted to call `.unwrap()` on a non `Ok` value.");
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
/** Tries to return err if value is `Ok` throws generic error message.
|
|
384
|
+
*
|
|
385
|
+
* @returns
|
|
386
|
+
*
|
|
387
|
+
* ## Usage
|
|
388
|
+
*
|
|
389
|
+
* ```ts
|
|
390
|
+
* result.unwrapErr();
|
|
391
|
+
* ```
|
|
392
|
+
*
|
|
393
|
+
* ## Examples
|
|
394
|
+
*
|
|
395
|
+
* ### When `Ok`
|
|
396
|
+
*
|
|
397
|
+
* ```ts
|
|
398
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello!");
|
|
399
|
+
*
|
|
400
|
+
* const result = functionThatMightFail();
|
|
401
|
+
*
|
|
402
|
+
* result.unwrapErr(); // Error: Attempted to call `.unwrapErr()` on a non `Err` value.
|
|
403
|
+
* ```
|
|
404
|
+
*
|
|
405
|
+
* ### When `Err`
|
|
406
|
+
*
|
|
407
|
+
* ```ts
|
|
408
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
409
|
+
*
|
|
410
|
+
* const result = functionThatMightFail();
|
|
411
|
+
*
|
|
412
|
+
* console.log(result.unwrapErr()); // "oops!"
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
unwrapErr() {
|
|
416
|
+
return this.match(
|
|
417
|
+
() => {
|
|
418
|
+
throw new Error("Attempted to call `.unwrapErr()` on a non `Err` value.");
|
|
419
|
+
},
|
|
420
|
+
(err) => err
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
/** Tries to unwrap the value if value is `Err` returns `defaultVal`
|
|
424
|
+
*
|
|
425
|
+
* @param defaultVal Value to be returned if `Err`
|
|
426
|
+
* @returns
|
|
427
|
+
*
|
|
428
|
+
* ## Usage
|
|
429
|
+
*
|
|
430
|
+
* ```ts
|
|
431
|
+
* result.unwrapOr(7);
|
|
432
|
+
* ```
|
|
433
|
+
*
|
|
434
|
+
* ## Examples
|
|
435
|
+
*
|
|
436
|
+
* ### When `Ok`
|
|
437
|
+
*
|
|
438
|
+
* ```ts
|
|
439
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello!");
|
|
440
|
+
*
|
|
441
|
+
* const result = functionThatMightFail();
|
|
442
|
+
*
|
|
443
|
+
* console.log(result.unwrapOr("Yellow!")); // "Hello!"
|
|
444
|
+
* ```
|
|
445
|
+
*
|
|
446
|
+
* ### When `Err`
|
|
447
|
+
*
|
|
448
|
+
* ```ts
|
|
449
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
450
|
+
*
|
|
451
|
+
* const result = functionThatMightFail();
|
|
452
|
+
*
|
|
453
|
+
* console.log(result.unwrapOr("Yellow!")); // "Yellow!"
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
unwrapOr(defaultVal) {
|
|
457
|
+
return this.match(
|
|
458
|
+
(val) => val,
|
|
459
|
+
(_) => defaultVal
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
/** Tries to unwrap the error if vale is `Ok` returns `defaultVal`
|
|
463
|
+
*
|
|
464
|
+
* @param defaultVal
|
|
465
|
+
* @returns
|
|
466
|
+
*
|
|
467
|
+
* ## Usage
|
|
468
|
+
*
|
|
469
|
+
* ```ts
|
|
470
|
+
* result.unwrapErrOr("Error");
|
|
471
|
+
* ```
|
|
472
|
+
*
|
|
473
|
+
* ## Examples
|
|
474
|
+
*
|
|
475
|
+
* ### When `Ok`
|
|
476
|
+
*
|
|
477
|
+
* ```ts
|
|
478
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello!");
|
|
479
|
+
*
|
|
480
|
+
* const result = functionThatMightFail();
|
|
481
|
+
*
|
|
482
|
+
* console.log(result.unwrapErrOr("Yellow!")); // "Yellow!"
|
|
483
|
+
* ```
|
|
484
|
+
*
|
|
485
|
+
* ### When `Err`
|
|
486
|
+
*
|
|
487
|
+
* ```ts
|
|
488
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
489
|
+
*
|
|
490
|
+
* const result = functionThatMightFail();
|
|
491
|
+
*
|
|
492
|
+
* console.log(result.unwrapErrOr("Yellow!")); // "oops!"
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
unwrapErrOr(defaultVal) {
|
|
496
|
+
return this.match(
|
|
497
|
+
() => defaultVal,
|
|
498
|
+
(err) => err
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
/** Tries to return the value if value is `Err` calls `fn`
|
|
502
|
+
*
|
|
503
|
+
* @param fn Function called if `Err`
|
|
504
|
+
*
|
|
505
|
+
* ## Usage
|
|
506
|
+
*
|
|
507
|
+
* ```ts
|
|
508
|
+
* result.unwrapOrElse(() => "Hello!");
|
|
509
|
+
* ```
|
|
510
|
+
*
|
|
511
|
+
* ## Examples
|
|
512
|
+
*
|
|
513
|
+
* ### When `Ok`
|
|
514
|
+
*
|
|
515
|
+
* ```ts
|
|
516
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello!");
|
|
517
|
+
*
|
|
518
|
+
* const result = functionThatMightFail();
|
|
519
|
+
*
|
|
520
|
+
* console.log(result.unwrapOrElse(() => "oops!")); // "Hello!"
|
|
521
|
+
* ```
|
|
522
|
+
*
|
|
523
|
+
* ### When `Err`
|
|
524
|
+
*
|
|
525
|
+
* ```ts
|
|
526
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
527
|
+
*
|
|
528
|
+
* const result = functionThatMightFail();
|
|
529
|
+
*
|
|
530
|
+
* console.log(result.unwrapOrElse(() => "Hello!")); // "Hello!"
|
|
531
|
+
* ```
|
|
532
|
+
*
|
|
533
|
+
*/
|
|
534
|
+
unwrapOrElse(fn) {
|
|
535
|
+
return this.match(
|
|
536
|
+
(val) => val,
|
|
537
|
+
(err) => fn(err)
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
/** Tries to return the error if value is `Ok` calls `fn`
|
|
541
|
+
*
|
|
542
|
+
* @param fn Function called if `Ok`
|
|
543
|
+
*
|
|
544
|
+
* ## Usage
|
|
545
|
+
*
|
|
546
|
+
* ```ts
|
|
547
|
+
* result.unwrapErrOrElse(() => "Error!");
|
|
548
|
+
* ```
|
|
549
|
+
*
|
|
550
|
+
* ## Examples
|
|
551
|
+
*
|
|
552
|
+
* ### When `Ok`
|
|
553
|
+
*
|
|
554
|
+
* ```ts
|
|
555
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello!");
|
|
556
|
+
*
|
|
557
|
+
* const result = functionThatMightFail();
|
|
558
|
+
*
|
|
559
|
+
* console.log(result.unwrapErrOrElse(() => "oops!")); // "oops!"
|
|
560
|
+
* ```
|
|
561
|
+
*
|
|
562
|
+
* ### When `Err`
|
|
563
|
+
*
|
|
564
|
+
* ```ts
|
|
565
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
566
|
+
*
|
|
567
|
+
* const result = functionThatMightFail();
|
|
568
|
+
*
|
|
569
|
+
* console.log(result.unwrapErrOrElse(() => "Hello!")); // "oops!"
|
|
570
|
+
* ```
|
|
571
|
+
*
|
|
572
|
+
*/
|
|
573
|
+
unwrapErrOrElse(fn) {
|
|
574
|
+
return this.match(
|
|
575
|
+
(val) => fn(val),
|
|
576
|
+
(err) => err
|
|
577
|
+
);
|
|
578
|
+
}
|
|
579
|
+
/** Tries to return value if value is `Err` throws custom error message.
|
|
580
|
+
*
|
|
581
|
+
* @param message Message to show when value is `Err`
|
|
582
|
+
* @returns
|
|
583
|
+
*
|
|
584
|
+
* ## Usage
|
|
585
|
+
*
|
|
586
|
+
* ```ts
|
|
587
|
+
* result.expect("Custom message");
|
|
588
|
+
* ```
|
|
589
|
+
*
|
|
590
|
+
* ## Examples
|
|
591
|
+
*
|
|
592
|
+
* ### When `Ok`
|
|
593
|
+
*
|
|
594
|
+
* ```ts
|
|
595
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello!");
|
|
596
|
+
*
|
|
597
|
+
* const result = functionThatMightFail();
|
|
598
|
+
*
|
|
599
|
+
* console.log(result.expect("I failed!")); // "Hello!"
|
|
600
|
+
* ```
|
|
601
|
+
*
|
|
602
|
+
* ### When `Err`
|
|
603
|
+
*
|
|
604
|
+
* ```ts
|
|
605
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
606
|
+
*
|
|
607
|
+
* const result = functionThatMightFail();
|
|
608
|
+
*
|
|
609
|
+
* result.expect("I failed!"); // Error: I failed!
|
|
610
|
+
* ```
|
|
611
|
+
*/
|
|
612
|
+
expect(message) {
|
|
613
|
+
return this.match(
|
|
614
|
+
(val) => val,
|
|
615
|
+
() => {
|
|
616
|
+
throw new Error(message);
|
|
617
|
+
}
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
/** Tries to return error value if value is `Ok` throws custom error message
|
|
621
|
+
*
|
|
622
|
+
* @param message
|
|
623
|
+
* @returns
|
|
624
|
+
*
|
|
625
|
+
* ## Usage
|
|
626
|
+
*
|
|
627
|
+
* ```ts
|
|
628
|
+
* result.expectErr("Custom message");
|
|
629
|
+
* ```
|
|
630
|
+
*
|
|
631
|
+
* ## Examples
|
|
632
|
+
*
|
|
633
|
+
* ### When `Ok`
|
|
634
|
+
*
|
|
635
|
+
* ```ts
|
|
636
|
+
* const functionThatMightFail = (): Result<string, string> => Ok("Hello!");
|
|
637
|
+
*
|
|
638
|
+
* const result = functionThatMightFail();
|
|
639
|
+
*
|
|
640
|
+
* console.log(result.expectErr("I failed!")); // Error: I failed!
|
|
641
|
+
* ```
|
|
642
|
+
*
|
|
643
|
+
* ### When `Err`
|
|
644
|
+
*
|
|
645
|
+
* ```ts
|
|
646
|
+
* const functionThatMightFail = (): Result<string, string> => Err("oops!");
|
|
647
|
+
*
|
|
648
|
+
* const result = functionThatMightFail();
|
|
649
|
+
*
|
|
650
|
+
* console.log(result.expectErr("I failed!")); // "oops!"
|
|
651
|
+
* ```
|
|
652
|
+
*/
|
|
653
|
+
expectErr(message) {
|
|
654
|
+
return this.match(
|
|
655
|
+
() => {
|
|
656
|
+
throw new Error(message);
|
|
657
|
+
},
|
|
658
|
+
(err) => err
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
var Ok = (val) => {
|
|
663
|
+
return new Result({ ok: true, val });
|
|
664
|
+
};
|
|
665
|
+
var Err = (err) => {
|
|
666
|
+
return new Result({ ok: false, err });
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
// src/config/index.ts
|
|
670
|
+
var CONFIG_NAME = "blocks.json";
|
|
671
|
+
var schema = v.object({
|
|
672
|
+
$schema: v.string(),
|
|
673
|
+
repos: v.optional(v.array(v.string()), []),
|
|
674
|
+
includeTests: v.boolean(),
|
|
675
|
+
path: v.pipe(v.string(), v.minLength(1)),
|
|
676
|
+
watermark: v.optional(v.boolean(), true)
|
|
677
|
+
});
|
|
678
|
+
var getConfig = () => {
|
|
679
|
+
if (!fs.existsSync(CONFIG_NAME)) {
|
|
680
|
+
return Err("Could not find your configuration file! Please run `npx jsrepo init`.");
|
|
681
|
+
}
|
|
682
|
+
const config = v.safeParse(schema, JSON.parse(fs.readFileSync(CONFIG_NAME).toString()));
|
|
683
|
+
if (!config.success) {
|
|
684
|
+
return Err("There was an error reading your `blocks.json` file!");
|
|
685
|
+
}
|
|
686
|
+
return Ok(config.output);
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
// src/utils/build.ts
|
|
690
|
+
import fs4 from "node:fs";
|
|
691
|
+
import path3 from "node:path";
|
|
692
|
+
import color2 from "chalk";
|
|
693
|
+
import { program } from "commander";
|
|
694
|
+
import * as v2 from "valibot";
|
|
695
|
+
|
|
696
|
+
// src/utils/index.ts
|
|
697
|
+
import color from "chalk";
|
|
698
|
+
var OUTPUT_FILE = "blocks-manifest.json";
|
|
699
|
+
var WARN = color.bgRgb(245, 149, 66).white("WARN");
|
|
700
|
+
var INFO = color.bgBlueBright.white("INFO");
|
|
701
|
+
|
|
702
|
+
// src/utils/language-support.ts
|
|
703
|
+
import fs3 from "node:fs";
|
|
704
|
+
import { builtinModules } from "node:module";
|
|
705
|
+
import path2 from "node:path";
|
|
706
|
+
import { walk } from "estree-walker";
|
|
707
|
+
import * as sv from "svelte/compiler";
|
|
708
|
+
import { Project } from "ts-morph";
|
|
709
|
+
import validatePackageName from "validate-npm-package-name";
|
|
710
|
+
|
|
711
|
+
// src/utils/package.ts
|
|
712
|
+
import fs2 from "node:fs";
|
|
713
|
+
import path from "node:path";
|
|
714
|
+
var findNearestPackageJson = (startDir, until) => {
|
|
715
|
+
const packagePath = path.join(startDir, "package.json");
|
|
716
|
+
if (fs2.existsSync(packagePath)) return packagePath;
|
|
717
|
+
if (startDir === until) return void 0;
|
|
718
|
+
const segments = startDir.split(/[\/\\]/);
|
|
719
|
+
return findNearestPackageJson(segments.slice(0, segments.length - 1).join("/"), until);
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
// src/utils/language-support.ts
|
|
723
|
+
var typescript = {
|
|
724
|
+
matches: (fileName) => fileName.endsWith(".ts") || fileName.endsWith(".js") || fileName.endsWith(".tsx") || fileName.endsWith(".jsx"),
|
|
725
|
+
resolveDependencies: (filePath, category, isSubDir) => {
|
|
726
|
+
const project = new Project();
|
|
727
|
+
const blockFile = project.addSourceFileAtPath(filePath);
|
|
728
|
+
const imports = blockFile.getImportDeclarations();
|
|
729
|
+
const relativeImports = imports.filter(
|
|
730
|
+
(declaration) => declaration.getModuleSpecifierValue().startsWith(".")
|
|
731
|
+
);
|
|
732
|
+
const localDeps = /* @__PURE__ */ new Set();
|
|
733
|
+
for (const relativeImport of relativeImports) {
|
|
734
|
+
const mod = relativeImport.getModuleSpecifierValue();
|
|
735
|
+
const localDep = resolveLocalImport(mod, category, isSubDir);
|
|
736
|
+
if (localDep) localDeps.add(localDep);
|
|
737
|
+
}
|
|
738
|
+
const deps = imports.filter((declaration) => !declaration.getModuleSpecifierValue().startsWith(".")).map((declaration) => declaration.getModuleSpecifierValue());
|
|
739
|
+
const { devDependencies, dependencies } = resolveRemoteDeps(Array.from(deps), filePath);
|
|
740
|
+
return Ok({
|
|
741
|
+
local: Array.from(localDeps),
|
|
742
|
+
dependencies,
|
|
743
|
+
devDependencies
|
|
744
|
+
});
|
|
745
|
+
},
|
|
746
|
+
comment: (content) => `/*
|
|
747
|
+
${content}
|
|
748
|
+
*/`
|
|
749
|
+
};
|
|
750
|
+
var svelte = {
|
|
751
|
+
matches: (fileName) => fileName.endsWith(".svelte"),
|
|
752
|
+
resolveDependencies: (filePath, category, isSubDir) => {
|
|
753
|
+
const sourceCode = fs3.readFileSync(filePath).toString();
|
|
754
|
+
const root = sv.parse(sourceCode, { modern: true });
|
|
755
|
+
if (!root.instance) return Ok({ dependencies: [], devDependencies: [], local: [] });
|
|
756
|
+
const localDeps = /* @__PURE__ */ new Set();
|
|
757
|
+
const deps = /* @__PURE__ */ new Set();
|
|
758
|
+
walk(root.instance, {
|
|
759
|
+
enter: (node) => {
|
|
760
|
+
if (node.type === "ImportDeclaration") {
|
|
761
|
+
if (typeof node.source.value === "string") {
|
|
762
|
+
if (node.source.value.startsWith(".")) {
|
|
763
|
+
const localDep = resolveLocalImport(
|
|
764
|
+
node.source.value,
|
|
765
|
+
category,
|
|
766
|
+
isSubDir
|
|
767
|
+
);
|
|
768
|
+
if (localDep) localDeps.add(localDep);
|
|
769
|
+
} else {
|
|
770
|
+
deps.add(node.source.value);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
const { devDependencies, dependencies } = resolveRemoteDeps(Array.from(deps), filePath);
|
|
777
|
+
return Ok({
|
|
778
|
+
dependencies,
|
|
779
|
+
devDependencies,
|
|
780
|
+
local: Array.from(localDeps)
|
|
781
|
+
});
|
|
782
|
+
},
|
|
783
|
+
comment: (content) => `<!--
|
|
784
|
+
${content}
|
|
785
|
+
-->`
|
|
786
|
+
};
|
|
787
|
+
var resolveLocalImport = (mod, category, isSubDir) => {
|
|
788
|
+
if (isSubDir && mod.startsWith("./")) return void 0;
|
|
789
|
+
if (mod.startsWith("./")) {
|
|
790
|
+
return `${category}/${path2.parse(path2.basename(mod)).name}`;
|
|
791
|
+
}
|
|
792
|
+
if (isSubDir && mod.startsWith("../") && !mod.startsWith("../.")) {
|
|
793
|
+
return `${category}/${path2.parse(path2.basename(mod)).name}`;
|
|
794
|
+
}
|
|
795
|
+
const segments = mod.replaceAll("../", "").split("/");
|
|
796
|
+
if (segments.length !== 2) return void 0;
|
|
797
|
+
return `${segments[0]}/${segments[1]}`;
|
|
798
|
+
};
|
|
799
|
+
var resolveRemoteDeps = (deps, filePath) => {
|
|
800
|
+
const filteredDeps = deps.filter(
|
|
801
|
+
(dep) => !builtinModules.includes(dep) && !dep.startsWith("node:") && validatePackageName(dep).validForNewPackages
|
|
802
|
+
);
|
|
803
|
+
const pkgPath = findNearestPackageJson(path2.dirname(filePath), "");
|
|
804
|
+
const dependencies = /* @__PURE__ */ new Set();
|
|
805
|
+
const devDependencies = /* @__PURE__ */ new Set();
|
|
806
|
+
if (pkgPath) {
|
|
807
|
+
const { devDependencies: packageDevDependencies, dependencies: packageDependencies } = JSON.parse(fs3.readFileSync(pkgPath, "utf-8"));
|
|
808
|
+
for (const dep of filteredDeps) {
|
|
809
|
+
let version2 = void 0;
|
|
810
|
+
if (packageDependencies !== void 0) {
|
|
811
|
+
version2 = packageDependencies[dep];
|
|
812
|
+
}
|
|
813
|
+
if (version2 !== void 0) {
|
|
814
|
+
dependencies.add(`${dep}@${version2}`);
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
if (packageDevDependencies !== void 0) {
|
|
818
|
+
version2 = packageDevDependencies[dep];
|
|
819
|
+
}
|
|
820
|
+
if (version2 !== void 0) {
|
|
821
|
+
devDependencies.add(`${dep}@${version2}`);
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
dependencies.add(dep);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return {
|
|
828
|
+
dependencies: Array.from(dependencies),
|
|
829
|
+
devDependencies: Array.from(devDependencies)
|
|
830
|
+
};
|
|
831
|
+
};
|
|
832
|
+
var languages = [typescript, svelte];
|
|
833
|
+
|
|
834
|
+
// src/utils/build.ts
|
|
835
|
+
var blockSchema = v2.object({
|
|
836
|
+
name: v2.string(),
|
|
837
|
+
category: v2.string(),
|
|
838
|
+
localDependencies: v2.array(v2.string()),
|
|
839
|
+
dependencies: v2.array(v2.string()),
|
|
840
|
+
devDependencies: v2.array(v2.string()),
|
|
841
|
+
tests: v2.boolean(),
|
|
842
|
+
/** Where to find the block relative to root */
|
|
843
|
+
directory: v2.string(),
|
|
844
|
+
subdirectory: v2.boolean(),
|
|
845
|
+
files: v2.array(v2.string())
|
|
846
|
+
});
|
|
847
|
+
var categorySchema = v2.object({
|
|
848
|
+
name: v2.string(),
|
|
849
|
+
blocks: v2.array(blockSchema)
|
|
850
|
+
});
|
|
851
|
+
var TEST_SUFFIXES = [".test.ts", "_test.ts", ".test.js", "_test.js"];
|
|
852
|
+
var isTestFile = (file) => TEST_SUFFIXES.find((suffix) => file.endsWith(suffix)) !== void 0;
|
|
853
|
+
var buildBlocksDirectory = (blocksPath, cwd) => {
|
|
854
|
+
let paths;
|
|
855
|
+
try {
|
|
856
|
+
paths = fs4.readdirSync(blocksPath);
|
|
857
|
+
} catch {
|
|
858
|
+
program.error(color2.red(`Couldn't read the ${color2.bold(blocksPath)} directory.`));
|
|
859
|
+
}
|
|
860
|
+
const categories = [];
|
|
861
|
+
for (const categoryPath of paths) {
|
|
862
|
+
const categoryDir = path3.join(blocksPath, categoryPath);
|
|
863
|
+
if (fs4.statSync(categoryDir).isFile()) continue;
|
|
864
|
+
const categoryName = path3.basename(categoryPath);
|
|
865
|
+
const category = {
|
|
866
|
+
name: categoryName,
|
|
867
|
+
blocks: []
|
|
868
|
+
};
|
|
869
|
+
const files = fs4.readdirSync(categoryDir);
|
|
870
|
+
for (const file of files) {
|
|
871
|
+
const blockDir = path3.join(categoryDir, file);
|
|
872
|
+
if (fs4.statSync(blockDir).isFile()) {
|
|
873
|
+
if (isTestFile(file)) continue;
|
|
874
|
+
const lang = languages.find((resolver) => resolver.matches(file));
|
|
875
|
+
if (!lang) {
|
|
876
|
+
console.warn(
|
|
877
|
+
`${WARN} Skipped \`${color2.bold(blockDir)}\` \`${color2.bold(
|
|
878
|
+
path3.parse(file).ext
|
|
879
|
+
)}\` files are not currently supported!`
|
|
880
|
+
);
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
const name2 = path3.parse(path3.basename(file)).name;
|
|
884
|
+
const testsPath = files.find(
|
|
885
|
+
(f) => TEST_SUFFIXES.find((suffix) => f === `${name2}${suffix}`)
|
|
886
|
+
);
|
|
887
|
+
const { dependencies, devDependencies, local } = lang.resolveDependencies(blockDir, categoryName, false).match(
|
|
888
|
+
(val) => val,
|
|
889
|
+
(err) => {
|
|
890
|
+
program.error(color2.red(err));
|
|
891
|
+
}
|
|
892
|
+
);
|
|
893
|
+
const block = {
|
|
894
|
+
name: name2,
|
|
895
|
+
directory: path3.relative(cwd, categoryDir),
|
|
896
|
+
category: categoryName,
|
|
897
|
+
tests: testsPath !== void 0,
|
|
898
|
+
subdirectory: false,
|
|
899
|
+
files: [file],
|
|
900
|
+
localDependencies: local,
|
|
901
|
+
dependencies,
|
|
902
|
+
devDependencies
|
|
903
|
+
};
|
|
904
|
+
if (testsPath !== void 0) {
|
|
905
|
+
block.files.push(testsPath);
|
|
906
|
+
}
|
|
907
|
+
category.blocks.push(block);
|
|
908
|
+
} else {
|
|
909
|
+
const blockName = file;
|
|
910
|
+
const blockFiles = fs4.readdirSync(blockDir);
|
|
911
|
+
const hasTests = blockFiles.findIndex((f) => isTestFile(f)) !== -1;
|
|
912
|
+
const localDepsSet = /* @__PURE__ */ new Set();
|
|
913
|
+
const depsSet = /* @__PURE__ */ new Set();
|
|
914
|
+
const devDepsSet = /* @__PURE__ */ new Set();
|
|
915
|
+
for (const f of blockFiles) {
|
|
916
|
+
if (isTestFile(f)) continue;
|
|
917
|
+
const lang = languages.find((resolver) => resolver.matches(f));
|
|
918
|
+
if (!lang) {
|
|
919
|
+
console.warn(
|
|
920
|
+
`${WARN} Skipped \`${color2.bold(path3.join(blockDir, f))}\` \`${color2.bold(
|
|
921
|
+
path3.parse(file).ext
|
|
922
|
+
)}\` files are not currently supported!`
|
|
923
|
+
);
|
|
924
|
+
continue;
|
|
925
|
+
}
|
|
926
|
+
const { local, dependencies, devDependencies } = lang.resolveDependencies(path3.join(blockDir, f), categoryName, true).match(
|
|
927
|
+
(val) => val,
|
|
928
|
+
(err) => {
|
|
929
|
+
program.error(color2.red(err));
|
|
930
|
+
}
|
|
931
|
+
);
|
|
932
|
+
for (const dep of local) {
|
|
933
|
+
localDepsSet.add(dep);
|
|
934
|
+
}
|
|
935
|
+
for (const dep of dependencies) {
|
|
936
|
+
depsSet.add(dep);
|
|
937
|
+
}
|
|
938
|
+
for (const dep of devDependencies) {
|
|
939
|
+
devDepsSet.add(dep);
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
const block = {
|
|
943
|
+
name: blockName,
|
|
944
|
+
directory: path3.relative(cwd, blockDir),
|
|
945
|
+
category: categoryName,
|
|
946
|
+
tests: hasTests,
|
|
947
|
+
subdirectory: true,
|
|
948
|
+
files: [...blockFiles],
|
|
949
|
+
localDependencies: Array.from(localDepsSet.keys()),
|
|
950
|
+
dependencies: Array.from(depsSet.keys()),
|
|
951
|
+
devDependencies: Array.from(devDepsSet.keys())
|
|
952
|
+
};
|
|
953
|
+
category.blocks.push(block);
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
categories.push(category);
|
|
957
|
+
}
|
|
958
|
+
return categories;
|
|
959
|
+
};
|
|
960
|
+
|
|
961
|
+
// src/utils/get-installed-blocks.ts
|
|
962
|
+
import fs5 from "node:fs";
|
|
963
|
+
import path4 from "node:path";
|
|
964
|
+
var getInstalledBlocks = (blocks, config) => {
|
|
965
|
+
const installedBlocks = [];
|
|
966
|
+
for (const [_, block] of blocks) {
|
|
967
|
+
const baseDir = path4.join(config.path, block.category);
|
|
968
|
+
let blockPath = path4.join(baseDir, block.files[0]);
|
|
969
|
+
if (block.subdirectory) {
|
|
970
|
+
blockPath = path4.join(baseDir, block.name);
|
|
971
|
+
}
|
|
972
|
+
if (fs5.existsSync(blockPath))
|
|
973
|
+
installedBlocks.push({
|
|
974
|
+
specifier: `${block.category}/${block.name}`,
|
|
975
|
+
path: blockPath
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
return installedBlocks;
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// src/utils/get-watermark.ts
|
|
982
|
+
var getWatermark = (version2, repoUrl) => {
|
|
983
|
+
return ` jsrepo ${version2}
|
|
984
|
+
Installed from ${repoUrl}
|
|
985
|
+
${(/* @__PURE__ */ new Date()).toLocaleDateString().replaceAll("/", "-")}`;
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
// src/utils/git-providers.ts
|
|
989
|
+
import { Octokit } from "octokit";
|
|
990
|
+
import * as v3 from "valibot";
|
|
991
|
+
var octokit = new Octokit({});
|
|
992
|
+
var github = {
|
|
993
|
+
name: () => "github",
|
|
994
|
+
resolveRaw: async (repoPath, resourcePath) => {
|
|
995
|
+
let info;
|
|
996
|
+
if (typeof repoPath === "string") {
|
|
997
|
+
info = await github.info(repoPath);
|
|
998
|
+
} else {
|
|
999
|
+
info = repoPath;
|
|
1000
|
+
}
|
|
1001
|
+
return new URL(
|
|
1002
|
+
resourcePath,
|
|
1003
|
+
`https://raw.githubusercontent.com/${info.owner}/${info.repoName}/refs/${info.refs}/${info.ref}/`
|
|
1004
|
+
);
|
|
1005
|
+
},
|
|
1006
|
+
info: async (repoPath) => {
|
|
1007
|
+
const repo = repoPath.replaceAll(/(https:\/\/github.com\/)|(github\/)/g, "");
|
|
1008
|
+
const [owner, repoName, ...rest] = repo.split("/");
|
|
1009
|
+
let ref = "main";
|
|
1010
|
+
if (rest[0] === "tree") {
|
|
1011
|
+
ref = rest[1];
|
|
1012
|
+
}
|
|
1013
|
+
let refs = "heads";
|
|
1014
|
+
if (ref !== "main") {
|
|
1015
|
+
try {
|
|
1016
|
+
const { data: tags } = await octokit.rest.git.listMatchingRefs({
|
|
1017
|
+
owner,
|
|
1018
|
+
repo: repoName,
|
|
1019
|
+
ref: "tags"
|
|
1020
|
+
});
|
|
1021
|
+
if (tags.some((tag) => tag.ref === `refs/tags/${ref}`)) {
|
|
1022
|
+
refs = "tags";
|
|
1023
|
+
}
|
|
1024
|
+
} catch {
|
|
1025
|
+
refs = "heads";
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
return {
|
|
1029
|
+
refs,
|
|
1030
|
+
url: repoPath,
|
|
1031
|
+
name: github.name(),
|
|
1032
|
+
repoName,
|
|
1033
|
+
owner,
|
|
1034
|
+
ref,
|
|
1035
|
+
provider: github
|
|
1036
|
+
};
|
|
1037
|
+
},
|
|
1038
|
+
matches: (repoPath) => repoPath.toLowerCase().startsWith("https://github.com") || repoPath.toLowerCase().startsWith("github")
|
|
1039
|
+
};
|
|
1040
|
+
var getProviderInfo = async (repo) => {
|
|
1041
|
+
if (github.matches(repo)) {
|
|
1042
|
+
return Ok(await github.info(repo));
|
|
1043
|
+
}
|
|
1044
|
+
return Err("Only GitHub repositories are supported at this time!");
|
|
1045
|
+
};
|
|
1046
|
+
var getManifest = async (url) => {
|
|
1047
|
+
try {
|
|
1048
|
+
const response = await fetch(url);
|
|
1049
|
+
if (!response.ok) {
|
|
1050
|
+
return Err(
|
|
1051
|
+
`There was an error fetching the \`${OUTPUT_FILE}\` from the repository \`${url.href}\` make sure the target repository has a \`${OUTPUT_FILE}\` in its root?`
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
const categories = v3.parse(v3.array(categorySchema), await response.json());
|
|
1055
|
+
return Ok(categories);
|
|
1056
|
+
} catch {
|
|
1057
|
+
return Err(
|
|
1058
|
+
`There was an error fetching the \`${OUTPUT_FILE}\` from the repository \`${url.href}\` make sure the target repository has a \`${OUTPUT_FILE}\` in its root?`
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
|
|
1063
|
+
// src/utils/prompts.ts
|
|
1064
|
+
import { intro, spinner } from "@clack/prompts";
|
|
1065
|
+
import color3 from "chalk";
|
|
1066
|
+
|
|
1067
|
+
// src/blocks/utilities/strip-ansi.ts
|
|
1068
|
+
import ansiRegex from "ansi-regex";
|
|
1069
|
+
var stripAsni = (str) => str.replace(ansiRegex(), "");
|
|
1070
|
+
|
|
1071
|
+
// src/blocks/utilities/pad.ts
|
|
1072
|
+
var leftPadMin = (str, length, padWith = " ") => {
|
|
1073
|
+
if (stripAsni(str).length > length)
|
|
1074
|
+
throw new Error("String length is greater than the length provided.");
|
|
1075
|
+
return padWith.repeat(length - stripAsni(str).length) + str;
|
|
1076
|
+
};
|
|
1077
|
+
var rightPad = (str, space, padWith = " ") => {
|
|
1078
|
+
return str + padWith.repeat(space);
|
|
1079
|
+
};
|
|
1080
|
+
var rightPadMin = (str, length, padWith = " ") => {
|
|
1081
|
+
if (stripAsni(str).length > length)
|
|
1082
|
+
throw new Error("String length is greater than the length provided.");
|
|
1083
|
+
return str + padWith.repeat(length - stripAsni(str).length);
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
// src/utils/prompts.ts
|
|
1087
|
+
var VERTICAL_BORDER = color3.gray("\u2502");
|
|
1088
|
+
var HORIZONTAL_BORDER = color3.gray("\u2500");
|
|
1089
|
+
var TOP_RIGHT_CORNER = color3.gray("\u2510");
|
|
1090
|
+
var BOTTOM_RIGHT_CORNER = color3.gray("\u2518");
|
|
1091
|
+
var JUNCTION_RIGHT = color3.gray("\u251C");
|
|
1092
|
+
var runTasks = async (tasks, { verbose = false }) => {
|
|
1093
|
+
const loading = spinner();
|
|
1094
|
+
for (const task of tasks) {
|
|
1095
|
+
if (!verbose) loading.start(task.loadingMessage);
|
|
1096
|
+
try {
|
|
1097
|
+
await task.run();
|
|
1098
|
+
} catch (err) {
|
|
1099
|
+
console.error(err);
|
|
1100
|
+
}
|
|
1101
|
+
if (!verbose) loading.stop(task.completedMessage);
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
var nextSteps = (steps) => {
|
|
1105
|
+
let max = 20;
|
|
1106
|
+
steps.map((val) => {
|
|
1107
|
+
const reset = rightPad(stripAsni(val), 4);
|
|
1108
|
+
if (reset.length > max) max = reset.length;
|
|
1109
|
+
});
|
|
1110
|
+
const NEXT_STEPS = "Next Steps";
|
|
1111
|
+
let result = `${VERTICAL_BORDER}
|
|
1112
|
+
`;
|
|
1113
|
+
result += `${JUNCTION_RIGHT} ${NEXT_STEPS} ${HORIZONTAL_BORDER.repeat(
|
|
1114
|
+
max - NEXT_STEPS.length - 1
|
|
1115
|
+
)}${TOP_RIGHT_CORNER}
|
|
1116
|
+
`;
|
|
1117
|
+
result += `${VERTICAL_BORDER} ${" ".repeat(max)} ${VERTICAL_BORDER}
|
|
1118
|
+
`;
|
|
1119
|
+
steps.map((step) => {
|
|
1120
|
+
result += `${VERTICAL_BORDER} ${rightPadMin(step, max - 1)} ${VERTICAL_BORDER}
|
|
1121
|
+
`;
|
|
1122
|
+
});
|
|
1123
|
+
result += `${VERTICAL_BORDER} ${" ".repeat(max)} ${VERTICAL_BORDER}
|
|
1124
|
+
`;
|
|
1125
|
+
result += `${JUNCTION_RIGHT}${HORIZONTAL_BORDER.repeat(max + 2)}${BOTTOM_RIGHT_CORNER}
|
|
1126
|
+
`;
|
|
1127
|
+
return result;
|
|
1128
|
+
};
|
|
1129
|
+
var _intro = (version2) => intro(`${color3.bgHex("#f7df1e").black(" jsrepo ")}${color3.gray(` v${version2} `)}`);
|
|
1130
|
+
|
|
1131
|
+
// src/commands/add.ts
|
|
1132
|
+
var schema2 = v4.object({
|
|
1133
|
+
yes: v4.boolean(),
|
|
1134
|
+
verbose: v4.boolean(),
|
|
1135
|
+
repo: v4.optional(v4.string()),
|
|
1136
|
+
allow: v4.boolean()
|
|
1137
|
+
});
|
|
1138
|
+
var add = new Command("add").argument("[blocks...]", "Whichever block you want to add to your project.").option("-y, --yes", "Add and install any required dependencies.", false).option("-A, --allow", "Allow jsrepo to download code from the provided repo.", false).option("--repo <repo>", "Repository to download the blocks from").option("--verbose", "Include debug logs.", false).action(async (blockNames, opts) => {
|
|
1139
|
+
const options = v4.parse(schema2, opts);
|
|
1140
|
+
await _add(blockNames, options);
|
|
1141
|
+
});
|
|
1142
|
+
var _add = async (blockNames, options) => {
|
|
1143
|
+
_intro(context.package.version);
|
|
1144
|
+
const verbose = (msg) => {
|
|
1145
|
+
if (options.verbose) {
|
|
1146
|
+
console.info(`${INFO} ${msg}`);
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
verbose(`Attempting to add ${JSON.stringify(blockNames)}`);
|
|
1150
|
+
const loading = spinner2();
|
|
1151
|
+
const config = getConfig().match(
|
|
1152
|
+
(val) => val,
|
|
1153
|
+
(err) => program2.error(color4.red(err))
|
|
1154
|
+
);
|
|
1155
|
+
const blocksMap = /* @__PURE__ */ new Map();
|
|
1156
|
+
let repoPaths = config.repos;
|
|
1157
|
+
if (options.repo) repoPaths = [options.repo];
|
|
1158
|
+
if (!options.allow && options.repo) {
|
|
1159
|
+
const result = await confirm({
|
|
1160
|
+
message: `Allow ${color4.cyan("jsrepo")} to download and run code from ${color4.cyan(options.repo)}?`,
|
|
1161
|
+
initialValue: true
|
|
1162
|
+
});
|
|
1163
|
+
if (isCancel(result) || !result) {
|
|
1164
|
+
cancel("Canceled!");
|
|
1165
|
+
process.exit(0);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
verbose(`Fetching blocks from ${color4.cyan(repoPaths.join(", "))}`);
|
|
1169
|
+
if (!options.verbose) loading.start(`Fetching blocks from ${color4.cyan(repoPaths.join(", "))}`);
|
|
1170
|
+
for (const repo of repoPaths) {
|
|
1171
|
+
const providerInfo = (await getProviderInfo(repo)).match(
|
|
1172
|
+
(info) => info,
|
|
1173
|
+
(err) => {
|
|
1174
|
+
loading.stop(`Failed fetching blocks from ${color4.cyan(repo)}`);
|
|
1175
|
+
program2.error(color4.red(err));
|
|
1176
|
+
}
|
|
1177
|
+
);
|
|
1178
|
+
const manifestUrl = await providerInfo.provider.resolveRaw(providerInfo, OUTPUT_FILE);
|
|
1179
|
+
verbose(`Got info for provider ${color4.cyan(providerInfo.name)}`);
|
|
1180
|
+
const categories = (await getManifest(manifestUrl)).match(
|
|
1181
|
+
(val) => val,
|
|
1182
|
+
(err) => {
|
|
1183
|
+
loading.stop(`Failed fetching blocks from ${color4.cyan(repo)}`);
|
|
1184
|
+
program2.error(color4.red(err));
|
|
1185
|
+
}
|
|
1186
|
+
);
|
|
1187
|
+
for (const category of categories) {
|
|
1188
|
+
for (const block of category.blocks) {
|
|
1189
|
+
blocksMap.set(
|
|
1190
|
+
`${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}/${category.name}/${block.name}`,
|
|
1191
|
+
{
|
|
1192
|
+
...block,
|
|
1193
|
+
sourceRepo: providerInfo
|
|
1194
|
+
}
|
|
1195
|
+
);
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
verbose(`Retrieved blocks from ${color4.cyan(repoPaths.join(", "))}`);
|
|
1200
|
+
if (!options.verbose) loading.stop(`Retrieved blocks from ${color4.cyan(repoPaths.join(", "))}`);
|
|
1201
|
+
const installedBlocks = getInstalledBlocks(blocksMap, config).map((val) => val.specifier);
|
|
1202
|
+
let installingBlockNames = blockNames;
|
|
1203
|
+
if (installingBlockNames.length === 0) {
|
|
1204
|
+
const promptResult = await multiselect({
|
|
1205
|
+
message: "Select which blocks to add.",
|
|
1206
|
+
options: Array.from(blocksMap.entries()).map(([key, value]) => {
|
|
1207
|
+
const shortName = `${value.category}/${value.name}`;
|
|
1208
|
+
const blockExists = installedBlocks.findIndex((block) => block === shortName) !== -1;
|
|
1209
|
+
let label;
|
|
1210
|
+
if (repoPaths.length > 1) {
|
|
1211
|
+
label = `${color4.cyan(
|
|
1212
|
+
`${value.sourceRepo.name}/${value.sourceRepo.owner}/${value.sourceRepo.repoName}/${value.category}`
|
|
1213
|
+
)}/${value.name}`;
|
|
1214
|
+
} else {
|
|
1215
|
+
label = `${color4.cyan(value.category)}/${value.name}`;
|
|
1216
|
+
}
|
|
1217
|
+
return {
|
|
1218
|
+
label: blockExists ? color4.gray(label) : label,
|
|
1219
|
+
value: key,
|
|
1220
|
+
// show hint for `Installed` if block is already installed
|
|
1221
|
+
hint: blockExists ? "Installed" : void 0
|
|
1222
|
+
};
|
|
1223
|
+
}),
|
|
1224
|
+
required: true
|
|
1225
|
+
});
|
|
1226
|
+
if (isCancel(promptResult)) {
|
|
1227
|
+
cancel("Canceled!");
|
|
1228
|
+
process.exit(0);
|
|
1229
|
+
}
|
|
1230
|
+
installingBlockNames = promptResult;
|
|
1231
|
+
}
|
|
1232
|
+
verbose(`Installing blocks ${color4.cyan(installingBlockNames.join(", "))}`);
|
|
1233
|
+
if (options.verbose) console.log("Blocks map: ", blocksMap);
|
|
1234
|
+
const installingBlocks = await getBlocks(installingBlockNames, blocksMap, repoPaths);
|
|
1235
|
+
const pm = (await detect({ cwd: process.cwd() }))?.agent ?? "npm";
|
|
1236
|
+
const tasks = [];
|
|
1237
|
+
const devDeps = /* @__PURE__ */ new Set();
|
|
1238
|
+
const deps = /* @__PURE__ */ new Set();
|
|
1239
|
+
for (const { name: specifier, block } of installingBlocks) {
|
|
1240
|
+
const watermark = getWatermark(context.package.version, block.sourceRepo.url);
|
|
1241
|
+
const providerInfo = block.sourceRepo;
|
|
1242
|
+
verbose(`Attempting to add ${specifier}`);
|
|
1243
|
+
const directory = path5.join(config.path, block.category);
|
|
1244
|
+
verbose(`Creating directory ${color4.bold(directory)}`);
|
|
1245
|
+
const blockExists = !block.subdirectory && fs6.existsSync(path5.join(directory, block.files[0])) || block.subdirectory && fs6.existsSync(path5.join(directory, block.name));
|
|
1246
|
+
if (blockExists && !options.yes) {
|
|
1247
|
+
const result = await confirm({
|
|
1248
|
+
message: `${color4.bold(block.name)} already exists in your project would you like to overwrite it?`,
|
|
1249
|
+
initialValue: false
|
|
1250
|
+
});
|
|
1251
|
+
if (isCancel(result) || !result) {
|
|
1252
|
+
cancel("Canceled!");
|
|
1253
|
+
process.exit(0);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
tasks.push({
|
|
1257
|
+
loadingMessage: `Adding ${specifier}`,
|
|
1258
|
+
completedMessage: `Added ${specifier}`,
|
|
1259
|
+
run: async () => {
|
|
1260
|
+
fs6.mkdirSync(directory, { recursive: true });
|
|
1261
|
+
const files = [];
|
|
1262
|
+
const getSourceFile = async (filePath) => {
|
|
1263
|
+
const rawUrl = await providerInfo.provider.resolveRaw(providerInfo, filePath);
|
|
1264
|
+
const response = await fetch(rawUrl);
|
|
1265
|
+
if (!response.ok) {
|
|
1266
|
+
loading.stop(color4.red(`Error fetching ${color4.bold(rawUrl.href)}`));
|
|
1267
|
+
program2.error(color4.red(`There was an error trying to get ${specifier}`));
|
|
1268
|
+
}
|
|
1269
|
+
return await response.text();
|
|
1270
|
+
};
|
|
1271
|
+
for (const sourceFile of block.files) {
|
|
1272
|
+
if (!config.includeTests && isTestFile(sourceFile)) continue;
|
|
1273
|
+
const sourcePath = path5.join(block.directory, sourceFile);
|
|
1274
|
+
let destPath;
|
|
1275
|
+
if (block.subdirectory) {
|
|
1276
|
+
destPath = path5.join(config.path, block.category, block.name, sourceFile);
|
|
1277
|
+
} else {
|
|
1278
|
+
destPath = path5.join(config.path, block.category, sourceFile);
|
|
1279
|
+
}
|
|
1280
|
+
const content = await getSourceFile(sourcePath);
|
|
1281
|
+
fs6.mkdirSync(destPath.slice(0, destPath.length - sourceFile.length), {
|
|
1282
|
+
recursive: true
|
|
1283
|
+
});
|
|
1284
|
+
files.push({ content, destPath });
|
|
1285
|
+
}
|
|
1286
|
+
for (const file of files) {
|
|
1287
|
+
let content = file.content;
|
|
1288
|
+
if (config.watermark) {
|
|
1289
|
+
const lang = languages.find((lang2) => lang2.matches(file.destPath));
|
|
1290
|
+
if (lang) {
|
|
1291
|
+
const comment = lang.comment(watermark);
|
|
1292
|
+
content = `${comment}
|
|
1293
|
+
|
|
1294
|
+
${content}`;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
fs6.writeFileSync(file.destPath, content);
|
|
1298
|
+
}
|
|
1299
|
+
if (config.includeTests) {
|
|
1300
|
+
verbose("Trying to include tests");
|
|
1301
|
+
const { devDependencies } = JSON.parse(
|
|
1302
|
+
fs6.readFileSync("package.json").toString()
|
|
1303
|
+
);
|
|
1304
|
+
if (devDependencies.vitest === void 0) {
|
|
1305
|
+
devDeps.add("vitest");
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
for (const dep of block.devDependencies) {
|
|
1309
|
+
devDeps.add(dep);
|
|
1310
|
+
}
|
|
1311
|
+
for (const dep of block.dependencies) {
|
|
1312
|
+
deps.add(dep);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
await runTasks(tasks, { verbose: options.verbose });
|
|
1318
|
+
const installDependencies = async (deps2, dev) => {
|
|
1319
|
+
if (!options.verbose) loading.start(`Installing dependencies with ${color4.cyan(pm)}`);
|
|
1320
|
+
let add2;
|
|
1321
|
+
if (dev) {
|
|
1322
|
+
add2 = resolveCommand(pm, "install", [...deps2, "-D"]);
|
|
1323
|
+
} else {
|
|
1324
|
+
add2 = resolveCommand(pm, "install", [...deps2]);
|
|
1325
|
+
}
|
|
1326
|
+
if (add2 == null) {
|
|
1327
|
+
program2.error(color4.red(`Could not resolve add command for '${pm}'.`));
|
|
1328
|
+
}
|
|
1329
|
+
try {
|
|
1330
|
+
await execa(add2.command, [...add2.args], { cwd: process.cwd() });
|
|
1331
|
+
} catch {
|
|
1332
|
+
program2.error(
|
|
1333
|
+
color4.red(
|
|
1334
|
+
`Failed to install ${color4.bold("vitest")}! Failed while running '${color4.bold(
|
|
1335
|
+
`${add2.command} ${add2.args.join(" ")}`
|
|
1336
|
+
)}'`
|
|
1337
|
+
)
|
|
1338
|
+
);
|
|
1339
|
+
}
|
|
1340
|
+
if (!options.verbose) loading.stop(`Installed ${color4.cyan(deps2.join(", "))}`);
|
|
1341
|
+
};
|
|
1342
|
+
const hasDependencies = deps.size > 0 || devDeps.size > 0;
|
|
1343
|
+
if (hasDependencies) {
|
|
1344
|
+
let install = options.yes;
|
|
1345
|
+
if (!options.yes) {
|
|
1346
|
+
const result = await confirm({
|
|
1347
|
+
message: "Would you like to install dependencies?",
|
|
1348
|
+
initialValue: true
|
|
1349
|
+
});
|
|
1350
|
+
if (isCancel(result)) {
|
|
1351
|
+
cancel("Canceled!");
|
|
1352
|
+
process.exit(0);
|
|
1353
|
+
}
|
|
1354
|
+
install = result;
|
|
1355
|
+
}
|
|
1356
|
+
if (install) {
|
|
1357
|
+
if (deps.size > 0) {
|
|
1358
|
+
await installDependencies(Array.from(deps), false);
|
|
1359
|
+
}
|
|
1360
|
+
if (devDeps.size > 0) {
|
|
1361
|
+
await installDependencies(Array.from(devDeps), true);
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1364
|
+
let steps = [];
|
|
1365
|
+
if (!install) {
|
|
1366
|
+
if (deps.size > 0) {
|
|
1367
|
+
const cmd = resolveCommand(pm, "install", [...deps]);
|
|
1368
|
+
steps.push(
|
|
1369
|
+
`Install dependencies \`${color4.cyan(`${cmd?.command} ${cmd?.args.join(" ")}`)}\``
|
|
1370
|
+
);
|
|
1371
|
+
}
|
|
1372
|
+
if (devDeps.size > 0) {
|
|
1373
|
+
const cmd = resolveCommand(pm, "install", [...devDeps, "-D"]);
|
|
1374
|
+
steps.push(
|
|
1375
|
+
`Install dev dependencies \`${color4.cyan(`${cmd?.command} ${cmd?.args.join(" ")}`)}\``
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
steps = steps.map((step, i) => `${i + 1}. ${step}`);
|
|
1380
|
+
if (!install) {
|
|
1381
|
+
steps.push("");
|
|
1382
|
+
}
|
|
1383
|
+
steps.push(`Import the blocks from \`${color4.cyan(config.path)}\``);
|
|
1384
|
+
const next = nextSteps(steps);
|
|
1385
|
+
process.stdout.write(next);
|
|
1386
|
+
}
|
|
1387
|
+
outro(color4.green("All done!"));
|
|
1388
|
+
};
|
|
1389
|
+
var getBlocks = async (blockSpecifiers, blocksMap, repoPaths) => {
|
|
1390
|
+
const blocks = /* @__PURE__ */ new Map();
|
|
1391
|
+
for (const blockSpecifier of blockSpecifiers) {
|
|
1392
|
+
let block = void 0;
|
|
1393
|
+
if (!blockSpecifier.startsWith("github")) {
|
|
1394
|
+
if (repoPaths.length === 0) {
|
|
1395
|
+
program2.error(
|
|
1396
|
+
color4.red(
|
|
1397
|
+
`If your config doesn't repos then you must provide the repo in the block specifier ex: \`${color4.bold(
|
|
1398
|
+
`github/<owner>/<name>/${blockSpecifier}`
|
|
1399
|
+
)}\`!`
|
|
1400
|
+
)
|
|
1401
|
+
);
|
|
1402
|
+
}
|
|
1403
|
+
for (const repo of repoPaths) {
|
|
1404
|
+
const providerInfo = (await getProviderInfo(repo)).unwrap();
|
|
1405
|
+
const tempBlock = blocksMap.get(
|
|
1406
|
+
`${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}/${blockSpecifier}`
|
|
1407
|
+
);
|
|
1408
|
+
if (tempBlock === void 0) continue;
|
|
1409
|
+
block = tempBlock;
|
|
1410
|
+
break;
|
|
1411
|
+
}
|
|
1412
|
+
} else {
|
|
1413
|
+
if (repoPaths.length === 0) {
|
|
1414
|
+
const [providerName, owner, repoName, ...rest] = blockSpecifier.split("/");
|
|
1415
|
+
let repo;
|
|
1416
|
+
if (rest.length > 2) {
|
|
1417
|
+
repo = `${providerName}/${owner}/${repoName}/${rest.join("/")}`;
|
|
1418
|
+
} else {
|
|
1419
|
+
repo = `${providerName}/${owner}/${repoName}`;
|
|
1420
|
+
}
|
|
1421
|
+
const providerInfo = (await getProviderInfo(repo)).match(
|
|
1422
|
+
(val) => val,
|
|
1423
|
+
(err) => program2.error(color4.red(err))
|
|
1424
|
+
);
|
|
1425
|
+
const manifestUrl = await providerInfo.provider.resolveRaw(
|
|
1426
|
+
providerInfo,
|
|
1427
|
+
OUTPUT_FILE
|
|
1428
|
+
);
|
|
1429
|
+
const categories = (await getManifest(manifestUrl)).match(
|
|
1430
|
+
(val) => val,
|
|
1431
|
+
(err) => program2.error(color4.red(err))
|
|
1432
|
+
);
|
|
1433
|
+
for (const category of categories) {
|
|
1434
|
+
for (const block2 of category.blocks) {
|
|
1435
|
+
blocksMap.set(
|
|
1436
|
+
`${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}/${category.name}/${block2.name}`,
|
|
1437
|
+
{
|
|
1438
|
+
...block2,
|
|
1439
|
+
sourceRepo: providerInfo
|
|
1440
|
+
}
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
block = blocksMap.get(blockSpecifier);
|
|
1446
|
+
}
|
|
1447
|
+
if (!block) {
|
|
1448
|
+
program2.error(
|
|
1449
|
+
color4.red(`Invalid block! ${color4.bold(blockSpecifier)} does not exist!`)
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1452
|
+
blocks.set(blockSpecifier, { name: blockSpecifier, subDependency: false, block });
|
|
1453
|
+
if (block.localDependencies && block.localDependencies.length > 0) {
|
|
1454
|
+
const subDeps = await getBlocks(
|
|
1455
|
+
block.localDependencies.filter((dep) => blocks.has(dep)),
|
|
1456
|
+
blocksMap,
|
|
1457
|
+
repoPaths
|
|
1458
|
+
);
|
|
1459
|
+
for (const dep of subDeps) {
|
|
1460
|
+
blocks.set(dep.name, dep);
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
return mapToArray(blocks, (_, val) => val);
|
|
1465
|
+
};
|
|
1466
|
+
|
|
1467
|
+
// src/commands/build.ts
|
|
1468
|
+
import fs7 from "node:fs";
|
|
1469
|
+
import path6 from "node:path";
|
|
1470
|
+
import { outro as outro2, spinner as spinner3 } from "@clack/prompts";
|
|
1471
|
+
import color5 from "chalk";
|
|
1472
|
+
import { Command as Command2 } from "commander";
|
|
1473
|
+
import * as v5 from "valibot";
|
|
1474
|
+
var schema3 = v5.object({
|
|
1475
|
+
verbose: v5.boolean(),
|
|
1476
|
+
output: v5.boolean(),
|
|
1477
|
+
dirs: v5.array(v5.string()),
|
|
1478
|
+
cwd: v5.string()
|
|
1479
|
+
});
|
|
1480
|
+
var build = new Command2("build").description(`Builds the provided --dirs in the project root into a \`${OUTPUT_FILE}\` file.`).option("--dirs [dirs...]", "The directories containing the blocks.", ["./blocks"]).option("--no-output", `Do not output a \`${OUTPUT_FILE}\` file.`).option("--verbose", "Include debug logs.", false).option("--cwd <cwd>", "The current working directory", process.cwd()).action(async (opts) => {
|
|
1481
|
+
const options = v5.parse(schema3, opts);
|
|
1482
|
+
await _build(options);
|
|
1483
|
+
});
|
|
1484
|
+
var _build = async (options) => {
|
|
1485
|
+
_intro(context.package.version);
|
|
1486
|
+
const loading = spinner3();
|
|
1487
|
+
const categories = [];
|
|
1488
|
+
const outFile = path6.join(options.cwd, OUTPUT_FILE);
|
|
1489
|
+
for (const dir of options.dirs) {
|
|
1490
|
+
const dirPath = path6.join(options.cwd, dir);
|
|
1491
|
+
loading.start(`Building ${color5.cyan(dirPath)}`);
|
|
1492
|
+
if (options.output && fs7.existsSync(outFile)) fs7.rmSync(outFile);
|
|
1493
|
+
categories.push(...buildBlocksDirectory(dirPath, options.cwd));
|
|
1494
|
+
loading.stop(`Built ${color5.cyan(dirPath)}`);
|
|
1495
|
+
}
|
|
1496
|
+
const categoriesMap = /* @__PURE__ */ new Map();
|
|
1497
|
+
for (const category of categories) {
|
|
1498
|
+
const cat = categoriesMap.get(category.name);
|
|
1499
|
+
if (!cat) {
|
|
1500
|
+
categoriesMap.set(category.name, category);
|
|
1501
|
+
continue;
|
|
1502
|
+
}
|
|
1503
|
+
cat.blocks = [...cat.blocks, ...category.blocks];
|
|
1504
|
+
categoriesMap.set(cat.name, cat);
|
|
1505
|
+
}
|
|
1506
|
+
if (options.output) {
|
|
1507
|
+
fs7.writeFileSync(outFile, JSON.stringify(categories, null, " "));
|
|
1508
|
+
} else {
|
|
1509
|
+
loading.stop("Built successfully!");
|
|
1510
|
+
}
|
|
1511
|
+
outro2(color5.green("All done!"));
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1514
|
+
// src/commands/diff.ts
|
|
1515
|
+
import fs8 from "node:fs";
|
|
1516
|
+
import path7 from "node:path";
|
|
1517
|
+
import { cancel as cancel2, confirm as confirm2, isCancel as isCancel2, outro as outro3, spinner as spinner4 } from "@clack/prompts";
|
|
1518
|
+
import color7 from "chalk";
|
|
1519
|
+
import { Command as Command3, program as program3 } from "commander";
|
|
1520
|
+
import { diffLines } from "diff";
|
|
1521
|
+
import * as v6 from "valibot";
|
|
1522
|
+
|
|
1523
|
+
// src/utils/diff.ts
|
|
1524
|
+
import color6 from "chalk";
|
|
1525
|
+
import { diffChars } from "diff";
|
|
1526
|
+
|
|
1527
|
+
// src/blocks/utilities/array-sum.ts
|
|
1528
|
+
var arraySum = (arr, fn) => {
|
|
1529
|
+
let total = 0;
|
|
1530
|
+
for (const item of arr) {
|
|
1531
|
+
total = total + fn(item);
|
|
1532
|
+
}
|
|
1533
|
+
return total;
|
|
1534
|
+
};
|
|
1535
|
+
|
|
1536
|
+
// src/blocks/utilities/lines.ts
|
|
1537
|
+
import os from "node:os";
|
|
1538
|
+
var NEW_LINE_REGEX = /\n|\r\n/g;
|
|
1539
|
+
var get = (str) => str.split(NEW_LINE_REGEX);
|
|
1540
|
+
var join = (lines, { lineNumbers = false, prefix } = {}) => {
|
|
1541
|
+
let transformed = lines;
|
|
1542
|
+
if (lineNumbers) {
|
|
1543
|
+
const length = lines.length.toString().length + 1;
|
|
1544
|
+
transformed = transformed.map((line, i) => `${leftPadMin(`${i + 1}`, length)} ${line}`);
|
|
1545
|
+
}
|
|
1546
|
+
if (prefix !== void 0) {
|
|
1547
|
+
transformed = transformed.map((line, i) => `${prefix(i, lines.length)}${line}`);
|
|
1548
|
+
}
|
|
1549
|
+
return transformed.join(os.EOL);
|
|
1550
|
+
};
|
|
1551
|
+
|
|
1552
|
+
// src/utils/diff.ts
|
|
1553
|
+
var formatDiff = ({
|
|
1554
|
+
from,
|
|
1555
|
+
to,
|
|
1556
|
+
changes,
|
|
1557
|
+
expand = false,
|
|
1558
|
+
maxUnchanged = 5,
|
|
1559
|
+
colorRemoved = color6.red,
|
|
1560
|
+
colorAdded = color6.green,
|
|
1561
|
+
prefix,
|
|
1562
|
+
onUnchanged,
|
|
1563
|
+
intro: intro2
|
|
1564
|
+
}) => {
|
|
1565
|
+
let result = "";
|
|
1566
|
+
const length = arraySum(changes, (change) => change.count ?? 0).toString().length + 1;
|
|
1567
|
+
let lineOffset = 0;
|
|
1568
|
+
if (changes.length === 1 && !changes[0].added && !changes[0].removed) {
|
|
1569
|
+
return onUnchanged({
|
|
1570
|
+
from,
|
|
1571
|
+
to,
|
|
1572
|
+
changes,
|
|
1573
|
+
expand,
|
|
1574
|
+
maxUnchanged,
|
|
1575
|
+
colorAdded,
|
|
1576
|
+
colorRemoved,
|
|
1577
|
+
prefix,
|
|
1578
|
+
onUnchanged,
|
|
1579
|
+
intro: intro2
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
result += intro2({
|
|
1583
|
+
from,
|
|
1584
|
+
to,
|
|
1585
|
+
changes,
|
|
1586
|
+
expand,
|
|
1587
|
+
maxUnchanged,
|
|
1588
|
+
colorAdded,
|
|
1589
|
+
colorRemoved,
|
|
1590
|
+
prefix,
|
|
1591
|
+
onUnchanged,
|
|
1592
|
+
intro: intro2
|
|
1593
|
+
});
|
|
1594
|
+
const linePrefix = (line) => color6.gray(`${prefix?.() ?? ""}${leftPadMin(`${line + 1 + lineOffset} `, length)} `);
|
|
1595
|
+
for (let i = 0; i < changes.length; i++) {
|
|
1596
|
+
const change = changes[i];
|
|
1597
|
+
const hasPreviousChange = changes[i - 1]?.added || changes[i - 1]?.removed;
|
|
1598
|
+
const hasNextChange = changes[i + 1]?.added || changes[i + 1]?.removed;
|
|
1599
|
+
if (!change.added && !change.removed) {
|
|
1600
|
+
if (!expand && change.count !== void 0 && change.count > maxUnchanged) {
|
|
1601
|
+
const prevLineOffset = lineOffset;
|
|
1602
|
+
const ls = get(change.value.trimEnd());
|
|
1603
|
+
let shownLines = 0;
|
|
1604
|
+
if (hasNextChange) shownLines += maxUnchanged;
|
|
1605
|
+
if (hasPreviousChange) shownLines += maxUnchanged;
|
|
1606
|
+
if (shownLines >= ls.length) {
|
|
1607
|
+
result += `${join(ls, {
|
|
1608
|
+
prefix: linePrefix
|
|
1609
|
+
})}
|
|
1610
|
+
`;
|
|
1611
|
+
lineOffset += ls.length;
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
if (hasPreviousChange) {
|
|
1615
|
+
result += `${join(ls.slice(0, maxUnchanged), {
|
|
1616
|
+
prefix: linePrefix
|
|
1617
|
+
})}
|
|
1618
|
+
`;
|
|
1619
|
+
}
|
|
1620
|
+
if (ls.length > shownLines) {
|
|
1621
|
+
const count = ls.length - shownLines;
|
|
1622
|
+
result += `${join(
|
|
1623
|
+
get(
|
|
1624
|
+
color6.gray(
|
|
1625
|
+
`+ ${count} more unchanged (${color6.italic("-E to expand")})`
|
|
1626
|
+
)
|
|
1627
|
+
),
|
|
1628
|
+
{
|
|
1629
|
+
prefix: () => `${prefix?.() ?? ""}${leftPadMin(" ", length)} `
|
|
1630
|
+
}
|
|
1631
|
+
)}
|
|
1632
|
+
`;
|
|
1633
|
+
}
|
|
1634
|
+
if (hasNextChange) {
|
|
1635
|
+
lineOffset = lineOffset + ls.length - maxUnchanged;
|
|
1636
|
+
result += `${join(ls.slice(ls.length - maxUnchanged), {
|
|
1637
|
+
prefix: linePrefix
|
|
1638
|
+
})}
|
|
1639
|
+
`;
|
|
1640
|
+
}
|
|
1641
|
+
lineOffset = prevLineOffset + change.count;
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
result += `${join(get(change.value.trimEnd()), {
|
|
1645
|
+
prefix: linePrefix
|
|
1646
|
+
})}
|
|
1647
|
+
`;
|
|
1648
|
+
lineOffset += change.count ?? 0;
|
|
1649
|
+
continue;
|
|
1650
|
+
}
|
|
1651
|
+
const colorChange = (change2) => {
|
|
1652
|
+
if (change2.added) {
|
|
1653
|
+
return colorAdded(change2.value.trimEnd());
|
|
1654
|
+
}
|
|
1655
|
+
if (change2.removed) {
|
|
1656
|
+
return colorRemoved(change2.value.trimEnd());
|
|
1657
|
+
}
|
|
1658
|
+
return change2.value;
|
|
1659
|
+
};
|
|
1660
|
+
if (change.removed && change.count === 1) {
|
|
1661
|
+
const diffedChars = diffChars(change.value, changes[i + 1].value);
|
|
1662
|
+
const sentence = diffedChars.map((chg) => colorChange(chg)).join("");
|
|
1663
|
+
result += `${linePrefix(0)}${sentence}`;
|
|
1664
|
+
lineOffset += 1;
|
|
1665
|
+
i++;
|
|
1666
|
+
} else {
|
|
1667
|
+
result += `${join(get(colorChange(change)), {
|
|
1668
|
+
prefix: linePrefix
|
|
1669
|
+
})}
|
|
1670
|
+
`;
|
|
1671
|
+
if (!change.removed) {
|
|
1672
|
+
lineOffset += change.count ?? 0;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return result;
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
// src/commands/diff.ts
|
|
1680
|
+
var L = color7.gray("\u2502");
|
|
1681
|
+
var schema4 = v6.object({
|
|
1682
|
+
allow: v6.boolean(),
|
|
1683
|
+
expand: v6.boolean(),
|
|
1684
|
+
maxUnchanged: v6.number(),
|
|
1685
|
+
repo: v6.optional(v6.string())
|
|
1686
|
+
});
|
|
1687
|
+
var diff = new Command3("diff").description("Compares local blocks to the blocks in the provided repository.").option("-A, --allow", "Allow jsrepo to download code from the provided repo.", false).option("-E, --expand", "Expands the diff so you see everything.", false).option("--repo <repo>", "Repository to download the blocks from.").option(
|
|
1688
|
+
"--max-unchanged <number>",
|
|
1689
|
+
"Maximum unchanged lines that will show without being collapsed.",
|
|
1690
|
+
(val) => Number.parseInt(val),
|
|
1691
|
+
// this is such a dumb api thing
|
|
1692
|
+
3
|
|
1693
|
+
).action(async (opts) => {
|
|
1694
|
+
const options = v6.parse(schema4, opts);
|
|
1695
|
+
await _diff(options);
|
|
1696
|
+
});
|
|
1697
|
+
var _diff = async (options) => {
|
|
1698
|
+
_intro(context.package.version);
|
|
1699
|
+
const loading = spinner4();
|
|
1700
|
+
const config = getConfig().match(
|
|
1701
|
+
(val) => val,
|
|
1702
|
+
(err) => program3.error(color7.red(err))
|
|
1703
|
+
);
|
|
1704
|
+
const blocksMap = /* @__PURE__ */ new Map();
|
|
1705
|
+
let repoPaths = config.repos;
|
|
1706
|
+
if (options.repo) repoPaths = [options.repo];
|
|
1707
|
+
if (!options.allow && options.repo) {
|
|
1708
|
+
const result = await confirm2({
|
|
1709
|
+
message: `Allow ${color7.cyan("jsrepo")} to download and run code from ${color7.cyan(options.repo)}?`,
|
|
1710
|
+
initialValue: true
|
|
1711
|
+
});
|
|
1712
|
+
if (isCancel2(result) || !result) {
|
|
1713
|
+
cancel2("Canceled!");
|
|
1714
|
+
process.exit(0);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
loading.start(`Fetching blocks from ${color7.cyan(repoPaths.join(", "))}`);
|
|
1718
|
+
for (const repo of repoPaths) {
|
|
1719
|
+
const providerInfo = (await getProviderInfo(repo)).match(
|
|
1720
|
+
(info) => info,
|
|
1721
|
+
(err) => program3.error(color7.red(err))
|
|
1722
|
+
);
|
|
1723
|
+
const manifestUrl = await providerInfo.provider.resolveRaw(providerInfo, OUTPUT_FILE);
|
|
1724
|
+
const categories = (await getManifest(manifestUrl)).match(
|
|
1725
|
+
(val) => val,
|
|
1726
|
+
(err) => program3.error(color7.red(err))
|
|
1727
|
+
);
|
|
1728
|
+
for (const category of categories) {
|
|
1729
|
+
for (const block of category.blocks) {
|
|
1730
|
+
blocksMap.set(
|
|
1731
|
+
`${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}/${category.name}/${block.name}`,
|
|
1732
|
+
{
|
|
1733
|
+
...block,
|
|
1734
|
+
sourceRepo: providerInfo
|
|
1735
|
+
}
|
|
1736
|
+
);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
loading.stop(`Retrieved blocks from ${color7.cyan(repoPaths.join(", "))}`);
|
|
1741
|
+
const installedBlocks = getInstalledBlocks(blocksMap, config);
|
|
1742
|
+
for (const blockSpecifier of installedBlocks) {
|
|
1743
|
+
let found = false;
|
|
1744
|
+
for (const repo of repoPaths) {
|
|
1745
|
+
const providerInfo = (await getProviderInfo(repo)).unwrap();
|
|
1746
|
+
const fullSpecifier = `${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}/${blockSpecifier.specifier}`;
|
|
1747
|
+
const block = blocksMap.get(fullSpecifier);
|
|
1748
|
+
if (block === void 0) continue;
|
|
1749
|
+
found = true;
|
|
1750
|
+
process.stdout.write(`${L}
|
|
1751
|
+
`);
|
|
1752
|
+
process.stdout.write(`${L} ${fullSpecifier}
|
|
1753
|
+
`);
|
|
1754
|
+
fullSpecifier;
|
|
1755
|
+
for (const file of block.files) {
|
|
1756
|
+
if (!config.includeTests && isTestFile(file)) continue;
|
|
1757
|
+
process.stdout.write(`${L}
|
|
1758
|
+
`);
|
|
1759
|
+
const sourcePath = path7.join(block.directory, file);
|
|
1760
|
+
const rawUrl = await providerInfo.provider.resolveRaw(providerInfo, sourcePath);
|
|
1761
|
+
const response = await fetch(rawUrl);
|
|
1762
|
+
if (!response.ok) {
|
|
1763
|
+
program3.error(color7.red(`There was an error trying to get ${fullSpecifier}`));
|
|
1764
|
+
}
|
|
1765
|
+
let remoteContent = await response.text();
|
|
1766
|
+
let localPath = path7.join(config.path, block.category, file);
|
|
1767
|
+
if (block.subdirectory) {
|
|
1768
|
+
localPath = path7.join(config.path, block.category, block.name, file);
|
|
1769
|
+
}
|
|
1770
|
+
let fileContent = "";
|
|
1771
|
+
if (fs8.existsSync(localPath)) {
|
|
1772
|
+
fileContent = fs8.readFileSync(localPath).toString();
|
|
1773
|
+
}
|
|
1774
|
+
if (config.watermark) {
|
|
1775
|
+
const lang = languages.find((lang2) => lang2.matches(sourcePath));
|
|
1776
|
+
if (lang) {
|
|
1777
|
+
const watermark = getWatermark(context.package.version, repo);
|
|
1778
|
+
const comment = lang.comment(watermark);
|
|
1779
|
+
remoteContent = `${comment}
|
|
1780
|
+
|
|
1781
|
+
${remoteContent}`;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
const changes = diffLines(fileContent, remoteContent);
|
|
1785
|
+
const from = path7.join(
|
|
1786
|
+
`${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}`,
|
|
1787
|
+
sourcePath
|
|
1788
|
+
).replaceAll("\\", "/");
|
|
1789
|
+
const to = localPath.replaceAll("\\", "/");
|
|
1790
|
+
const formattedDiff = formatDiff({
|
|
1791
|
+
from,
|
|
1792
|
+
to,
|
|
1793
|
+
changes,
|
|
1794
|
+
expand: options.expand,
|
|
1795
|
+
maxUnchanged: options.maxUnchanged,
|
|
1796
|
+
colorAdded: color7.greenBright,
|
|
1797
|
+
colorRemoved: color7.redBright,
|
|
1798
|
+
prefix: () => `${L} `,
|
|
1799
|
+
onUnchanged: ({ from: from2, to: to2, prefix }) => `${prefix?.() ?? ""}${color7.cyan(from2)} \u2192 ${color7.gray(to2)} ${color7.gray("(unchanged)")}
|
|
1800
|
+
`,
|
|
1801
|
+
intro: ({ from: from2, to: to2, changes: changes2, prefix }) => {
|
|
1802
|
+
const totalChanges = changes2.filter((a) => a.added).length;
|
|
1803
|
+
return `${prefix?.() ?? ""}${color7.cyan(from2)} \u2192 ${color7.gray(to2)} (${totalChanges} change${totalChanges === 1 ? "" : "s"})
|
|
1804
|
+
${prefix?.() ?? ""}
|
|
1805
|
+
`;
|
|
1806
|
+
}
|
|
1807
|
+
});
|
|
1808
|
+
process.stdout.write(formattedDiff);
|
|
1809
|
+
}
|
|
1810
|
+
break;
|
|
1811
|
+
}
|
|
1812
|
+
if (!found) {
|
|
1813
|
+
program3.error(
|
|
1814
|
+
color7.red(`Invalid block! ${color7.bold(blockSpecifier)} does not exist!`)
|
|
1815
|
+
);
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
outro3(color7.green("All done!"));
|
|
1819
|
+
};
|
|
1820
|
+
|
|
1821
|
+
// src/commands/init.ts
|
|
1822
|
+
import fs9 from "node:fs";
|
|
1823
|
+
import { cancel as cancel3, confirm as confirm3, isCancel as isCancel3, outro as outro4, spinner as spinner5, text } from "@clack/prompts";
|
|
1824
|
+
import color8 from "chalk";
|
|
1825
|
+
import { Command as Command4 } from "commander";
|
|
1826
|
+
import * as v7 from "valibot";
|
|
1827
|
+
var schema5 = v7.object({
|
|
1828
|
+
path: v7.optional(v7.string()),
|
|
1829
|
+
tests: v7.optional(v7.boolean()),
|
|
1830
|
+
repos: v7.optional(v7.array(v7.string())),
|
|
1831
|
+
watermark: v7.boolean()
|
|
1832
|
+
});
|
|
1833
|
+
var init = new Command4("init").description("Initializes the configuration file").option("--path <path>", "Path to install the blocks").option("--repos [repos...]", "Repository to install the blocks from").option(
|
|
1834
|
+
"--no-watermark",
|
|
1835
|
+
"Will not add a watermark to each file upon adding it to your project."
|
|
1836
|
+
).option("--tests", "Will include tests along with the functions.").action(async (opts) => {
|
|
1837
|
+
const options = v7.parse(schema5, opts);
|
|
1838
|
+
await _init(options);
|
|
1839
|
+
});
|
|
1840
|
+
var _init = async (options) => {
|
|
1841
|
+
_intro(context.package.version);
|
|
1842
|
+
const initialConfig = getConfig();
|
|
1843
|
+
const loading = spinner5();
|
|
1844
|
+
if (!options.path) {
|
|
1845
|
+
const result = await text({
|
|
1846
|
+
message: "Where should we add the blocks?",
|
|
1847
|
+
validate(value) {
|
|
1848
|
+
if (value.trim() === "") return "Please provide a value";
|
|
1849
|
+
},
|
|
1850
|
+
initialValue: initialConfig.isOk() ? initialConfig.unwrap().path : "src/blocks"
|
|
1851
|
+
});
|
|
1852
|
+
if (isCancel3(result)) {
|
|
1853
|
+
cancel3("Canceled!");
|
|
1854
|
+
process.exit(0);
|
|
1855
|
+
}
|
|
1856
|
+
options.path = result;
|
|
1857
|
+
}
|
|
1858
|
+
if (!options.repos) {
|
|
1859
|
+
options.repos = initialConfig.isOk() ? initialConfig.unwrap().repos : [];
|
|
1860
|
+
while (true) {
|
|
1861
|
+
if (options.repos.length > 0) {
|
|
1862
|
+
const confirmResult = await confirm3({
|
|
1863
|
+
message: "Add another repo?",
|
|
1864
|
+
initialValue: false
|
|
1865
|
+
});
|
|
1866
|
+
if (isCancel3(confirmResult)) {
|
|
1867
|
+
cancel3("Canceled!");
|
|
1868
|
+
process.exit(0);
|
|
1869
|
+
}
|
|
1870
|
+
if (!confirmResult) break;
|
|
1871
|
+
}
|
|
1872
|
+
const result = await text({
|
|
1873
|
+
message: "Where should we download the blocks from?",
|
|
1874
|
+
placeholder: "github/ieedan/std",
|
|
1875
|
+
validate: (val) => {
|
|
1876
|
+
if (!val.startsWith("https://github.com") && !val.startsWith("github/")) {
|
|
1877
|
+
return `Must be a ${color8.bold("GitHub")} repository!`;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
});
|
|
1881
|
+
if (isCancel3(result)) {
|
|
1882
|
+
cancel3("Canceled!");
|
|
1883
|
+
process.exit(0);
|
|
1884
|
+
}
|
|
1885
|
+
options.repos.push(result);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
const config = {
|
|
1889
|
+
$schema: `https://unpkg.com/jsrepo@${context.package.version}/schema.json`,
|
|
1890
|
+
repos: options.repos,
|
|
1891
|
+
path: options.path,
|
|
1892
|
+
includeTests: initialConfig.isOk() && options.tests === void 0 ? initialConfig.unwrap().includeTests : options.tests ?? false,
|
|
1893
|
+
watermark: options.watermark
|
|
1894
|
+
};
|
|
1895
|
+
loading.start(`Writing config to \`${CONFIG_NAME}\``);
|
|
1896
|
+
fs9.writeFileSync(CONFIG_NAME, `${JSON.stringify(config, null, " ")}
|
|
1897
|
+
`);
|
|
1898
|
+
fs9.mkdirSync(config.path, { recursive: true });
|
|
1899
|
+
loading.stop(`Wrote config to \`${CONFIG_NAME}\`.`);
|
|
1900
|
+
outro4(color8.green("All done!"));
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1903
|
+
// src/commands/test.ts
|
|
1904
|
+
import fs10 from "node:fs";
|
|
1905
|
+
import path8 from "node:path";
|
|
1906
|
+
import { cancel as cancel4, confirm as confirm4, isCancel as isCancel4, outro as outro5, spinner as spinner6 } from "@clack/prompts";
|
|
1907
|
+
import color9 from "chalk";
|
|
1908
|
+
import { Argument, Command as Command5, program as program4 } from "commander";
|
|
1909
|
+
import { execa as execa2 } from "execa";
|
|
1910
|
+
import { resolveCommand as resolveCommand2 } from "package-manager-detector/commands";
|
|
1911
|
+
import { detect as detect2 } from "package-manager-detector/detect";
|
|
1912
|
+
import { Project as Project2 } from "ts-morph";
|
|
1913
|
+
import * as v8 from "valibot";
|
|
1914
|
+
var schema6 = v8.object({
|
|
1915
|
+
debug: v8.boolean(),
|
|
1916
|
+
verbose: v8.boolean(),
|
|
1917
|
+
repo: v8.optional(v8.string()),
|
|
1918
|
+
allow: v8.boolean()
|
|
1919
|
+
});
|
|
1920
|
+
var test = new Command5("test").description("Tests blocks against most recent tests").addArgument(new Argument("[blocks...]", "Whichever blocks you want to test.").default([])).option("--verbose", "Include debug logs.", false).option("-A, --allow", "Allow jsrepo to download code from the provided repo.", false).option("--repo <repo>", "Repository to download the blocks from").option("--debug", "Leaves the temp test file around for debugging upon failure.", false).action(async (blockNames, opts) => {
|
|
1921
|
+
const options = v8.parse(schema6, opts);
|
|
1922
|
+
await _test(blockNames, options);
|
|
1923
|
+
});
|
|
1924
|
+
var _test = async (blockNames, options) => {
|
|
1925
|
+
_intro(context.package.version);
|
|
1926
|
+
const verbose = (msg) => {
|
|
1927
|
+
if (options.verbose) {
|
|
1928
|
+
console.info(`${INFO} ${msg}`);
|
|
1929
|
+
}
|
|
1930
|
+
};
|
|
1931
|
+
verbose(`Attempting to test ${JSON.stringify(blockNames)}`);
|
|
1932
|
+
const config = getConfig().match(
|
|
1933
|
+
(val) => val,
|
|
1934
|
+
(err) => program4.error(color9.red(err))
|
|
1935
|
+
);
|
|
1936
|
+
const loading = spinner6();
|
|
1937
|
+
const blocksMap = /* @__PURE__ */ new Map();
|
|
1938
|
+
let repoPaths = config.repos;
|
|
1939
|
+
if (options.repo) repoPaths = [options.repo];
|
|
1940
|
+
if (!options.allow && options.repo) {
|
|
1941
|
+
const result = await confirm4({
|
|
1942
|
+
message: `Allow ${color9.cyan("jsrepo")} to download and run code from ${color9.cyan(options.repo)}?`,
|
|
1943
|
+
initialValue: true
|
|
1944
|
+
});
|
|
1945
|
+
if (isCancel4(result) || !result) {
|
|
1946
|
+
cancel4("Canceled!");
|
|
1947
|
+
process.exit(0);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
verbose(`Fetching blocks from ${color9.cyan(repoPaths.join(", "))}`);
|
|
1951
|
+
if (!options.verbose) loading.start(`Fetching blocks from ${color9.cyan(repoPaths.join(", "))}`);
|
|
1952
|
+
for (const repo of repoPaths) {
|
|
1953
|
+
const providerInfo = (await getProviderInfo(repo)).match(
|
|
1954
|
+
(info) => info,
|
|
1955
|
+
(err) => program4.error(color9.red(err))
|
|
1956
|
+
);
|
|
1957
|
+
const manifestUrl = await providerInfo.provider.resolveRaw(providerInfo, OUTPUT_FILE);
|
|
1958
|
+
verbose(`Got info for provider ${color9.cyan(providerInfo.name)}`);
|
|
1959
|
+
const response = await fetch(manifestUrl);
|
|
1960
|
+
if (!response.ok) {
|
|
1961
|
+
if (!options.verbose) loading.stop(`Error fetching ${color9.cyan(manifestUrl.href)}`);
|
|
1962
|
+
program4.error(
|
|
1963
|
+
color9.red(
|
|
1964
|
+
`There was an error fetching the \`${OUTPUT_FILE}\` from the repository ${color9.cyan(
|
|
1965
|
+
repo
|
|
1966
|
+
)} make sure the target repository has a \`${OUTPUT_FILE}\` in its root?`
|
|
1967
|
+
)
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1970
|
+
const categories = v8.parse(v8.array(categorySchema), await response.json());
|
|
1971
|
+
for (const category of categories) {
|
|
1972
|
+
for (const block of category.blocks) {
|
|
1973
|
+
blocksMap.set(
|
|
1974
|
+
`${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}/${category.name}/${block.name}`,
|
|
1975
|
+
{
|
|
1976
|
+
...block,
|
|
1977
|
+
sourceRepo: providerInfo
|
|
1978
|
+
}
|
|
1979
|
+
);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
verbose(`Retrieved blocks from ${color9.cyan(repoPaths.join(", "))}`);
|
|
1984
|
+
if (!options.verbose) loading.stop(`Retrieved blocks from ${color9.cyan(repoPaths.join(", "))}`);
|
|
1985
|
+
const tempTestDirectory = `blocks-tests-temp-${Date.now()}`;
|
|
1986
|
+
verbose(`Trying to create the temp directory ${color9.bold(tempTestDirectory)}.`);
|
|
1987
|
+
fs10.mkdirSync(tempTestDirectory, { recursive: true });
|
|
1988
|
+
const cleanUp = () => {
|
|
1989
|
+
fs10.rmSync(tempTestDirectory, { recursive: true, force: true });
|
|
1990
|
+
};
|
|
1991
|
+
const installedBlocks = getInstalledBlocks(blocksMap, config).map((val) => val.specifier);
|
|
1992
|
+
let testingBlocks = blockNames;
|
|
1993
|
+
if (blockNames.length === 0) {
|
|
1994
|
+
testingBlocks = installedBlocks;
|
|
1995
|
+
}
|
|
1996
|
+
if (testingBlocks.length === 0) {
|
|
1997
|
+
cleanUp();
|
|
1998
|
+
program4.error(color9.red("There were no blocks found in your project!"));
|
|
1999
|
+
}
|
|
2000
|
+
const testingBlocksMapped = [];
|
|
2001
|
+
for (const blockSpecifier of testingBlocks) {
|
|
2002
|
+
let block = void 0;
|
|
2003
|
+
if (!blockSpecifier.startsWith("github")) {
|
|
2004
|
+
for (const repo of repoPaths) {
|
|
2005
|
+
const providerInfo = (await getProviderInfo(repo)).unwrap();
|
|
2006
|
+
const tempBlock = blocksMap.get(
|
|
2007
|
+
`${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}/${blockSpecifier}`
|
|
2008
|
+
);
|
|
2009
|
+
if (tempBlock === void 0) continue;
|
|
2010
|
+
block = tempBlock;
|
|
2011
|
+
break;
|
|
2012
|
+
}
|
|
2013
|
+
} else {
|
|
2014
|
+
if (repoPaths.length === 0) {
|
|
2015
|
+
const [providerName, owner, repoName, ...rest] = blockSpecifier.split("/");
|
|
2016
|
+
let repo;
|
|
2017
|
+
if (rest.length > 2) {
|
|
2018
|
+
repo = `${providerName}/${owner}/${repoName}/${rest.join("/")}`;
|
|
2019
|
+
} else {
|
|
2020
|
+
repo = `${providerName}/${owner}/${repoName}`;
|
|
2021
|
+
}
|
|
2022
|
+
const providerInfo = (await getProviderInfo(repo)).match(
|
|
2023
|
+
(val) => val,
|
|
2024
|
+
(err) => program4.error(color9.red(err))
|
|
2025
|
+
);
|
|
2026
|
+
const manifestUrl = await providerInfo.provider.resolveRaw(
|
|
2027
|
+
providerInfo,
|
|
2028
|
+
OUTPUT_FILE
|
|
2029
|
+
);
|
|
2030
|
+
const categories = (await getManifest(manifestUrl)).match(
|
|
2031
|
+
(val) => val,
|
|
2032
|
+
(err) => program4.error(color9.red(err))
|
|
2033
|
+
);
|
|
2034
|
+
for (const category of categories) {
|
|
2035
|
+
for (const block2 of category.blocks) {
|
|
2036
|
+
blocksMap.set(
|
|
2037
|
+
`${providerInfo.name}/${providerInfo.owner}/${providerInfo.repoName}/${category.name}/${block2.name}`,
|
|
2038
|
+
{
|
|
2039
|
+
...block2,
|
|
2040
|
+
sourceRepo: providerInfo
|
|
2041
|
+
}
|
|
2042
|
+
);
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
block = blocksMap.get(blockSpecifier);
|
|
2047
|
+
}
|
|
2048
|
+
if (!block) {
|
|
2049
|
+
program4.error(
|
|
2050
|
+
color9.red(`Invalid block! ${color9.bold(blockSpecifier)} does not exist!`)
|
|
2051
|
+
);
|
|
2052
|
+
}
|
|
2053
|
+
testingBlocksMapped.push({ name: blockSpecifier, block });
|
|
2054
|
+
}
|
|
2055
|
+
for (const { name: specifier, block } of testingBlocksMapped) {
|
|
2056
|
+
const providerInfo = block.sourceRepo;
|
|
2057
|
+
if (!options.verbose) {
|
|
2058
|
+
loading.start(`Setting up test file for ${color9.cyan(specifier)}`);
|
|
2059
|
+
}
|
|
2060
|
+
if (!block.tests) {
|
|
2061
|
+
loading.stop(`No tests found for ${color9.cyan(specifier)}`);
|
|
2062
|
+
continue;
|
|
2063
|
+
}
|
|
2064
|
+
const getSourceFile = async (filePath) => {
|
|
2065
|
+
const rawUrl = await providerInfo.provider.resolveRaw(providerInfo, filePath);
|
|
2066
|
+
const response = await fetch(rawUrl);
|
|
2067
|
+
if (!response.ok) {
|
|
2068
|
+
loading.stop(color9.red(`Error fetching ${color9.bold(rawUrl.href)}`));
|
|
2069
|
+
program4.error(color9.red(`There was an error trying to get ${specifier}`));
|
|
2070
|
+
}
|
|
2071
|
+
return await response.text();
|
|
2072
|
+
};
|
|
2073
|
+
verbose(`Downloading and copying test files for ${specifier}`);
|
|
2074
|
+
const testFiles = [];
|
|
2075
|
+
for (const testFile of block.files.filter((file) => isTestFile(file))) {
|
|
2076
|
+
const content = await getSourceFile(path8.join(block.directory, testFile));
|
|
2077
|
+
const destPath = path8.join(tempTestDirectory, testFile);
|
|
2078
|
+
fs10.writeFileSync(destPath, content);
|
|
2079
|
+
testFiles.push(destPath);
|
|
2080
|
+
}
|
|
2081
|
+
const directory = path8.join(config.path, block.category);
|
|
2082
|
+
let blockFilePath = path8.join(directory, `${block.name}`);
|
|
2083
|
+
blockFilePath = blockFilePath.replaceAll("\\", "/");
|
|
2084
|
+
verbose(`${color9.bold(specifier)} file path is ${color9.bold(blockFilePath)}`);
|
|
2085
|
+
const project = new Project2();
|
|
2086
|
+
for (const file of testFiles) {
|
|
2087
|
+
verbose(`Opening test file ${file}`);
|
|
2088
|
+
const tempFile = project.addSourceFileAtPath(file);
|
|
2089
|
+
for (const importDeclaration of tempFile.getImportDeclarations()) {
|
|
2090
|
+
const moduleSpecifier = importDeclaration.getModuleSpecifierValue();
|
|
2091
|
+
let newModuleSpecifier = void 0;
|
|
2092
|
+
if (moduleSpecifier.startsWith(".")) {
|
|
2093
|
+
if (block.subdirectory) {
|
|
2094
|
+
newModuleSpecifier = path8.join(
|
|
2095
|
+
"../",
|
|
2096
|
+
config.path,
|
|
2097
|
+
block.category,
|
|
2098
|
+
block.name,
|
|
2099
|
+
moduleSpecifier
|
|
2100
|
+
);
|
|
2101
|
+
} else {
|
|
2102
|
+
newModuleSpecifier = path8.join(
|
|
2103
|
+
"../",
|
|
2104
|
+
config.path,
|
|
2105
|
+
block.category,
|
|
2106
|
+
moduleSpecifier
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
if (newModuleSpecifier) {
|
|
2111
|
+
importDeclaration.setModuleSpecifier(newModuleSpecifier.replaceAll(/\\/g, "/"));
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
project.saveSync();
|
|
2116
|
+
verbose(`Completed ${color9.cyan.bold(specifier)} test file`);
|
|
2117
|
+
if (!options.verbose) {
|
|
2118
|
+
loading.stop(`Completed setup for ${color9.bold(specifier)}`);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
verbose("Beginning testing");
|
|
2122
|
+
const pm = await detect2({ cwd: process.cwd() });
|
|
2123
|
+
if (pm == null) {
|
|
2124
|
+
program4.error(color9.red("Could not detect package manager"));
|
|
2125
|
+
}
|
|
2126
|
+
const resolved = resolveCommand2(pm.agent, "execute", ["vitest", "run", tempTestDirectory]);
|
|
2127
|
+
if (resolved == null) {
|
|
2128
|
+
program4.error(color9.red(`Could not resolve add command for '${pm.agent}'.`));
|
|
2129
|
+
}
|
|
2130
|
+
const { command, args } = resolved;
|
|
2131
|
+
const testCommand = `${command} ${args.join(" ")}`;
|
|
2132
|
+
const testingProcess = execa2({
|
|
2133
|
+
cwd: process.cwd(),
|
|
2134
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
2135
|
+
})`${testCommand}`;
|
|
2136
|
+
const handler = (data) => console.info(data.toString());
|
|
2137
|
+
testingProcess.stdout.on("data", handler);
|
|
2138
|
+
testingProcess.stderr.on("data", handler);
|
|
2139
|
+
try {
|
|
2140
|
+
await testingProcess;
|
|
2141
|
+
cleanUp();
|
|
2142
|
+
outro5(color9.green("All done!"));
|
|
2143
|
+
} catch (err) {
|
|
2144
|
+
if (options.debug) {
|
|
2145
|
+
console.info(
|
|
2146
|
+
`${color9.bold("--debug")} flag provided. Skipping cleanup. Run '${color9.bold(
|
|
2147
|
+
testCommand
|
|
2148
|
+
)}' to retry tests.
|
|
2149
|
+
`
|
|
2150
|
+
);
|
|
2151
|
+
} else {
|
|
2152
|
+
cleanUp();
|
|
2153
|
+
}
|
|
2154
|
+
program4.error(color9.red(`Tests failed! Error ${err}`));
|
|
2155
|
+
}
|
|
2156
|
+
};
|
|
2157
|
+
|
|
2158
|
+
// src/index.ts
|
|
2159
|
+
var resolveRelativeToRoot = (p) => {
|
|
2160
|
+
const dirname = fileURLToPath(import.meta.url);
|
|
2161
|
+
return path9.join(dirname, "../..", p);
|
|
2162
|
+
};
|
|
2163
|
+
var { version, name, description, repository } = JSON.parse(
|
|
2164
|
+
fs11.readFileSync(resolveRelativeToRoot("package.json"), "utf-8")
|
|
2165
|
+
);
|
|
2166
|
+
var context = {
|
|
2167
|
+
package: {
|
|
2168
|
+
name,
|
|
2169
|
+
description,
|
|
2170
|
+
version,
|
|
2171
|
+
repository
|
|
2172
|
+
},
|
|
2173
|
+
resolveRelativeToRoot
|
|
2174
|
+
};
|
|
2175
|
+
program5.name(name).description(description).version(version).addCommand(add).addCommand(init).addCommand(test).addCommand(build).addCommand(diff);
|
|
2176
|
+
program5.parse();
|
|
2177
|
+
export {
|
|
2178
|
+
context
|
|
2179
|
+
};
|