@multiplatform.one/cli 2.1.6 → 2.1.8
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/lib/bin/multiplatformOne.mjs +441 -219
- package/package.json +20 -19
- package/src/bin/multiplatformOne.ts +407 -142
- package/src/types.ts +4 -4
- package/types/.tsbuildinfo +1 -0
- package/types/bin/multiplatformOne.d.ts +2 -0
- package/types/bin/multiplatformOne.d.ts.map +1 -0
- package/types/index.d.ts +2 -0
- package/types/index.d.ts.map +1 -0
- package/types/types.d.ts +28 -0
- package/types/types.d.ts.map +1 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/*
|
|
2
2
|
* File: /src/bin/multiplatformOne.ts
|
|
3
3
|
* Project: @multiplatform.one/cli
|
|
4
|
-
* File Created:
|
|
4
|
+
* File Created: 10-01-2025 21:02:36
|
|
5
5
|
* Author: Clay Risser
|
|
6
6
|
* -----
|
|
7
|
-
* BitSpur (c) Copyright 2021 -
|
|
7
|
+
* BitSpur (c) Copyright 2021 - 2025
|
|
8
8
|
*
|
|
9
9
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
10
|
* you may not use this file except in compliance with the License.
|
|
@@ -24,23 +24,34 @@ import fs from "node:fs/promises";
|
|
|
24
24
|
import os from "node:os";
|
|
25
25
|
import path from "node:path";
|
|
26
26
|
import { fileURLToPath } from "node:url";
|
|
27
|
-
import
|
|
27
|
+
import {
|
|
28
|
+
formatServiceList,
|
|
29
|
+
projectRoot,
|
|
30
|
+
waitForApi,
|
|
31
|
+
waitForFrappe,
|
|
32
|
+
waitForKeycloak,
|
|
33
|
+
waitForPostgres,
|
|
34
|
+
waitServices,
|
|
35
|
+
} from "@multiplatform.one/utils/dev";
|
|
28
36
|
import { program } from "commander";
|
|
29
37
|
import dotenv from "dotenv";
|
|
30
38
|
import { execa } from "execa";
|
|
31
39
|
import inquirer from "inquirer";
|
|
32
40
|
import ora from "ora";
|
|
33
|
-
import pg from "pg";
|
|
34
41
|
import type { CookieCutterConfig } from "../types";
|
|
35
42
|
|
|
36
|
-
const
|
|
43
|
+
const availableServices = ["api", "frappe", "solana", "ethereum", "sui"];
|
|
44
|
+
const defaultDotenvPath = path.resolve(projectRoot, ".env");
|
|
37
45
|
const availablePlatforms = [
|
|
38
46
|
"electron",
|
|
39
47
|
"expo",
|
|
40
48
|
"keycloak",
|
|
41
|
-
"
|
|
49
|
+
"one",
|
|
42
50
|
"storybook",
|
|
43
51
|
"storybook-expo",
|
|
52
|
+
"vocs",
|
|
53
|
+
"vscode",
|
|
54
|
+
"webext",
|
|
44
55
|
];
|
|
45
56
|
|
|
46
57
|
process.env.COOKIECUTTER = `sh ${path.resolve(
|
|
@@ -72,13 +83,12 @@ program
|
|
|
72
83
|
"branch, tag or commit to checkout",
|
|
73
84
|
"main",
|
|
74
85
|
)
|
|
75
|
-
.option("-p, --platforms <platforms>", "platforms to
|
|
76
|
-
.option("-
|
|
86
|
+
.option("-p, --platforms <platforms>", "platforms to use")
|
|
87
|
+
.option("-s, --services <services>", "services to use")
|
|
77
88
|
.argument("[name]", "the name of the project", "")
|
|
78
89
|
.description("init multiplatform.one")
|
|
79
90
|
.action(async (name, options) => {
|
|
80
|
-
let {
|
|
81
|
-
|
|
91
|
+
let { services, platforms } = options;
|
|
82
92
|
if (
|
|
83
93
|
(
|
|
84
94
|
await execa("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
@@ -101,19 +111,18 @@ program
|
|
|
101
111
|
])
|
|
102
112
|
).name;
|
|
103
113
|
}
|
|
104
|
-
if (!
|
|
105
|
-
const
|
|
114
|
+
if (!services) {
|
|
115
|
+
const servicesResult = (
|
|
106
116
|
await inquirer.prompt([
|
|
107
117
|
{
|
|
108
|
-
message: "What
|
|
109
|
-
name: "
|
|
118
|
+
message: "What services are you using?",
|
|
119
|
+
name: "services",
|
|
110
120
|
type: "checkbox",
|
|
111
|
-
choices:
|
|
121
|
+
choices: availableServices.map((name) => ({ name })),
|
|
112
122
|
},
|
|
113
123
|
])
|
|
114
|
-
).
|
|
115
|
-
|
|
116
|
-
backends = backendsResult.join(",");
|
|
124
|
+
).services;
|
|
125
|
+
services = servicesResult.join(",");
|
|
117
126
|
}
|
|
118
127
|
if (!platforms) {
|
|
119
128
|
const platformsResult = (
|
|
@@ -129,7 +138,7 @@ program
|
|
|
129
138
|
platforms = platformsResult.join(",");
|
|
130
139
|
}
|
|
131
140
|
const cookieCutterConfig: CookieCutterConfig = {
|
|
132
|
-
default_context: { name, platforms,
|
|
141
|
+
default_context: { name, platforms, services },
|
|
133
142
|
};
|
|
134
143
|
const cookieCutterConfigFile = path.join(
|
|
135
144
|
await fs.mkdtemp(path.join(os.tmpdir(), "multiplatform-")),
|
|
@@ -177,20 +186,20 @@ program
|
|
|
177
186
|
"main",
|
|
178
187
|
)
|
|
179
188
|
.option("-p, --platforms <platforms>", "platforms to keep")
|
|
180
|
-
.option("-
|
|
189
|
+
.option("-s, --services <services>", "services to keep")
|
|
181
190
|
.description("update multiplatform.one")
|
|
182
191
|
.action(async (options) => {
|
|
183
|
-
let {
|
|
184
|
-
if (!
|
|
185
|
-
const
|
|
192
|
+
let { services, platforms } = options;
|
|
193
|
+
if (!services) {
|
|
194
|
+
const servicesResult = await inquirer.prompt([
|
|
186
195
|
{
|
|
187
|
-
message: "What
|
|
188
|
-
name: "
|
|
196
|
+
message: "What services are you using?",
|
|
197
|
+
name: "services",
|
|
189
198
|
type: "checkbox",
|
|
190
|
-
choices:
|
|
199
|
+
choices: availableServices.map((name) => ({ name })),
|
|
191
200
|
},
|
|
192
201
|
]);
|
|
193
|
-
|
|
202
|
+
services = servicesResult.services.join(",");
|
|
194
203
|
}
|
|
195
204
|
if (!platforms) {
|
|
196
205
|
const platformsResult = await inquirer.prompt([
|
|
@@ -243,7 +252,9 @@ program
|
|
|
243
252
|
await fs.readFile(path.resolve(projectRoot, "package.json"), "utf8"),
|
|
244
253
|
)?.name;
|
|
245
254
|
if (name) {
|
|
246
|
-
cookieCutterConfig = {
|
|
255
|
+
cookieCutterConfig = {
|
|
256
|
+
default_context: { name, services, platforms },
|
|
257
|
+
};
|
|
247
258
|
}
|
|
248
259
|
}
|
|
249
260
|
if (!cookieCutterConfig) throw new Error("not a multiplatform.one project");
|
|
@@ -281,7 +292,6 @@ program
|
|
|
281
292
|
}
|
|
282
293
|
});
|
|
283
294
|
|
|
284
|
-
const waitServices = ["api", "frappe", "postgres", "keycloak"];
|
|
285
295
|
program
|
|
286
296
|
.command("wait")
|
|
287
297
|
.description("wait for a service to be ready")
|
|
@@ -293,136 +303,391 @@ program
|
|
|
293
303
|
`the services to wait for (${waitServices.join(", ")})`,
|
|
294
304
|
)
|
|
295
305
|
.action(async (servicesString, options) => {
|
|
296
|
-
dotenv.config({ path: options.dotenv });
|
|
306
|
+
dotenv.config({ path: options.dotenv || defaultDotenvPath });
|
|
297
307
|
const interval = Number.parseInt(options.interval);
|
|
298
308
|
const timeout = Number.parseInt(options.timeout);
|
|
299
309
|
const services: string[] = servicesString.split(",");
|
|
300
|
-
const unreadyServices = [...services];
|
|
301
|
-
const spinner = ora(
|
|
302
|
-
`waiting for ${formatServiceList(unreadyServices)}`,
|
|
303
|
-
).start();
|
|
304
|
-
function updateSpinner(readyService: string) {
|
|
305
|
-
unreadyServices.splice(unreadyServices.indexOf(readyService), 1);
|
|
306
|
-
spinner.stop();
|
|
307
|
-
spinner.succeed(`${readyService} is ready`);
|
|
308
|
-
if (!unreadyServices.length) return;
|
|
309
|
-
spinner.start(`waiting for ${formatServiceList(unreadyServices)}`);
|
|
310
|
-
}
|
|
311
|
-
let timeoutId: NodeJS.Timeout;
|
|
312
310
|
try {
|
|
313
|
-
await
|
|
314
|
-
Promise.all(
|
|
315
|
-
services.map(async (service) => {
|
|
316
|
-
switch (service) {
|
|
317
|
-
case "api": {
|
|
318
|
-
await waitForApi(interval);
|
|
319
|
-
updateSpinner("api");
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
case "frappe": {
|
|
323
|
-
await waitForFrappe(interval);
|
|
324
|
-
updateSpinner("frappe");
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
case "postgres": {
|
|
328
|
-
await waitForPostgres(interval);
|
|
329
|
-
updateSpinner("postgres");
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
case "keycloak": {
|
|
333
|
-
await waitForKeycloak(interval);
|
|
334
|
-
updateSpinner("keycloak");
|
|
335
|
-
return;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
ora(
|
|
339
|
-
`available services are ${formatServiceList(waitServices)}`,
|
|
340
|
-
).fail();
|
|
341
|
-
}),
|
|
342
|
-
),
|
|
343
|
-
new Promise((_, reject) => {
|
|
344
|
-
timeoutId = setTimeout(() => reject(new Error("Timeout")), timeout);
|
|
345
|
-
}),
|
|
346
|
-
]);
|
|
311
|
+
await waitWithSpinner(services, { interval, timeout });
|
|
347
312
|
} catch (err) {
|
|
348
|
-
if (err instanceof Error && err.message === "Timeout") {
|
|
349
|
-
spinner.fail(
|
|
350
|
-
`${formatServiceList(unreadyServices)} timed out after ${timeout}ms`,
|
|
351
|
-
);
|
|
352
|
-
} else {
|
|
353
|
-
spinner.fail(err);
|
|
354
|
-
}
|
|
355
313
|
process.exit(1);
|
|
356
|
-
} finally {
|
|
357
|
-
clearTimeout(timeoutId);
|
|
358
314
|
}
|
|
359
315
|
});
|
|
360
316
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
317
|
+
program
|
|
318
|
+
.command("mesh")
|
|
319
|
+
.description("start the mesh server")
|
|
320
|
+
.option("-a, --api", "use api", false)
|
|
321
|
+
.option("-e, --dotenv <dotenv>", "dotenv file path", ".env")
|
|
322
|
+
.option("-f, --frappe", "use frappe", false)
|
|
323
|
+
.option("-i, --interval <interval>", "interval to wait for", "1000")
|
|
324
|
+
.option("-p, --port <port>", "port to run mesh on", "5002")
|
|
325
|
+
.option("-t, --timeout <timeout>", "timeout to wait for", "600000")
|
|
326
|
+
.action(async (options) => {
|
|
327
|
+
dotenv.config({ path: options.dotenv || defaultDotenvPath });
|
|
328
|
+
process.env.UWS_HTTP_MAX_HEADERS_SIZE = "16384";
|
|
329
|
+
if (options.frappe || options.api) {
|
|
330
|
+
process.env.MESH_API = options.api ? "1" : "0";
|
|
331
|
+
process.env.MESH_FRAPPE = options.frappe ? "1" : "0";
|
|
332
|
+
}
|
|
333
|
+
if (
|
|
334
|
+
!(await fs
|
|
335
|
+
.stat(path.resolve(projectRoot, "app/main.ts"))
|
|
336
|
+
.catch(() => false))
|
|
337
|
+
) {
|
|
338
|
+
process.env.MESH_APP = "0";
|
|
339
|
+
}
|
|
340
|
+
if (
|
|
341
|
+
!(await fs
|
|
342
|
+
.stat(path.resolve(projectRoot, "frappe/package.json"))
|
|
343
|
+
.catch(() => false))
|
|
344
|
+
) {
|
|
345
|
+
process.env.MESH_FRAPPE = "0";
|
|
346
|
+
}
|
|
347
|
+
const services = [
|
|
348
|
+
...(process.env.MESH_API === "1" ? ["api"] : []),
|
|
349
|
+
...(process.env.MESH_FRAPPE === "1" ? ["frappe"] : []),
|
|
350
|
+
];
|
|
351
|
+
if (services.length > 0) {
|
|
352
|
+
const interval = Number.parseInt(options.interval);
|
|
353
|
+
const timeout = Number.parseInt(options.timeout);
|
|
354
|
+
try {
|
|
355
|
+
await waitWithSpinner(services, { interval, timeout });
|
|
356
|
+
} catch (err) {
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
await execa(
|
|
361
|
+
"mesh",
|
|
362
|
+
[
|
|
363
|
+
"dev",
|
|
364
|
+
"--port",
|
|
365
|
+
Number(options.port || process.env.MESH_PORT || 5002).toString(),
|
|
366
|
+
],
|
|
367
|
+
{
|
|
368
|
+
stdio: "inherit",
|
|
369
|
+
},
|
|
370
|
+
);
|
|
371
|
+
});
|
|
366
372
|
|
|
367
|
-
program
|
|
373
|
+
program
|
|
374
|
+
.command("build")
|
|
375
|
+
.argument(
|
|
376
|
+
"[args...]",
|
|
377
|
+
`build to run: ${[...availableServices, "packages"].join(", ")} (default: all)`,
|
|
378
|
+
)
|
|
379
|
+
.description("run build command")
|
|
380
|
+
.action(async (args: string[]) => {
|
|
381
|
+
args = args.flatMap((arg) => arg.split(","));
|
|
382
|
+
const packages = args.includes("packages");
|
|
383
|
+
const projects = args.filter((arg) => arg !== "packages");
|
|
384
|
+
if ((!projects.length && !packages) || packages) {
|
|
385
|
+
await execa(
|
|
386
|
+
"turbo",
|
|
387
|
+
[
|
|
388
|
+
"run",
|
|
389
|
+
"build",
|
|
390
|
+
"--filter",
|
|
391
|
+
"'!./platforms/*'",
|
|
392
|
+
"--filter",
|
|
393
|
+
"'!./api'",
|
|
394
|
+
"--filter",
|
|
395
|
+
"'!./solana'",
|
|
396
|
+
"--filter",
|
|
397
|
+
"'!./ethereum'",
|
|
398
|
+
"--filter",
|
|
399
|
+
"'!./sui'",
|
|
400
|
+
],
|
|
401
|
+
{
|
|
402
|
+
stdio: "inherit",
|
|
403
|
+
shell: true,
|
|
404
|
+
cwd: projectRoot,
|
|
405
|
+
},
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
for (const platform of await fs.readdir("platforms")) {
|
|
409
|
+
if (
|
|
410
|
+
((!projects.length && !packages) || projects.includes(platform)) &&
|
|
411
|
+
(await fs
|
|
412
|
+
.access(path.join(projectRoot, "platforms", platform, "package.json"))
|
|
413
|
+
.then(
|
|
414
|
+
() =>
|
|
415
|
+
JSON.parse(
|
|
416
|
+
fsSync.readFileSync(
|
|
417
|
+
path.join(projectRoot, "platforms", platform, "package.json"),
|
|
418
|
+
"utf8",
|
|
419
|
+
),
|
|
420
|
+
).scripts?.build,
|
|
421
|
+
)
|
|
422
|
+
.catch(() => false))
|
|
423
|
+
) {
|
|
424
|
+
await execa("./mkpm", [`${platform}/build`], {
|
|
425
|
+
stdio: "inherit",
|
|
426
|
+
shell: true,
|
|
427
|
+
cwd: projectRoot,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
for (const service of availableServices) {
|
|
432
|
+
if (
|
|
433
|
+
((!projects.length && !packages) || projects.includes(service)) &&
|
|
434
|
+
(await fs
|
|
435
|
+
.access(path.join(projectRoot, service, "package.json"))
|
|
436
|
+
.then(
|
|
437
|
+
() =>
|
|
438
|
+
JSON.parse(
|
|
439
|
+
fsSync.readFileSync(
|
|
440
|
+
path.join(projectRoot, service, "package.json"),
|
|
441
|
+
"utf8",
|
|
442
|
+
),
|
|
443
|
+
).scripts?.build,
|
|
444
|
+
)
|
|
445
|
+
.catch(() => false))
|
|
446
|
+
) {
|
|
447
|
+
await execa("./mkpm", [`${service}/build`], {
|
|
448
|
+
stdio: "inherit",
|
|
449
|
+
shell: true,
|
|
450
|
+
cwd: projectRoot,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
});
|
|
368
455
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
)
|
|
374
|
-
|
|
375
|
-
|
|
456
|
+
program
|
|
457
|
+
.command("test")
|
|
458
|
+
.argument(
|
|
459
|
+
"[args...]",
|
|
460
|
+
`test to run: ${[...availableServices, "packages"].join(", ")} (default: all)`,
|
|
461
|
+
)
|
|
462
|
+
.description("run test command")
|
|
463
|
+
.action(async (args: string[]) => {
|
|
464
|
+
args = args.flatMap((arg) => arg.split(","));
|
|
465
|
+
const packages = args.includes("packages");
|
|
466
|
+
const projects = args.filter((arg) => arg !== "packages");
|
|
467
|
+
if ((!projects.length && !packages) || packages) {
|
|
468
|
+
await execa(
|
|
469
|
+
"turbo",
|
|
470
|
+
[
|
|
471
|
+
"run",
|
|
472
|
+
"test",
|
|
473
|
+
"--filter",
|
|
474
|
+
"'!./platforms/*'",
|
|
475
|
+
"--filter",
|
|
476
|
+
"'!./api'",
|
|
477
|
+
"--filter",
|
|
478
|
+
"'!./solana'",
|
|
479
|
+
"--filter",
|
|
480
|
+
"'!./ethereum'",
|
|
481
|
+
"--filter",
|
|
482
|
+
"'!./sui'",
|
|
483
|
+
],
|
|
484
|
+
{
|
|
485
|
+
stdio: "inherit",
|
|
486
|
+
shell: true,
|
|
487
|
+
cwd: projectRoot,
|
|
488
|
+
},
|
|
489
|
+
);
|
|
376
490
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
491
|
+
for (const platform of await fs.readdir("platforms")) {
|
|
492
|
+
if (
|
|
493
|
+
((!projects.length && !packages) || projects.includes(platform)) &&
|
|
494
|
+
(await fs
|
|
495
|
+
.access(path.join(projectRoot, "platforms", platform, "package.json"))
|
|
496
|
+
.then(
|
|
497
|
+
() =>
|
|
498
|
+
JSON.parse(
|
|
499
|
+
fsSync.readFileSync(
|
|
500
|
+
path.join(projectRoot, "platforms", platform, "package.json"),
|
|
501
|
+
"utf8",
|
|
502
|
+
),
|
|
503
|
+
).scripts?.test,
|
|
504
|
+
)
|
|
505
|
+
.catch(() => false))
|
|
506
|
+
) {
|
|
507
|
+
await execa("./mkpm", [`${platform}/test`], {
|
|
508
|
+
stdio: "inherit",
|
|
509
|
+
shell: true,
|
|
510
|
+
cwd: projectRoot,
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
for (const service of availableServices) {
|
|
515
|
+
if (
|
|
516
|
+
((!projects.length && !packages) || projects.includes(service)) &&
|
|
517
|
+
(await fs
|
|
518
|
+
.access(path.join(projectRoot, service, "package.json"))
|
|
519
|
+
.then(
|
|
520
|
+
() =>
|
|
521
|
+
JSON.parse(
|
|
522
|
+
fsSync.readFileSync(
|
|
523
|
+
path.join(projectRoot, service, "package.json"),
|
|
524
|
+
"utf8",
|
|
525
|
+
),
|
|
526
|
+
).scripts?.test,
|
|
527
|
+
)
|
|
528
|
+
.catch(() => false))
|
|
529
|
+
) {
|
|
530
|
+
await execa("./mkpm", [`${service}/test`], {
|
|
531
|
+
stdio: "inherit",
|
|
532
|
+
shell: true,
|
|
533
|
+
cwd: projectRoot,
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
});
|
|
381
538
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
539
|
+
program
|
|
540
|
+
.command("check")
|
|
541
|
+
.argument("[args...]", "checks to run: spelling, types, lint (default: all)")
|
|
542
|
+
.description("run check command")
|
|
543
|
+
.action(async (args: string[]) => {
|
|
544
|
+
args = args.flatMap((arg) => arg.split(","));
|
|
545
|
+
const spelling = !args.length || args.includes("spelling");
|
|
546
|
+
const types = !args.length || args.includes("types");
|
|
547
|
+
const lint = !args.length || args.includes("lint");
|
|
548
|
+
let exitCode = 0;
|
|
549
|
+
if (spelling) {
|
|
550
|
+
try {
|
|
551
|
+
await execa(
|
|
552
|
+
"cspell",
|
|
553
|
+
[
|
|
554
|
+
"--unique",
|
|
555
|
+
"`(git ls-files && (git lfs ls-files | cut -d' ' -f3))`",
|
|
556
|
+
],
|
|
557
|
+
{
|
|
558
|
+
stdio: "inherit",
|
|
559
|
+
shell: true,
|
|
560
|
+
cwd: projectRoot,
|
|
561
|
+
},
|
|
562
|
+
);
|
|
563
|
+
} catch (err) {
|
|
564
|
+
if (err instanceof Error && "exitCode" in err) {
|
|
565
|
+
exitCode = (err as { exitCode: number }).exitCode;
|
|
566
|
+
} else {
|
|
567
|
+
exitCode = 1;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
389
570
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
571
|
+
if (types) {
|
|
572
|
+
try {
|
|
573
|
+
await execa("turbo", ["run", "typecheck"], {
|
|
574
|
+
stdio: "inherit",
|
|
575
|
+
shell: true,
|
|
576
|
+
cwd: projectRoot,
|
|
577
|
+
});
|
|
578
|
+
} catch (err) {
|
|
579
|
+
if (err instanceof Error && "exitCode" in err) {
|
|
580
|
+
exitCode = (err as { exitCode: number }).exitCode;
|
|
581
|
+
} else {
|
|
582
|
+
exitCode = 1;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (lint) {
|
|
587
|
+
try {
|
|
588
|
+
await execa(
|
|
589
|
+
"biome",
|
|
590
|
+
[
|
|
591
|
+
"check",
|
|
592
|
+
"--fix",
|
|
593
|
+
"--unsafe",
|
|
594
|
+
"`(git ls-files && (git lfs ls-files | cut -d' ' -f3)) | sort | uniq -u | grep -E '(html)|(s?css)|(md)|(json)|(yaml)|([jt]sx?)$'`",
|
|
595
|
+
],
|
|
596
|
+
{
|
|
597
|
+
stdio: "inherit",
|
|
598
|
+
shell: true,
|
|
599
|
+
cwd: projectRoot,
|
|
600
|
+
},
|
|
601
|
+
);
|
|
602
|
+
} catch (err) {
|
|
603
|
+
if (err instanceof Error && "exitCode" in err) {
|
|
604
|
+
exitCode = (err as { exitCode: number }).exitCode;
|
|
605
|
+
} else {
|
|
606
|
+
exitCode = 1;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
process.exit(exitCode);
|
|
611
|
+
});
|
|
394
612
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
613
|
+
program
|
|
614
|
+
.command("count")
|
|
615
|
+
.description("count lines of code")
|
|
616
|
+
.action(async () => {
|
|
617
|
+
await execa(
|
|
618
|
+
"cloc",
|
|
619
|
+
[
|
|
620
|
+
"`(git ls-files && (git lfs ls-files | cut -d' ' -f3)) | sort | uniq -u | grep -E '(html)|(s?css)|(md)|(json)|(yaml)|([jt]sx?)$'`",
|
|
621
|
+
],
|
|
622
|
+
{
|
|
623
|
+
stdio: "inherit",
|
|
624
|
+
shell: true,
|
|
625
|
+
cwd: projectRoot,
|
|
626
|
+
},
|
|
627
|
+
);
|
|
402
628
|
});
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
629
|
+
|
|
630
|
+
program
|
|
631
|
+
.command("generate")
|
|
632
|
+
.description("run generate command")
|
|
633
|
+
.action(async () => {
|
|
634
|
+
await execa("turbo", ["run", "generate"], {
|
|
635
|
+
stdio: "inherit",
|
|
636
|
+
shell: true,
|
|
637
|
+
cwd: projectRoot,
|
|
638
|
+
});
|
|
639
|
+
});
|
|
640
|
+
|
|
641
|
+
program.parse(process.argv);
|
|
642
|
+
|
|
643
|
+
async function waitWithSpinner(
|
|
644
|
+
services: string[],
|
|
645
|
+
options: { interval?: number; timeout?: number } = {},
|
|
646
|
+
) {
|
|
647
|
+
const { interval = 1000, timeout = 600000 } = options;
|
|
648
|
+
const waitFunctions = {
|
|
649
|
+
api: waitForApi,
|
|
650
|
+
frappe: waitForFrappe,
|
|
651
|
+
postgres: waitForPostgres,
|
|
652
|
+
keycloak: waitForKeycloak,
|
|
653
|
+
};
|
|
654
|
+
const unreadyServices = [...services];
|
|
655
|
+
const spinner = ora(
|
|
656
|
+
`waiting for ${formatServiceList(unreadyServices)}`,
|
|
657
|
+
).start();
|
|
658
|
+
function updateSpinner(readyService: string) {
|
|
659
|
+
unreadyServices.splice(unreadyServices.indexOf(readyService), 1);
|
|
660
|
+
spinner.succeed(`${readyService} is ready`);
|
|
661
|
+
if (unreadyServices.length) {
|
|
662
|
+
spinner.start(`waiting for ${formatServiceList(unreadyServices)}`);
|
|
411
663
|
}
|
|
412
|
-
} finally {
|
|
413
|
-
await client.end();
|
|
414
664
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
async function waitForKeycloak(interval: number) {
|
|
665
|
+
let timeoutId: NodeJS.Timeout | undefined;
|
|
418
666
|
try {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
667
|
+
await Promise.race([
|
|
668
|
+
Promise.all(
|
|
669
|
+
services.map(async (service) => {
|
|
670
|
+
const waitFn = waitFunctions[service];
|
|
671
|
+
if (!waitFn) throw new Error(`Unknown service: ${service}`);
|
|
672
|
+
await waitFn(interval);
|
|
673
|
+
updateSpinner(service);
|
|
674
|
+
}),
|
|
675
|
+
),
|
|
676
|
+
new Promise((_, reject) => {
|
|
677
|
+
timeoutId = setTimeout(() => reject(new Error("Timeout")), timeout);
|
|
678
|
+
}),
|
|
679
|
+
]);
|
|
680
|
+
} catch (err) {
|
|
681
|
+
const error = err as Error;
|
|
682
|
+
if (error.message === "Timeout") {
|
|
683
|
+
spinner.fail(
|
|
684
|
+
`${formatServiceList(unreadyServices)} timed out after ${timeout}ms`,
|
|
685
|
+
);
|
|
686
|
+
} else {
|
|
687
|
+
spinner.fail(error.message);
|
|
424
688
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
689
|
+
throw error;
|
|
690
|
+
} finally {
|
|
691
|
+
clearTimeout(timeoutId);
|
|
692
|
+
}
|
|
428
693
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
2
|
* File: /src/types.ts
|
|
3
3
|
* Project: @multiplatform.one/cli
|
|
4
|
-
* File Created:
|
|
4
|
+
* File Created: 10-01-2025 21:02:36
|
|
5
5
|
* Author: Clay Risser
|
|
6
6
|
* -----
|
|
7
|
-
* BitSpur (c) Copyright 2021 -
|
|
7
|
+
* BitSpur (c) Copyright 2021 - 2025
|
|
8
8
|
*
|
|
9
9
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
10
10
|
* you may not use this file except in compliance with the License.
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
export interface CookieCutterConfig {
|
|
23
23
|
default_context: {
|
|
24
|
-
|
|
24
|
+
services: string;
|
|
25
25
|
name: string;
|
|
26
26
|
platforms: string;
|
|
27
27
|
};
|