@multiplatform.one/cli 2.1.7 → 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 +16 -15
- 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,43 +1,68 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
-
|
|
4
1
|
// src/bin/multiplatformOne.ts
|
|
5
2
|
import fsSync from "node:fs";
|
|
6
3
|
import fs from "node:fs/promises";
|
|
7
4
|
import os from "node:os";
|
|
8
5
|
import path from "node:path";
|
|
9
6
|
import { fileURLToPath } from "node:url";
|
|
10
|
-
import
|
|
7
|
+
import {
|
|
8
|
+
formatServiceList,
|
|
9
|
+
projectRoot,
|
|
10
|
+
waitForApi,
|
|
11
|
+
waitForFrappe,
|
|
12
|
+
waitForKeycloak,
|
|
13
|
+
waitForPostgres,
|
|
14
|
+
waitServices
|
|
15
|
+
} from "@multiplatform.one/utils/dev";
|
|
11
16
|
import { program } from "commander";
|
|
12
17
|
import dotenv from "dotenv";
|
|
13
18
|
import { execa } from "execa";
|
|
14
19
|
import inquirer from "inquirer";
|
|
15
20
|
import ora from "ora";
|
|
16
|
-
|
|
17
|
-
var
|
|
18
|
-
"api",
|
|
19
|
-
"frappe"
|
|
20
|
-
];
|
|
21
|
+
var availableServices = ["api", "frappe", "solana", "ethereum", "sui"];
|
|
22
|
+
var defaultDotenvPath = path.resolve(projectRoot, ".env");
|
|
21
23
|
var availablePlatforms = [
|
|
22
24
|
"electron",
|
|
23
25
|
"expo",
|
|
24
26
|
"keycloak",
|
|
25
|
-
"
|
|
27
|
+
"one",
|
|
26
28
|
"storybook",
|
|
27
|
-
"storybook-expo"
|
|
29
|
+
"storybook-expo",
|
|
30
|
+
"vocs",
|
|
31
|
+
"vscode",
|
|
32
|
+
"webext"
|
|
28
33
|
];
|
|
29
|
-
process.env.COOKIECUTTER = `sh ${path.resolve(
|
|
34
|
+
process.env.COOKIECUTTER = `sh ${path.resolve(
|
|
35
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
36
|
+
"../../scripts/cookiecutter.sh"
|
|
37
|
+
)}`;
|
|
30
38
|
program.name("multiplatform.one");
|
|
31
|
-
program.version(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
program.version(
|
|
40
|
+
JSON.parse(
|
|
41
|
+
fsSync.readFileSync(
|
|
42
|
+
path.resolve(
|
|
43
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
44
|
+
"../../package.json"
|
|
45
|
+
),
|
|
46
|
+
"utf8"
|
|
47
|
+
)
|
|
48
|
+
)?.version
|
|
49
|
+
);
|
|
50
|
+
program.command("init").option(
|
|
51
|
+
"-r, --remote <remote>",
|
|
52
|
+
"the remote to use",
|
|
53
|
+
"https://gitlab.com/bitspur/multiplatform.one/cookiecutter"
|
|
54
|
+
).option(
|
|
55
|
+
"-c, --checkout <branch>",
|
|
56
|
+
"branch, tag or commit to checkout",
|
|
57
|
+
"main"
|
|
58
|
+
).option("-p, --platforms <platforms>", "platforms to use").option("-s, --services <services>", "services to use").argument("[name]", "the name of the project", "").description("init multiplatform.one").action(async (name, options) => {
|
|
59
|
+
let { services, platforms } = options;
|
|
60
|
+
if ((await execa("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
38
61
|
reject: false
|
|
39
62
|
})).exitCode === 0) {
|
|
40
|
-
throw new Error(
|
|
63
|
+
throw new Error(
|
|
64
|
+
"multiplatform.one cannot be initialized inside a git repository"
|
|
65
|
+
);
|
|
41
66
|
}
|
|
42
67
|
if (!name) {
|
|
43
68
|
name = (await inquirer.prompt([
|
|
@@ -48,18 +73,16 @@ program.command("init").option("-r, --remote <remote>", "the remote to use", "ht
|
|
|
48
73
|
}
|
|
49
74
|
])).name;
|
|
50
75
|
}
|
|
51
|
-
if (!
|
|
52
|
-
const
|
|
76
|
+
if (!services) {
|
|
77
|
+
const servicesResult = (await inquirer.prompt([
|
|
53
78
|
{
|
|
54
|
-
message: "What
|
|
55
|
-
name: "
|
|
79
|
+
message: "What services are you using?",
|
|
80
|
+
name: "services",
|
|
56
81
|
type: "checkbox",
|
|
57
|
-
choices:
|
|
58
|
-
name: name2
|
|
59
|
-
}))
|
|
82
|
+
choices: availableServices.map((name2) => ({ name: name2 }))
|
|
60
83
|
}
|
|
61
|
-
])).
|
|
62
|
-
|
|
84
|
+
])).services;
|
|
85
|
+
services = servicesResult.join(",");
|
|
63
86
|
}
|
|
64
87
|
if (!platforms) {
|
|
65
88
|
const platformsResult = (await inquirer.prompt([
|
|
@@ -67,56 +90,66 @@ program.command("init").option("-r, --remote <remote>", "the remote to use", "ht
|
|
|
67
90
|
message: "What platforms are you using?",
|
|
68
91
|
name: "platforms",
|
|
69
92
|
type: "checkbox",
|
|
70
|
-
choices: availablePlatforms.map((name2) => ({
|
|
71
|
-
name: name2
|
|
72
|
-
}))
|
|
93
|
+
choices: availablePlatforms.map((name2) => ({ name: name2 }))
|
|
73
94
|
}
|
|
74
95
|
])).platforms;
|
|
75
96
|
platforms = platformsResult.join(",");
|
|
76
97
|
}
|
|
77
98
|
const cookieCutterConfig = {
|
|
78
|
-
default_context: {
|
|
79
|
-
name,
|
|
80
|
-
platforms,
|
|
81
|
-
backends
|
|
82
|
-
}
|
|
99
|
+
default_context: { name, platforms, services }
|
|
83
100
|
};
|
|
84
|
-
const cookieCutterConfigFile = path.join(
|
|
101
|
+
const cookieCutterConfigFile = path.join(
|
|
102
|
+
await fs.mkdtemp(path.join(os.tmpdir(), "multiplatform-")),
|
|
103
|
+
"config.json"
|
|
104
|
+
);
|
|
85
105
|
try {
|
|
86
|
-
await fs.writeFile(
|
|
87
|
-
await execa("sh", [
|
|
88
|
-
path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../scripts/init.sh"),
|
|
89
|
-
"--no-input",
|
|
90
|
-
"-f",
|
|
91
|
-
"--config-file",
|
|
106
|
+
await fs.writeFile(
|
|
92
107
|
cookieCutterConfigFile,
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
JSON.stringify(cookieCutterConfig, null, 2)
|
|
109
|
+
);
|
|
110
|
+
await execa(
|
|
111
|
+
"sh",
|
|
112
|
+
[
|
|
113
|
+
path.resolve(
|
|
114
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
115
|
+
"../../scripts/init.sh"
|
|
116
|
+
),
|
|
117
|
+
"--no-input",
|
|
118
|
+
"-f",
|
|
119
|
+
"--config-file",
|
|
120
|
+
cookieCutterConfigFile,
|
|
121
|
+
"--checkout",
|
|
122
|
+
options.checkout,
|
|
123
|
+
options.remote
|
|
124
|
+
],
|
|
125
|
+
{
|
|
126
|
+
stdio: "inherit"
|
|
127
|
+
}
|
|
128
|
+
);
|
|
99
129
|
} finally {
|
|
100
|
-
await fs.rm(cookieCutterConfigFile, {
|
|
101
|
-
recursive: true,
|
|
102
|
-
force: true
|
|
103
|
-
});
|
|
130
|
+
await fs.rm(cookieCutterConfigFile, { recursive: true, force: true });
|
|
104
131
|
}
|
|
105
132
|
});
|
|
106
|
-
program.command("update").option(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
133
|
+
program.command("update").option(
|
|
134
|
+
"-r, --remote <remote>",
|
|
135
|
+
"the remote to use",
|
|
136
|
+
"https://gitlab.com/bitspur/multiplatform.one/cookiecutter"
|
|
137
|
+
).option(
|
|
138
|
+
"-c, --checkout <branch>",
|
|
139
|
+
"branch, tag or commit to checkout",
|
|
140
|
+
"main"
|
|
141
|
+
).option("-p, --platforms <platforms>", "platforms to keep").option("-s, --services <services>", "services to keep").description("update multiplatform.one").action(async (options) => {
|
|
142
|
+
let { services, platforms } = options;
|
|
143
|
+
if (!services) {
|
|
144
|
+
const servicesResult = await inquirer.prompt([
|
|
110
145
|
{
|
|
111
|
-
message: "What
|
|
112
|
-
name: "
|
|
146
|
+
message: "What services are you using?",
|
|
147
|
+
name: "services",
|
|
113
148
|
type: "checkbox",
|
|
114
|
-
choices:
|
|
115
|
-
name
|
|
116
|
-
}))
|
|
149
|
+
choices: availableServices.map((name) => ({ name }))
|
|
117
150
|
}
|
|
118
151
|
]);
|
|
119
|
-
|
|
152
|
+
services = servicesResult.services.join(",");
|
|
120
153
|
}
|
|
121
154
|
if (!platforms) {
|
|
122
155
|
const platformsResult = await inquirer.prompt([
|
|
@@ -124,202 +157,391 @@ program.command("update").option("-r, --remote <remote>", "the remote to use", "
|
|
|
124
157
|
message: "What platforms are you using?",
|
|
125
158
|
name: "platforms",
|
|
126
159
|
type: "checkbox",
|
|
127
|
-
choices: availablePlatforms.map((name) => ({
|
|
128
|
-
name
|
|
129
|
-
}))
|
|
160
|
+
choices: availablePlatforms.map((name) => ({ name }))
|
|
130
161
|
}
|
|
131
162
|
]);
|
|
132
163
|
platforms = platformsResult.platforms.join(",");
|
|
133
164
|
}
|
|
134
|
-
if ((await execa("git", [
|
|
135
|
-
"rev-parse",
|
|
136
|
-
"--is-inside-work-tree"
|
|
137
|
-
], {
|
|
165
|
+
if ((await execa("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
138
166
|
reject: false
|
|
139
167
|
})).exitCode !== 0) {
|
|
140
|
-
throw new Error(
|
|
168
|
+
throw new Error(
|
|
169
|
+
"multiplatform.one cannot be updated outside of a git repository"
|
|
170
|
+
);
|
|
141
171
|
}
|
|
142
|
-
if ((await execa("git", [
|
|
143
|
-
"diff",
|
|
144
|
-
"--cached",
|
|
145
|
-
"--quiet"
|
|
146
|
-
], {
|
|
172
|
+
if ((await execa("git", ["diff", "--cached", "--quiet"], {
|
|
147
173
|
reject: false
|
|
148
174
|
})).exitCode !== 0) {
|
|
149
|
-
throw new Error(
|
|
175
|
+
throw new Error(
|
|
176
|
+
"multiplatform.one cannot be updated with uncommitted changes"
|
|
177
|
+
);
|
|
150
178
|
}
|
|
151
|
-
const
|
|
152
|
-
"rev-parse",
|
|
153
|
-
"--show-toplevel"
|
|
154
|
-
])).stdout.trim();
|
|
179
|
+
const projectRoot2 = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
155
180
|
let cookieCutterConfig;
|
|
156
|
-
if ((await fs.stat(path.resolve(
|
|
157
|
-
|
|
181
|
+
if ((await fs.stat(path.resolve(projectRoot2, "package.json"))).isFile() && (await fs.stat(path.resolve(projectRoot2, "app/package.json"))).isFile() && JSON.parse(
|
|
182
|
+
await fs.readFile(
|
|
183
|
+
path.resolve(projectRoot2, "app/package.json"),
|
|
184
|
+
"utf8"
|
|
185
|
+
)
|
|
186
|
+
)?.dependencies?.["multiplatform.one"]?.length) {
|
|
187
|
+
const name = JSON.parse(
|
|
188
|
+
await fs.readFile(path.resolve(projectRoot2, "package.json"), "utf8")
|
|
189
|
+
)?.name;
|
|
158
190
|
if (name) {
|
|
159
191
|
cookieCutterConfig = {
|
|
160
|
-
default_context: {
|
|
161
|
-
name,
|
|
162
|
-
backends,
|
|
163
|
-
platforms
|
|
164
|
-
}
|
|
192
|
+
default_context: { name, services, platforms }
|
|
165
193
|
};
|
|
166
194
|
}
|
|
167
195
|
}
|
|
168
196
|
if (!cookieCutterConfig) throw new Error("not a multiplatform.one project");
|
|
169
|
-
const cookieCutterConfigFile = path.join(
|
|
197
|
+
const cookieCutterConfigFile = path.join(
|
|
198
|
+
await fs.mkdtemp(path.join(os.tmpdir(), "multiplatform-")),
|
|
199
|
+
"config.json"
|
|
200
|
+
);
|
|
170
201
|
try {
|
|
171
|
-
await fs.writeFile(
|
|
172
|
-
await execa("sh", [
|
|
173
|
-
path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../scripts/update.sh"),
|
|
174
|
-
"--no-input",
|
|
175
|
-
"-f",
|
|
176
|
-
"--config-file",
|
|
202
|
+
await fs.writeFile(
|
|
177
203
|
cookieCutterConfigFile,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
204
|
+
JSON.stringify(cookieCutterConfig, null, 2)
|
|
205
|
+
);
|
|
206
|
+
await execa(
|
|
207
|
+
"sh",
|
|
208
|
+
[
|
|
209
|
+
path.resolve(
|
|
210
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
211
|
+
"../../scripts/update.sh"
|
|
212
|
+
),
|
|
213
|
+
"--no-input",
|
|
214
|
+
"-f",
|
|
215
|
+
"--config-file",
|
|
216
|
+
cookieCutterConfigFile,
|
|
217
|
+
"--checkout",
|
|
218
|
+
options.checkout,
|
|
219
|
+
options.remote
|
|
220
|
+
],
|
|
221
|
+
{
|
|
222
|
+
cwd: projectRoot2,
|
|
223
|
+
stdio: "inherit"
|
|
224
|
+
}
|
|
225
|
+
);
|
|
185
226
|
} finally {
|
|
186
|
-
await fs.rm(cookieCutterConfigFile, {
|
|
187
|
-
recursive: true,
|
|
188
|
-
force: true
|
|
189
|
-
});
|
|
227
|
+
await fs.rm(cookieCutterConfigFile, { recursive: true, force: true });
|
|
190
228
|
}
|
|
191
229
|
});
|
|
192
|
-
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
];
|
|
198
|
-
program.command("wait").description("wait for a service to be ready").option("-i, --interval <interval>", "interval to wait for", "1000").option("-t, --timeout <timeout>", "timeout to wait for", "600000").option("-e, --dotenv <dotenv>", "dotenv file path", ".env").argument("<services>", `the services to wait for (${waitServices.join(", ")})`).action(async (servicesString, options) => {
|
|
199
|
-
dotenv.config({
|
|
200
|
-
path: options.dotenv
|
|
201
|
-
});
|
|
230
|
+
program.command("wait").description("wait for a service to be ready").option("-i, --interval <interval>", "interval to wait for", "1000").option("-t, --timeout <timeout>", "timeout to wait for", "600000").option("-e, --dotenv <dotenv>", "dotenv file path", ".env").argument(
|
|
231
|
+
"<services>",
|
|
232
|
+
`the services to wait for (${waitServices.join(", ")})`
|
|
233
|
+
).action(async (servicesString, options) => {
|
|
234
|
+
dotenv.config({ path: options.dotenv || defaultDotenvPath });
|
|
202
235
|
const interval = Number.parseInt(options.interval);
|
|
203
236
|
const timeout = Number.parseInt(options.timeout);
|
|
204
237
|
const services = servicesString.split(",");
|
|
205
|
-
|
|
206
|
-
|
|
238
|
+
try {
|
|
239
|
+
await waitWithSpinner(services, { interval, timeout });
|
|
240
|
+
} catch (err) {
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
program.command("mesh").description("start the mesh server").option("-a, --api", "use api", false).option("-e, --dotenv <dotenv>", "dotenv file path", ".env").option("-f, --frappe", "use frappe", false).option("-i, --interval <interval>", "interval to wait for", "1000").option("-p, --port <port>", "port to run mesh on", "5002").option("-t, --timeout <timeout>", "timeout to wait for", "600000").action(async (options) => {
|
|
245
|
+
dotenv.config({ path: options.dotenv || defaultDotenvPath });
|
|
246
|
+
process.env.UWS_HTTP_MAX_HEADERS_SIZE = "16384";
|
|
247
|
+
if (options.frappe || options.api) {
|
|
248
|
+
process.env.MESH_API = options.api ? "1" : "0";
|
|
249
|
+
process.env.MESH_FRAPPE = options.frappe ? "1" : "0";
|
|
250
|
+
}
|
|
251
|
+
if (!await fs.stat(path.resolve(projectRoot, "app/main.ts")).catch(() => false)) {
|
|
252
|
+
process.env.MESH_APP = "0";
|
|
253
|
+
}
|
|
254
|
+
if (!await fs.stat(path.resolve(projectRoot, "frappe/package.json")).catch(() => false)) {
|
|
255
|
+
process.env.MESH_FRAPPE = "0";
|
|
256
|
+
}
|
|
257
|
+
const services = [
|
|
258
|
+
...process.env.MESH_API === "1" ? ["api"] : [],
|
|
259
|
+
...process.env.MESH_FRAPPE === "1" ? ["frappe"] : []
|
|
207
260
|
];
|
|
208
|
-
|
|
261
|
+
if (services.length > 0) {
|
|
262
|
+
const interval = Number.parseInt(options.interval);
|
|
263
|
+
const timeout = Number.parseInt(options.timeout);
|
|
264
|
+
try {
|
|
265
|
+
await waitWithSpinner(services, { interval, timeout });
|
|
266
|
+
} catch (err) {
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
await execa(
|
|
271
|
+
"mesh",
|
|
272
|
+
[
|
|
273
|
+
"dev",
|
|
274
|
+
"--port",
|
|
275
|
+
Number(options.port || process.env.MESH_PORT || 5002).toString()
|
|
276
|
+
],
|
|
277
|
+
{
|
|
278
|
+
stdio: "inherit"
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
program.command("build").argument(
|
|
283
|
+
"[args...]",
|
|
284
|
+
`build to run: ${[...availableServices, "packages"].join(", ")} (default: all)`
|
|
285
|
+
).description("run build command").action(async (args) => {
|
|
286
|
+
args = args.flatMap((arg) => arg.split(","));
|
|
287
|
+
const packages = args.includes("packages");
|
|
288
|
+
const projects = args.filter((arg) => arg !== "packages");
|
|
289
|
+
if (!projects.length && !packages || packages) {
|
|
290
|
+
await execa(
|
|
291
|
+
"turbo",
|
|
292
|
+
[
|
|
293
|
+
"run",
|
|
294
|
+
"build",
|
|
295
|
+
"--filter",
|
|
296
|
+
"'!./platforms/*'",
|
|
297
|
+
"--filter",
|
|
298
|
+
"'!./api'",
|
|
299
|
+
"--filter",
|
|
300
|
+
"'!./solana'",
|
|
301
|
+
"--filter",
|
|
302
|
+
"'!./ethereum'",
|
|
303
|
+
"--filter",
|
|
304
|
+
"'!./sui'"
|
|
305
|
+
],
|
|
306
|
+
{
|
|
307
|
+
stdio: "inherit",
|
|
308
|
+
shell: true,
|
|
309
|
+
cwd: projectRoot
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
for (const platform of await fs.readdir("platforms")) {
|
|
314
|
+
if ((!projects.length && !packages || projects.includes(platform)) && await fs.access(path.join(projectRoot, "platforms", platform, "package.json")).then(
|
|
315
|
+
() => JSON.parse(
|
|
316
|
+
fsSync.readFileSync(
|
|
317
|
+
path.join(projectRoot, "platforms", platform, "package.json"),
|
|
318
|
+
"utf8"
|
|
319
|
+
)
|
|
320
|
+
).scripts?.build
|
|
321
|
+
).catch(() => false)) {
|
|
322
|
+
await execa("./mkpm", [`${platform}/build`], {
|
|
323
|
+
stdio: "inherit",
|
|
324
|
+
shell: true,
|
|
325
|
+
cwd: projectRoot
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
for (const service of availableServices) {
|
|
330
|
+
if ((!projects.length && !packages || projects.includes(service)) && await fs.access(path.join(projectRoot, service, "package.json")).then(
|
|
331
|
+
() => JSON.parse(
|
|
332
|
+
fsSync.readFileSync(
|
|
333
|
+
path.join(projectRoot, service, "package.json"),
|
|
334
|
+
"utf8"
|
|
335
|
+
)
|
|
336
|
+
).scripts?.build
|
|
337
|
+
).catch(() => false)) {
|
|
338
|
+
await execa("./mkpm", [`${service}/build`], {
|
|
339
|
+
stdio: "inherit",
|
|
340
|
+
shell: true,
|
|
341
|
+
cwd: projectRoot
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
program.command("test").argument(
|
|
347
|
+
"[args...]",
|
|
348
|
+
`test to run: ${[...availableServices, "packages"].join(", ")} (default: all)`
|
|
349
|
+
).description("run test command").action(async (args) => {
|
|
350
|
+
args = args.flatMap((arg) => arg.split(","));
|
|
351
|
+
const packages = args.includes("packages");
|
|
352
|
+
const projects = args.filter((arg) => arg !== "packages");
|
|
353
|
+
if (!projects.length && !packages || packages) {
|
|
354
|
+
await execa(
|
|
355
|
+
"turbo",
|
|
356
|
+
[
|
|
357
|
+
"run",
|
|
358
|
+
"test",
|
|
359
|
+
"--filter",
|
|
360
|
+
"'!./platforms/*'",
|
|
361
|
+
"--filter",
|
|
362
|
+
"'!./api'",
|
|
363
|
+
"--filter",
|
|
364
|
+
"'!./solana'",
|
|
365
|
+
"--filter",
|
|
366
|
+
"'!./ethereum'",
|
|
367
|
+
"--filter",
|
|
368
|
+
"'!./sui'"
|
|
369
|
+
],
|
|
370
|
+
{
|
|
371
|
+
stdio: "inherit",
|
|
372
|
+
shell: true,
|
|
373
|
+
cwd: projectRoot
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
for (const platform of await fs.readdir("platforms")) {
|
|
378
|
+
if ((!projects.length && !packages || projects.includes(platform)) && await fs.access(path.join(projectRoot, "platforms", platform, "package.json")).then(
|
|
379
|
+
() => JSON.parse(
|
|
380
|
+
fsSync.readFileSync(
|
|
381
|
+
path.join(projectRoot, "platforms", platform, "package.json"),
|
|
382
|
+
"utf8"
|
|
383
|
+
)
|
|
384
|
+
).scripts?.test
|
|
385
|
+
).catch(() => false)) {
|
|
386
|
+
await execa("./mkpm", [`${platform}/test`], {
|
|
387
|
+
stdio: "inherit",
|
|
388
|
+
shell: true,
|
|
389
|
+
cwd: projectRoot
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
for (const service of availableServices) {
|
|
394
|
+
if ((!projects.length && !packages || projects.includes(service)) && await fs.access(path.join(projectRoot, service, "package.json")).then(
|
|
395
|
+
() => JSON.parse(
|
|
396
|
+
fsSync.readFileSync(
|
|
397
|
+
path.join(projectRoot, service, "package.json"),
|
|
398
|
+
"utf8"
|
|
399
|
+
)
|
|
400
|
+
).scripts?.test
|
|
401
|
+
).catch(() => false)) {
|
|
402
|
+
await execa("./mkpm", [`${service}/test`], {
|
|
403
|
+
stdio: "inherit",
|
|
404
|
+
shell: true,
|
|
405
|
+
cwd: projectRoot
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
program.command("check").argument("[args...]", "checks to run: spelling, types, lint (default: all)").description("run check command").action(async (args) => {
|
|
411
|
+
args = args.flatMap((arg) => arg.split(","));
|
|
412
|
+
const spelling = !args.length || args.includes("spelling");
|
|
413
|
+
const types = !args.length || args.includes("types");
|
|
414
|
+
const lint = !args.length || args.includes("lint");
|
|
415
|
+
let exitCode = 0;
|
|
416
|
+
if (spelling) {
|
|
417
|
+
try {
|
|
418
|
+
await execa(
|
|
419
|
+
"cspell",
|
|
420
|
+
[
|
|
421
|
+
"--unique",
|
|
422
|
+
"`(git ls-files && (git lfs ls-files | cut -d' ' -f3))`"
|
|
423
|
+
],
|
|
424
|
+
{
|
|
425
|
+
stdio: "inherit",
|
|
426
|
+
shell: true,
|
|
427
|
+
cwd: projectRoot
|
|
428
|
+
}
|
|
429
|
+
);
|
|
430
|
+
} catch (err) {
|
|
431
|
+
if (err instanceof Error && "exitCode" in err) {
|
|
432
|
+
exitCode = err.exitCode;
|
|
433
|
+
} else {
|
|
434
|
+
exitCode = 1;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (types) {
|
|
439
|
+
try {
|
|
440
|
+
await execa("turbo", ["run", "typecheck"], {
|
|
441
|
+
stdio: "inherit",
|
|
442
|
+
shell: true,
|
|
443
|
+
cwd: projectRoot
|
|
444
|
+
});
|
|
445
|
+
} catch (err) {
|
|
446
|
+
if (err instanceof Error && "exitCode" in err) {
|
|
447
|
+
exitCode = err.exitCode;
|
|
448
|
+
} else {
|
|
449
|
+
exitCode = 1;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (lint) {
|
|
454
|
+
try {
|
|
455
|
+
await execa(
|
|
456
|
+
"biome",
|
|
457
|
+
[
|
|
458
|
+
"check",
|
|
459
|
+
"--fix",
|
|
460
|
+
"--unsafe",
|
|
461
|
+
"`(git ls-files && (git lfs ls-files | cut -d' ' -f3)) | sort | uniq -u | grep -E '(html)|(s?css)|(md)|(json)|(yaml)|([jt]sx?)$'`"
|
|
462
|
+
],
|
|
463
|
+
{
|
|
464
|
+
stdio: "inherit",
|
|
465
|
+
shell: true,
|
|
466
|
+
cwd: projectRoot
|
|
467
|
+
}
|
|
468
|
+
);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
if (err instanceof Error && "exitCode" in err) {
|
|
471
|
+
exitCode = err.exitCode;
|
|
472
|
+
} else {
|
|
473
|
+
exitCode = 1;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
process.exit(exitCode);
|
|
478
|
+
});
|
|
479
|
+
program.command("count").description("count lines of code").action(async () => {
|
|
480
|
+
await execa(
|
|
481
|
+
"cloc",
|
|
482
|
+
[
|
|
483
|
+
"`(git ls-files && (git lfs ls-files | cut -d' ' -f3)) | sort | uniq -u | grep -E '(html)|(s?css)|(md)|(json)|(yaml)|([jt]sx?)$'`"
|
|
484
|
+
],
|
|
485
|
+
{
|
|
486
|
+
stdio: "inherit",
|
|
487
|
+
shell: true,
|
|
488
|
+
cwd: projectRoot
|
|
489
|
+
}
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
program.command("generate").description("run generate command").action(async () => {
|
|
493
|
+
await execa("turbo", ["run", "generate"], {
|
|
494
|
+
stdio: "inherit",
|
|
495
|
+
shell: true,
|
|
496
|
+
cwd: projectRoot
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
program.parse(process.argv);
|
|
500
|
+
async function waitWithSpinner(services, options = {}) {
|
|
501
|
+
const { interval = 1e3, timeout = 6e5 } = options;
|
|
502
|
+
const waitFunctions = {
|
|
503
|
+
api: waitForApi,
|
|
504
|
+
frappe: waitForFrappe,
|
|
505
|
+
postgres: waitForPostgres,
|
|
506
|
+
keycloak: waitForKeycloak
|
|
507
|
+
};
|
|
508
|
+
const unreadyServices = [...services];
|
|
509
|
+
const spinner = ora(
|
|
510
|
+
`waiting for ${formatServiceList(unreadyServices)}`
|
|
511
|
+
).start();
|
|
209
512
|
function updateSpinner(readyService) {
|
|
210
513
|
unreadyServices.splice(unreadyServices.indexOf(readyService), 1);
|
|
211
|
-
spinner.stop();
|
|
212
514
|
spinner.succeed(`${readyService} is ready`);
|
|
213
|
-
if (
|
|
214
|
-
|
|
515
|
+
if (unreadyServices.length) {
|
|
516
|
+
spinner.start(`waiting for ${formatServiceList(unreadyServices)}`);
|
|
517
|
+
}
|
|
215
518
|
}
|
|
216
|
-
__name(updateSpinner, "updateSpinner");
|
|
217
519
|
let timeoutId;
|
|
218
520
|
try {
|
|
219
521
|
await Promise.race([
|
|
220
|
-
Promise.all(
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
await waitForFrappe(interval);
|
|
229
|
-
updateSpinner("frappe");
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
case "postgres": {
|
|
233
|
-
await waitForPostgres(interval);
|
|
234
|
-
updateSpinner("postgres");
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
case "keycloak": {
|
|
238
|
-
await waitForKeycloak(interval);
|
|
239
|
-
updateSpinner("keycloak");
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
ora(`available services are ${formatServiceList(waitServices)}`).fail();
|
|
244
|
-
})),
|
|
522
|
+
Promise.all(
|
|
523
|
+
services.map(async (service) => {
|
|
524
|
+
const waitFn = waitFunctions[service];
|
|
525
|
+
if (!waitFn) throw new Error(`Unknown service: ${service}`);
|
|
526
|
+
await waitFn(interval);
|
|
527
|
+
updateSpinner(service);
|
|
528
|
+
})
|
|
529
|
+
),
|
|
245
530
|
new Promise((_, reject) => {
|
|
246
531
|
timeoutId = setTimeout(() => reject(new Error("Timeout")), timeout);
|
|
247
532
|
})
|
|
248
533
|
]);
|
|
249
534
|
} catch (err) {
|
|
250
|
-
|
|
251
|
-
|
|
535
|
+
const error = err;
|
|
536
|
+
if (error.message === "Timeout") {
|
|
537
|
+
spinner.fail(
|
|
538
|
+
`${formatServiceList(unreadyServices)} timed out after ${timeout}ms`
|
|
539
|
+
);
|
|
252
540
|
} else {
|
|
253
|
-
spinner.fail(
|
|
541
|
+
spinner.fail(error.message);
|
|
254
542
|
}
|
|
255
|
-
|
|
543
|
+
throw error;
|
|
256
544
|
} finally {
|
|
257
545
|
clearTimeout(timeoutId);
|
|
258
546
|
}
|
|
259
|
-
});
|
|
260
|
-
function formatServiceList(services) {
|
|
261
|
-
if (services.length === 1) return services[0];
|
|
262
|
-
if (services.length === 2) return `${services[0]} and ${services[1]}`;
|
|
263
|
-
return `${services.slice(0, -1).join(", ")} and ${services[services.length - 1]}`;
|
|
264
|
-
}
|
|
265
|
-
__name(formatServiceList, "formatServiceList");
|
|
266
|
-
program.parse(process.argv);
|
|
267
|
-
async function waitForApi(interval) {
|
|
268
|
-
try {
|
|
269
|
-
const res = await axios.get(`http://localhost:${process.env.API_PORT || "5001"}/healthz`);
|
|
270
|
-
if ((res?.status || 500) < 300) {
|
|
271
|
-
return;
|
|
272
|
-
}
|
|
273
|
-
} catch (err) {
|
|
274
|
-
}
|
|
275
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
276
|
-
return waitForApi(interval);
|
|
277
|
-
}
|
|
278
|
-
__name(waitForApi, "waitForApi");
|
|
279
|
-
async function waitForFrappe(interval) {
|
|
280
|
-
try {
|
|
281
|
-
const res = await axios.get(`${process.env.FRAPPE_BASE_URL || "http://frappe.localhost"}/api/method/ping`);
|
|
282
|
-
if ((res?.status || 500) < 300) {
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
} catch (err) {
|
|
286
|
-
}
|
|
287
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
288
|
-
return waitForFrappe(interval);
|
|
289
|
-
}
|
|
290
|
-
__name(waitForFrappe, "waitForFrappe");
|
|
291
|
-
async function waitForPostgres(interval) {
|
|
292
|
-
const client = new pg.Client({
|
|
293
|
-
database: process.env.POSTGRES_DATABASE || "postgres",
|
|
294
|
-
host: process.env.POSTGRES_HOSTNAME || "localhost",
|
|
295
|
-
password: process.env.POSTGRES_PASSWORD || "postgres",
|
|
296
|
-
port: Number.parseInt(process.env.POSTGRES_PORT || "5432"),
|
|
297
|
-
user: process.env.POSTGRES_USERNAME || "postgres"
|
|
298
|
-
});
|
|
299
|
-
try {
|
|
300
|
-
await client.connect();
|
|
301
|
-
while (true) {
|
|
302
|
-
try {
|
|
303
|
-
await client.query("SELECT 1");
|
|
304
|
-
return;
|
|
305
|
-
} catch (error) {
|
|
306
|
-
}
|
|
307
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
308
|
-
}
|
|
309
|
-
} finally {
|
|
310
|
-
await client.end();
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
__name(waitForPostgres, "waitForPostgres");
|
|
314
|
-
async function waitForKeycloak(interval) {
|
|
315
|
-
try {
|
|
316
|
-
const res = await axios.get(`${process.env.KEYCLOAK_BASE_URL || "http://localhost:8080"}/realms/master/.well-known/openid-configuration`);
|
|
317
|
-
if ((res?.status || 500) < 300) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
} catch (err) {
|
|
321
|
-
}
|
|
322
|
-
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
323
|
-
return waitForKeycloak(interval);
|
|
324
547
|
}
|
|
325
|
-
__name(waitForKeycloak, "waitForKeycloak");
|