create-bunli 0.5.4 → 0.6.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/dist/cli.js +574 -74
- package/dist/create-project.d.ts +24 -1
- package/dist/create.d.ts +16 -1
- package/dist/index.js +532 -63
- package/dist/templates/advanced/package.json +2 -1
- package/dist/templates/advanced/src/commands/config.ts +152 -70
- package/dist/templates/advanced/src/commands/serve.ts +2 -2
- package/dist/templates/advanced/src/commands/validate.ts +20 -25
- package/dist/templates/advanced/src/index.ts +33 -22
- package/dist/templates/advanced/src/utils/config.ts +5 -4
- package/dist/templates/monorepo/packages/core/src/commands/analyze.ts +9 -4
- package/dist/templates/monorepo/packages/core/src/commands/process.ts +10 -5
- package/package.json +5 -4
- package/templates/advanced/package.json +2 -1
- package/templates/advanced/src/commands/config.ts +152 -70
- package/templates/advanced/src/commands/serve.ts +2 -2
- package/templates/advanced/src/commands/validate.ts +20 -25
- package/templates/advanced/src/index.ts +33 -22
- package/templates/advanced/src/utils/config.ts +5 -4
- package/templates/monorepo/packages/core/src/commands/analyze.ts +9 -4
- package/templates/monorepo/packages/core/src/commands/process.ts +10 -5
package/dist/create-project.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BunliUtils } from '@bunli/utils';
|
|
2
2
|
import type { CreateOptions } from './types.js';
|
|
3
|
+
import { Result } from 'better-result';
|
|
3
4
|
interface CreateProjectOptions extends CreateOptions {
|
|
4
5
|
name: string;
|
|
5
6
|
dir: string;
|
|
@@ -9,5 +10,27 @@ interface CreateProjectOptions extends CreateOptions {
|
|
|
9
10
|
colors: BunliUtils['colors'];
|
|
10
11
|
shell: typeof Bun.$;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
+
declare const UserCancelledError_base: import("better-result").TaggedErrorClass<"UserCancelledError", {
|
|
14
|
+
message: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare class UserCancelledError extends UserCancelledError_base {
|
|
17
|
+
constructor(message: string);
|
|
18
|
+
}
|
|
19
|
+
declare const ShellCommandError_base: import("better-result").TaggedErrorClass<"ShellCommandError", {
|
|
20
|
+
message: string;
|
|
21
|
+
command: string;
|
|
22
|
+
output: string;
|
|
23
|
+
}>;
|
|
24
|
+
declare class ShellCommandError extends ShellCommandError_base {
|
|
25
|
+
constructor(command: string, output: string);
|
|
26
|
+
}
|
|
27
|
+
declare const TemplateProcessingError_base: import("better-result").TaggedErrorClass<"TemplateProcessingError", {
|
|
28
|
+
message: string;
|
|
29
|
+
cause: unknown;
|
|
30
|
+
}>;
|
|
31
|
+
declare class TemplateProcessingError extends TemplateProcessingError_base {
|
|
32
|
+
constructor(cause: unknown);
|
|
33
|
+
}
|
|
34
|
+
export type CreateProjectError = UserCancelledError | ShellCommandError | TemplateProcessingError;
|
|
35
|
+
export declare function createProject(options: CreateProjectOptions): Promise<Result<void, CreateProjectError>>;
|
|
13
36
|
export {};
|
package/dist/create.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { HandlerArgs } from '@bunli/core';
|
|
2
|
+
import { type CreateProjectError } from './create-project.js';
|
|
3
|
+
import { Result } from 'better-result';
|
|
2
4
|
interface CreateOptions {
|
|
3
5
|
name?: string;
|
|
4
6
|
template: string;
|
|
@@ -7,5 +9,18 @@ interface CreateOptions {
|
|
|
7
9
|
install: boolean;
|
|
8
10
|
offline?: boolean;
|
|
9
11
|
}
|
|
10
|
-
|
|
12
|
+
declare const InvalidProjectNameError_base: import("better-result").TaggedErrorClass<"InvalidProjectNameError", {
|
|
13
|
+
message: string;
|
|
14
|
+
}>;
|
|
15
|
+
declare class InvalidProjectNameError extends InvalidProjectNameError_base {
|
|
16
|
+
constructor(name: string);
|
|
17
|
+
}
|
|
18
|
+
declare const UserCancelledError_base: import("better-result").TaggedErrorClass<"UserCancelledError", {
|
|
19
|
+
message: string;
|
|
20
|
+
}>;
|
|
21
|
+
export declare class UserCancelledError extends UserCancelledError_base {
|
|
22
|
+
constructor(message: string);
|
|
23
|
+
}
|
|
24
|
+
export type CreateCommandError = InvalidProjectNameError | UserCancelledError | CreateProjectError;
|
|
25
|
+
export declare function create(context: HandlerArgs<CreateOptions>): Promise<Result<void, CreateCommandError>>;
|
|
11
26
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -8,10 +8,13 @@ async function processTemplate(options) {
|
|
|
8
8
|
let templateDir;
|
|
9
9
|
if (source.startsWith("/") || source.startsWith("./") || source.startsWith("../")) {
|
|
10
10
|
const sourceDir = source.startsWith("/") ? source : join(process.cwd(), source);
|
|
11
|
-
await Bun.spawn(["cp", "-r", sourceDir + "/.", dir], {
|
|
11
|
+
const copyExitCode = await Bun.spawn(["cp", "-r", sourceDir + "/.", dir], {
|
|
12
12
|
stdout: "inherit",
|
|
13
13
|
stderr: "inherit"
|
|
14
14
|
}).exited;
|
|
15
|
+
if (copyExitCode !== 0) {
|
|
16
|
+
throw new Error(`Failed to copy local template from ${sourceDir}`);
|
|
17
|
+
}
|
|
15
18
|
templateDir = dir;
|
|
16
19
|
} else {
|
|
17
20
|
const result = await downloadTemplate(source, {
|
|
@@ -44,12 +47,13 @@ async function loadTemplateManifest(dir) {
|
|
|
44
47
|
const content = await file.text();
|
|
45
48
|
if (path.endsWith(".json")) {
|
|
46
49
|
const manifest = JSON.parse(content);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
const removeExitCode = await Bun.spawn(["rm", "-f", path], {
|
|
51
|
+
stdout: "ignore",
|
|
52
|
+
stderr: "ignore"
|
|
53
|
+
}).exited;
|
|
54
|
+
if (removeExitCode !== 0) {
|
|
55
|
+
console.warn(`Warning: failed to remove template manifest ${path}`);
|
|
56
|
+
}
|
|
53
57
|
return manifest;
|
|
54
58
|
}
|
|
55
59
|
}
|
|
@@ -114,7 +118,10 @@ async function runPostInstallHooks(dir, hooks) {
|
|
|
114
118
|
stdout: "inherit",
|
|
115
119
|
stderr: "inherit"
|
|
116
120
|
});
|
|
117
|
-
await proc.exited;
|
|
121
|
+
const exitCode = await proc.exited;
|
|
122
|
+
if (exitCode !== 0) {
|
|
123
|
+
throw new Error(`Post-install hook failed: ${hook}`);
|
|
124
|
+
}
|
|
118
125
|
}
|
|
119
126
|
}
|
|
120
127
|
function resolveTemplateSource(template) {
|
|
@@ -145,79 +152,541 @@ async function isLocalTemplate(template) {
|
|
|
145
152
|
return await Bun.file(join(bundledPath, "package.json")).exists();
|
|
146
153
|
}
|
|
147
154
|
|
|
155
|
+
// ../../node_modules/better-result/dist/index.mjs
|
|
156
|
+
function dual(arity, body) {
|
|
157
|
+
if (arity === 2)
|
|
158
|
+
return (...args) => {
|
|
159
|
+
if (args.length >= 2)
|
|
160
|
+
return body(args[0], args[1]);
|
|
161
|
+
return (self) => body(self, args[0]);
|
|
162
|
+
};
|
|
163
|
+
if (arity === 3)
|
|
164
|
+
return (...args) => {
|
|
165
|
+
if (args.length >= 3)
|
|
166
|
+
return body(args[0], args[1], args[2]);
|
|
167
|
+
return (self) => body(self, args[0], args[1]);
|
|
168
|
+
};
|
|
169
|
+
if (arity === 4)
|
|
170
|
+
return (...args) => {
|
|
171
|
+
if (args.length >= 4)
|
|
172
|
+
return body(args[0], args[1], args[2], args[3]);
|
|
173
|
+
return (self) => body(self, args[0], args[1], args[2]);
|
|
174
|
+
};
|
|
175
|
+
return (...args) => {
|
|
176
|
+
if (args.length >= arity)
|
|
177
|
+
return body(...args);
|
|
178
|
+
return (self) => body(self, ...args);
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
var serializeCause = (cause) => {
|
|
182
|
+
if (cause instanceof Error)
|
|
183
|
+
return {
|
|
184
|
+
name: cause.name,
|
|
185
|
+
message: cause.message,
|
|
186
|
+
stack: cause.stack
|
|
187
|
+
};
|
|
188
|
+
return cause;
|
|
189
|
+
};
|
|
190
|
+
var isAnyTaggedError = (value) => {
|
|
191
|
+
return value instanceof Error && "_tag" in value && typeof value._tag === "string";
|
|
192
|
+
};
|
|
193
|
+
var TaggedError = Object.assign((tag) => () => {
|
|
194
|
+
|
|
195
|
+
class Base extends Error {
|
|
196
|
+
_tag = tag;
|
|
197
|
+
static is(value) {
|
|
198
|
+
return value instanceof Base;
|
|
199
|
+
}
|
|
200
|
+
constructor(args) {
|
|
201
|
+
const message = args && "message" in args && typeof args.message === "string" ? args.message : undefined;
|
|
202
|
+
const cause = args && "cause" in args ? args.cause : undefined;
|
|
203
|
+
super(message, cause !== undefined ? { cause } : undefined);
|
|
204
|
+
if (args)
|
|
205
|
+
Object.assign(this, args);
|
|
206
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
207
|
+
this.name = tag;
|
|
208
|
+
if (cause instanceof Error && cause.stack) {
|
|
209
|
+
const indented = cause.stack.replace(/\n/g, `
|
|
210
|
+
`);
|
|
211
|
+
this.stack = `${this.stack}
|
|
212
|
+
Caused by: ${indented}`;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
toJSON() {
|
|
216
|
+
return {
|
|
217
|
+
...this,
|
|
218
|
+
_tag: this._tag,
|
|
219
|
+
name: this.name,
|
|
220
|
+
message: this.message,
|
|
221
|
+
cause: serializeCause(this.cause),
|
|
222
|
+
stack: this.stack
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return Base;
|
|
227
|
+
}, { is: isAnyTaggedError });
|
|
228
|
+
var matchError = dual(2, (err$1, handlers) => {
|
|
229
|
+
const handler = handlers[err$1._tag];
|
|
230
|
+
return handler(err$1);
|
|
231
|
+
});
|
|
232
|
+
var matchErrorPartial = dual(3, (err$1, handlers, fallback) => {
|
|
233
|
+
const handler = handlers[err$1._tag];
|
|
234
|
+
if (typeof handler === "function")
|
|
235
|
+
return handler(err$1);
|
|
236
|
+
return fallback(err$1);
|
|
237
|
+
});
|
|
238
|
+
var UnhandledException = class extends TaggedError("UnhandledException")() {
|
|
239
|
+
constructor(args) {
|
|
240
|
+
const message = args.cause instanceof Error ? `Unhandled exception: ${args.cause.message}` : `Unhandled exception: ${String(args.cause)}`;
|
|
241
|
+
super({
|
|
242
|
+
message,
|
|
243
|
+
cause: args.cause
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
var Panic = class extends TaggedError("Panic")() {
|
|
248
|
+
};
|
|
249
|
+
var ResultDeserializationError = class extends TaggedError("ResultDeserializationError")() {
|
|
250
|
+
constructor(args) {
|
|
251
|
+
super({
|
|
252
|
+
message: `Failed to deserialize value as Result: expected { status: "ok", value } or { status: "error", error }`,
|
|
253
|
+
value: args.value
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
var panic = (message, cause) => {
|
|
258
|
+
throw new Panic({
|
|
259
|
+
message,
|
|
260
|
+
cause
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
var tryOrPanic = (fn, message) => {
|
|
264
|
+
try {
|
|
265
|
+
return fn();
|
|
266
|
+
} catch (cause) {
|
|
267
|
+
throw panic(message, cause);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
var tryOrPanicAsync = async (fn, message) => {
|
|
271
|
+
try {
|
|
272
|
+
return await fn();
|
|
273
|
+
} catch (cause) {
|
|
274
|
+
throw panic(message, cause);
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
var Ok = class Ok2 {
|
|
278
|
+
status = "ok";
|
|
279
|
+
constructor(value) {
|
|
280
|
+
this.value = value;
|
|
281
|
+
}
|
|
282
|
+
isOk() {
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
isErr() {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
map(fn) {
|
|
289
|
+
return tryOrPanic(() => new Ok2(fn(this.value)), "map callback threw");
|
|
290
|
+
}
|
|
291
|
+
mapError(_fn) {
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
andThen(fn) {
|
|
295
|
+
return tryOrPanic(() => fn(this.value), "andThen callback threw");
|
|
296
|
+
}
|
|
297
|
+
andThenAsync(fn) {
|
|
298
|
+
return tryOrPanicAsync(() => fn(this.value), "andThenAsync callback threw");
|
|
299
|
+
}
|
|
300
|
+
match(handlers) {
|
|
301
|
+
return tryOrPanic(() => handlers.ok(this.value), "match ok handler threw");
|
|
302
|
+
}
|
|
303
|
+
unwrap(_message) {
|
|
304
|
+
return this.value;
|
|
305
|
+
}
|
|
306
|
+
unwrapOr(_fallback) {
|
|
307
|
+
return this.value;
|
|
308
|
+
}
|
|
309
|
+
tap(fn) {
|
|
310
|
+
return tryOrPanic(() => {
|
|
311
|
+
fn(this.value);
|
|
312
|
+
return this;
|
|
313
|
+
}, "tap callback threw");
|
|
314
|
+
}
|
|
315
|
+
tapAsync(fn) {
|
|
316
|
+
return tryOrPanicAsync(async () => {
|
|
317
|
+
await fn(this.value);
|
|
318
|
+
return this;
|
|
319
|
+
}, "tapAsync callback threw");
|
|
320
|
+
}
|
|
321
|
+
*[Symbol.iterator]() {
|
|
322
|
+
return this.value;
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
var Err = class Err2 {
|
|
326
|
+
status = "error";
|
|
327
|
+
constructor(error) {
|
|
328
|
+
this.error = error;
|
|
329
|
+
}
|
|
330
|
+
isOk() {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
isErr() {
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
map(_fn) {
|
|
337
|
+
return this;
|
|
338
|
+
}
|
|
339
|
+
mapError(fn) {
|
|
340
|
+
return tryOrPanic(() => new Err2(fn(this.error)), "mapError callback threw");
|
|
341
|
+
}
|
|
342
|
+
andThen(_fn) {
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
andThenAsync(_fn) {
|
|
346
|
+
return Promise.resolve(this);
|
|
347
|
+
}
|
|
348
|
+
match(handlers) {
|
|
349
|
+
return tryOrPanic(() => handlers.err(this.error), "match err handler threw");
|
|
350
|
+
}
|
|
351
|
+
unwrap(message) {
|
|
352
|
+
return panic(message ?? `Unwrap called on Err: ${String(this.error)}`, this.error);
|
|
353
|
+
}
|
|
354
|
+
unwrapOr(fallback) {
|
|
355
|
+
return fallback;
|
|
356
|
+
}
|
|
357
|
+
tap(_fn) {
|
|
358
|
+
return this;
|
|
359
|
+
}
|
|
360
|
+
tapAsync(_fn) {
|
|
361
|
+
return Promise.resolve(this);
|
|
362
|
+
}
|
|
363
|
+
*[Symbol.iterator]() {
|
|
364
|
+
yield this;
|
|
365
|
+
return panic("Unreachable: Err yielded in Result.gen but generator continued", this.error);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
function ok(value) {
|
|
369
|
+
return new Ok(value);
|
|
370
|
+
}
|
|
371
|
+
var isOk = (result) => {
|
|
372
|
+
return result.status === "ok";
|
|
373
|
+
};
|
|
374
|
+
var err = (error) => new Err(error);
|
|
375
|
+
var isError = (result) => {
|
|
376
|
+
return result.status === "error";
|
|
377
|
+
};
|
|
378
|
+
var tryFn = (options, config) => {
|
|
379
|
+
const execute = () => {
|
|
380
|
+
if (typeof options === "function")
|
|
381
|
+
try {
|
|
382
|
+
return ok(options());
|
|
383
|
+
} catch (cause) {
|
|
384
|
+
return err(new UnhandledException({ cause }));
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
return ok(options.try());
|
|
388
|
+
} catch (originalCause) {
|
|
389
|
+
try {
|
|
390
|
+
return err(options.catch(originalCause));
|
|
391
|
+
} catch (catchHandlerError) {
|
|
392
|
+
throw panic("Result.try catch handler threw", catchHandlerError);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
const times = config?.retry?.times ?? 0;
|
|
397
|
+
let result = execute();
|
|
398
|
+
for (let retry = 0;retry < times && result.status === "error"; retry++)
|
|
399
|
+
result = execute();
|
|
400
|
+
return result;
|
|
401
|
+
};
|
|
402
|
+
var tryPromise = async (options, config) => {
|
|
403
|
+
const execute = async () => {
|
|
404
|
+
if (typeof options === "function")
|
|
405
|
+
try {
|
|
406
|
+
return ok(await options());
|
|
407
|
+
} catch (cause) {
|
|
408
|
+
return err(new UnhandledException({ cause }));
|
|
409
|
+
}
|
|
410
|
+
try {
|
|
411
|
+
return ok(await options.try());
|
|
412
|
+
} catch (originalCause) {
|
|
413
|
+
try {
|
|
414
|
+
return err(await options.catch(originalCause));
|
|
415
|
+
} catch (catchHandlerError) {
|
|
416
|
+
throw panic("Result.tryPromise catch handler threw", catchHandlerError);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
const retry = config?.retry;
|
|
421
|
+
if (!retry)
|
|
422
|
+
return execute();
|
|
423
|
+
const getDelay = (retryAttempt) => {
|
|
424
|
+
switch (retry.backoff) {
|
|
425
|
+
case "constant":
|
|
426
|
+
return retry.delayMs;
|
|
427
|
+
case "linear":
|
|
428
|
+
return retry.delayMs * (retryAttempt + 1);
|
|
429
|
+
case "exponential":
|
|
430
|
+
return retry.delayMs * 2 ** retryAttempt;
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
434
|
+
let result = await execute();
|
|
435
|
+
const shouldRetryFn = retry.shouldRetry ?? (() => true);
|
|
436
|
+
for (let attempt = 0;attempt < retry.times; attempt++) {
|
|
437
|
+
if (result.status !== "error")
|
|
438
|
+
break;
|
|
439
|
+
const error = result.error;
|
|
440
|
+
if (!tryOrPanic(() => shouldRetryFn(error), "shouldRetry predicate threw"))
|
|
441
|
+
break;
|
|
442
|
+
await sleep(getDelay(attempt));
|
|
443
|
+
result = await execute();
|
|
444
|
+
}
|
|
445
|
+
return result;
|
|
446
|
+
};
|
|
447
|
+
var map = dual(2, (result, fn) => {
|
|
448
|
+
return result.map(fn);
|
|
449
|
+
});
|
|
450
|
+
var mapError = dual(2, (result, fn) => {
|
|
451
|
+
return result.mapError(fn);
|
|
452
|
+
});
|
|
453
|
+
var andThen = dual(2, (result, fn) => {
|
|
454
|
+
return result.andThen(fn);
|
|
455
|
+
});
|
|
456
|
+
var andThenAsync = dual(2, (result, fn) => {
|
|
457
|
+
return result.andThenAsync(fn);
|
|
458
|
+
});
|
|
459
|
+
var match = dual(2, (result, handlers) => {
|
|
460
|
+
return result.match(handlers);
|
|
461
|
+
});
|
|
462
|
+
var tap = dual(2, (result, fn) => {
|
|
463
|
+
return result.tap(fn);
|
|
464
|
+
});
|
|
465
|
+
var tapAsync = dual(2, (result, fn) => {
|
|
466
|
+
return result.tapAsync(fn);
|
|
467
|
+
});
|
|
468
|
+
var unwrap = (result, message) => {
|
|
469
|
+
return result.unwrap(message);
|
|
470
|
+
};
|
|
471
|
+
function assertIsResult(value) {
|
|
472
|
+
if (value !== null && typeof value === "object" && "status" in value && (value.status === "ok" || value.status === "error"))
|
|
473
|
+
return;
|
|
474
|
+
return panic("Result.gen body must return Result.ok() or Result.err(), got: " + (value === null ? "null" : typeof value === "object" ? JSON.stringify(value) : String(value)));
|
|
475
|
+
}
|
|
476
|
+
var unwrapOr = dual(2, (result, fallback) => {
|
|
477
|
+
return result.unwrapOr(fallback);
|
|
478
|
+
});
|
|
479
|
+
var gen = (body, thisArg) => {
|
|
480
|
+
const iterator = body.call(thisArg);
|
|
481
|
+
if (Symbol.asyncIterator in iterator)
|
|
482
|
+
return (async () => {
|
|
483
|
+
const asyncIter = iterator;
|
|
484
|
+
let state$1;
|
|
485
|
+
try {
|
|
486
|
+
state$1 = await asyncIter.next();
|
|
487
|
+
} catch (cause) {
|
|
488
|
+
throw panic("generator body threw", cause);
|
|
489
|
+
}
|
|
490
|
+
assertIsResult(state$1.value);
|
|
491
|
+
if (!state$1.done)
|
|
492
|
+
try {
|
|
493
|
+
await asyncIter.return?.(undefined);
|
|
494
|
+
} catch (cause) {
|
|
495
|
+
throw panic("generator cleanup threw", cause);
|
|
496
|
+
}
|
|
497
|
+
return state$1.value;
|
|
498
|
+
})();
|
|
499
|
+
const syncIter = iterator;
|
|
500
|
+
let state;
|
|
501
|
+
try {
|
|
502
|
+
state = syncIter.next();
|
|
503
|
+
} catch (cause) {
|
|
504
|
+
throw panic("generator body threw", cause);
|
|
505
|
+
}
|
|
506
|
+
assertIsResult(state.value);
|
|
507
|
+
if (!state.done)
|
|
508
|
+
try {
|
|
509
|
+
syncIter.return?.(undefined);
|
|
510
|
+
} catch (cause) {
|
|
511
|
+
throw panic("generator cleanup threw", cause);
|
|
512
|
+
}
|
|
513
|
+
return state.value;
|
|
514
|
+
};
|
|
515
|
+
async function* resultAwait(promise) {
|
|
516
|
+
return yield* await promise;
|
|
517
|
+
}
|
|
518
|
+
function isSerializedResult(obj) {
|
|
519
|
+
return obj !== null && typeof obj === "object" && "status" in obj && (obj.status === "ok" && ("value" in obj) || obj.status === "error" && ("error" in obj));
|
|
520
|
+
}
|
|
521
|
+
var serialize = (result) => {
|
|
522
|
+
return result.status === "ok" ? {
|
|
523
|
+
status: "ok",
|
|
524
|
+
value: result.value
|
|
525
|
+
} : {
|
|
526
|
+
status: "error",
|
|
527
|
+
error: result.error
|
|
528
|
+
};
|
|
529
|
+
};
|
|
530
|
+
var deserialize = (value) => {
|
|
531
|
+
if (isSerializedResult(value))
|
|
532
|
+
return value.status === "ok" ? new Ok(value.value) : new Err(value.error);
|
|
533
|
+
return err(new ResultDeserializationError({ value }));
|
|
534
|
+
};
|
|
535
|
+
var hydrate = (value) => {
|
|
536
|
+
return deserialize(value);
|
|
537
|
+
};
|
|
538
|
+
var partition = (results) => {
|
|
539
|
+
const oks = [];
|
|
540
|
+
const errs = [];
|
|
541
|
+
for (const r of results)
|
|
542
|
+
if (r.status === "ok")
|
|
543
|
+
oks.push(r.value);
|
|
544
|
+
else
|
|
545
|
+
errs.push(r.error);
|
|
546
|
+
return [oks, errs];
|
|
547
|
+
};
|
|
548
|
+
var flatten = (result) => {
|
|
549
|
+
if (result.status === "ok")
|
|
550
|
+
return result.value;
|
|
551
|
+
return result;
|
|
552
|
+
};
|
|
553
|
+
var Result = {
|
|
554
|
+
ok,
|
|
555
|
+
isOk,
|
|
556
|
+
err,
|
|
557
|
+
isError,
|
|
558
|
+
try: tryFn,
|
|
559
|
+
tryPromise,
|
|
560
|
+
map,
|
|
561
|
+
mapError,
|
|
562
|
+
andThen,
|
|
563
|
+
andThenAsync,
|
|
564
|
+
match,
|
|
565
|
+
tap,
|
|
566
|
+
tapAsync,
|
|
567
|
+
unwrap,
|
|
568
|
+
unwrapOr,
|
|
569
|
+
gen,
|
|
570
|
+
await: resultAwait,
|
|
571
|
+
serialize,
|
|
572
|
+
deserialize,
|
|
573
|
+
hydrate,
|
|
574
|
+
partition,
|
|
575
|
+
flatten
|
|
576
|
+
};
|
|
577
|
+
|
|
148
578
|
// src/create-project.ts
|
|
579
|
+
var toErrorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
580
|
+
var tryAsync = (fn, mapError2) => Result.tryPromise({ try: fn, catch: mapError2 });
|
|
581
|
+
|
|
582
|
+
class UserCancelledError extends TaggedError("UserCancelledError")() {
|
|
583
|
+
constructor(message) {
|
|
584
|
+
super({ message });
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
class ShellCommandError extends TaggedError("ShellCommandError")() {
|
|
589
|
+
constructor(command, output) {
|
|
590
|
+
super({
|
|
591
|
+
message: `Command failed (${command}): ${output}`,
|
|
592
|
+
command,
|
|
593
|
+
output
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
class TemplateProcessingError extends TaggedError("TemplateProcessingError")() {
|
|
599
|
+
constructor(cause) {
|
|
600
|
+
super({ message: `Failed to process template: ${toErrorMessage(cause)}`, cause });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
149
603
|
async function createProject(options) {
|
|
150
604
|
const { name, dir, template, git, install, prompt, spinner, colors, shell, offline } = options;
|
|
151
|
-
|
|
152
|
-
|
|
605
|
+
const directoryCheck = await shell`test -d ${dir}`.nothrow();
|
|
606
|
+
if (directoryCheck.exitCode === 0) {
|
|
153
607
|
const overwrite = await prompt.confirm(`Directory ${dir} already exists. Overwrite?`, { default: false });
|
|
154
608
|
if (!overwrite) {
|
|
155
|
-
|
|
156
|
-
|
|
609
|
+
return Result.err(new UserCancelledError("Cancelled"));
|
|
610
|
+
}
|
|
611
|
+
const removeDirectory = await shell`rm -rf ${dir}`.nothrow();
|
|
612
|
+
if (removeDirectory.exitCode !== 0) {
|
|
613
|
+
return Result.err(new ShellCommandError(`rm -rf ${dir}`, removeDirectory.stderr.toString().trim()));
|
|
157
614
|
}
|
|
158
|
-
|
|
159
|
-
} catch {}
|
|
615
|
+
}
|
|
160
616
|
const spin = spinner("Creating project structure...");
|
|
161
617
|
spin.start();
|
|
162
|
-
await shell`mkdir -p ${dir}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
618
|
+
const mkdirResult = await shell`mkdir -p ${dir}`.nothrow();
|
|
619
|
+
if (mkdirResult.exitCode !== 0) {
|
|
620
|
+
spin.fail("Failed to create project directory");
|
|
621
|
+
return Result.err(new ShellCommandError(`mkdir -p ${dir}`, mkdirResult.stderr.toString().trim()));
|
|
622
|
+
}
|
|
623
|
+
let templateSource = template;
|
|
624
|
+
if (await isLocalTemplate(template)) {
|
|
625
|
+
templateSource = getBundledTemplatePath(template);
|
|
626
|
+
} else {
|
|
627
|
+
templateSource = resolveTemplateSource(template);
|
|
628
|
+
}
|
|
629
|
+
const templateResult = await tryAsync(() => processTemplate({
|
|
630
|
+
source: templateSource,
|
|
631
|
+
dir,
|
|
632
|
+
offline,
|
|
633
|
+
variables: {
|
|
634
|
+
name,
|
|
635
|
+
version: "0.1.0",
|
|
636
|
+
description: "A CLI built with Bunli",
|
|
637
|
+
author: "",
|
|
638
|
+
license: "MIT",
|
|
639
|
+
year: new Date().getFullYear().toString()
|
|
169
640
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
try {
|
|
188
|
-
await shell`cd ${dir} && git init`.quiet();
|
|
189
|
-
await shell`cd ${dir} && git add .`.quiet();
|
|
190
|
-
await shell`cd ${dir} && git commit -m "feat: initialize ${name} CLI project with Bunli
|
|
641
|
+
}), (cause) => new TemplateProcessingError(cause));
|
|
642
|
+
if (Result.isError(templateResult)) {
|
|
643
|
+
spin.fail("Failed to create project");
|
|
644
|
+
console.error(colors.red(templateResult.error.message));
|
|
645
|
+
const cleanup = await shell`rm -rf ${dir}`.nothrow();
|
|
646
|
+
if (cleanup.exitCode !== 0) {
|
|
647
|
+
console.error(colors.yellow(`Warning: cleanup failed: ${cleanup.stderr.toString().trim()}`));
|
|
648
|
+
}
|
|
649
|
+
return templateResult;
|
|
650
|
+
}
|
|
651
|
+
spin.succeed("Project structure created");
|
|
652
|
+
if (git) {
|
|
653
|
+
const gitSpin = spinner("Initializing git repository...");
|
|
654
|
+
gitSpin.start();
|
|
655
|
+
const gitInit = await shell`cd ${dir} && git init`.nothrow();
|
|
656
|
+
const gitAdd = await shell`cd ${dir} && git add .`.nothrow();
|
|
657
|
+
const gitCommit = await shell`cd ${dir} && git commit -m "feat: initialize ${name} CLI project with Bunli
|
|
191
658
|
|
|
192
659
|
- Generated using create-bunli template
|
|
193
660
|
- Includes basic CLI structure with commands directory
|
|
194
661
|
- Configured with Bunli build system and TypeScript
|
|
195
|
-
- Ready for development with bun run dev"
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
662
|
+
- Ready for development with bun run dev"`.nothrow();
|
|
663
|
+
if (gitInit.exitCode === 0 && gitAdd.exitCode === 0 && gitCommit.exitCode === 0) {
|
|
664
|
+
gitSpin.succeed("Git repository initialized");
|
|
665
|
+
} else {
|
|
666
|
+
gitSpin.fail("Failed to initialize git repository");
|
|
667
|
+
const output = [gitInit.stderr, gitAdd.stderr, gitCommit.stderr].map((value) => value.toString().trim()).filter(Boolean).join(`
|
|
668
|
+
`);
|
|
669
|
+
if (output) {
|
|
670
|
+
console.error(colors.dim(` ${output}`));
|
|
200
671
|
}
|
|
201
672
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
673
|
+
}
|
|
674
|
+
if (install) {
|
|
675
|
+
const installSpin = spinner("Installing dependencies...");
|
|
676
|
+
installSpin.start();
|
|
677
|
+
const installResult = await shell`cd ${dir} && bun install`.nothrow();
|
|
678
|
+
if (installResult.exitCode === 0) {
|
|
679
|
+
installSpin.succeed("Dependencies installed");
|
|
680
|
+
} else {
|
|
681
|
+
installSpin.fail("Failed to install dependencies");
|
|
682
|
+
console.error(colors.dim(" You can install them manually by running: bun install"));
|
|
683
|
+
const errorOutput = installResult.stderr.toString().trim();
|
|
684
|
+
if (errorOutput) {
|
|
685
|
+
console.error(colors.dim(` ${errorOutput}`));
|
|
211
686
|
}
|
|
212
687
|
}
|
|
213
|
-
} catch (error) {
|
|
214
|
-
spin.fail("Failed to create project");
|
|
215
|
-
console.error(colors.red(`Error: ${error}`));
|
|
216
|
-
try {
|
|
217
|
-
await shell`rm -rf ${dir}`.quiet();
|
|
218
|
-
} catch {}
|
|
219
|
-
process.exit(1);
|
|
220
688
|
}
|
|
689
|
+
return Result.ok(undefined);
|
|
221
690
|
}
|
|
222
691
|
|
|
223
692
|
// src/index.ts
|