@slim-lang/core 1.2.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.
Files changed (52) hide show
  1. package/README.md +666 -0
  2. package/package.json +55 -0
  3. package/packages/slim/.spm +7 -0
  4. package/packages/slim/converters/main.slim +106 -0
  5. package/packages/slim/helpers/array.slim +25 -0
  6. package/packages/slim/helpers/path.slim +3 -0
  7. package/packages/slim/helpers/request.slim +102 -0
  8. package/packages/slim/helpers/string.slim +27 -0
  9. package/packages/slim/main.slim +42 -0
  10. package/packages/slim/parse/main.slim +25 -0
  11. package/packages/slim/server/main.slim +423 -0
  12. package/packages/slim/time/main.slim +66 -0
  13. package/packages/slim/types/common.slim +6 -0
  14. package/packages/slim/types/formats.slim +23 -0
  15. package/packages/slim/types/hash.slim +6 -0
  16. package/packages/slim/types/mails.slim +3 -0
  17. package/packages/slim/types/numerical.slim +9 -0
  18. package/packages/slim/types/time.slim +3 -0
  19. package/run-dev-slim.js +133 -0
  20. package/run-slim.js +20 -0
  21. package/src/bin/api/github_auth.js +89 -0
  22. package/src/bin/api/github_get.js +139 -0
  23. package/src/bin/api/github_req.js +455 -0
  24. package/src/bin/api/lock.js +37 -0
  25. package/src/bin/api/spm.js +103 -0
  26. package/src/bin/api/storage.js +30 -0
  27. package/src/bin/cli.js +404 -0
  28. package/src/bin/config.default.json +5 -0
  29. package/src/bin/helpers.js +147 -0
  30. package/src/bin/parsers/spm.js +174 -0
  31. package/src/bin/spm.js +519 -0
  32. package/src/checker.js +926 -0
  33. package/src/compile.js +230 -0
  34. package/src/external/classErrors.js +202 -0
  35. package/src/external/client.js +38 -0
  36. package/src/external/core.js +861 -0
  37. package/src/external/defaults.js +25 -0
  38. package/src/external/helpers.js +541 -0
  39. package/src/external/slim-globals.d.ts +65 -0
  40. package/src/external/types.js +38 -0
  41. package/src/format.js +81 -0
  42. package/src/handlers/errorHandler.js +43 -0
  43. package/src/handlers/parser/components.js +250 -0
  44. package/src/handlers/parserHandler.js +793 -0
  45. package/src/jsdoc.js +273 -0
  46. package/src/lexer.js +174 -0
  47. package/src/modulePaths.js +74 -0
  48. package/src/parser.js +818 -0
  49. package/src/repl.js +32 -0
  50. package/src/sourcemap.js +0 -0
  51. package/src/test-runner.js +62 -0
  52. package/src/transform.js +765 -0
@@ -0,0 +1,174 @@
1
+ import path from "node:path"
2
+ import { formatItalic, getPackagePath, isPackageExists } from "../helpers.js"
3
+ import { existsSync } from "node:fs"
4
+ import { readFile } from "node:fs/promises"
5
+
6
+ export function validateGitHub(value) {
7
+ const regex = /^[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\/[a-zA-Z0-9._-]+$/;
8
+
9
+ if (regex.test(value)) {
10
+ return {
11
+ success: true,
12
+ value
13
+ };
14
+ }
15
+
16
+ try {
17
+ const url = new URL(value);
18
+
19
+ if (url.hostname === "github.com") {
20
+ const parts = url.pathname.split("/").filter(Boolean);
21
+
22
+ if (parts.length >= 2) {
23
+ const github = `${parts[0]}/${parts[1]}`;
24
+
25
+ return {
26
+ success: false,
27
+ value: github,
28
+ msg: `Use "${github}" instead of "${value}"`
29
+ };
30
+ }
31
+ }
32
+ } catch {}
33
+
34
+ return {
35
+ success: false,
36
+ msg: `Invalid GitHub repository. Expected "user/repo".`
37
+ };
38
+ }
39
+
40
+ export async function getSPM(pkgName) {
41
+ if (isPackageExists(pkgName)) {
42
+ const pkgPath = getPackagePath(pkgName)
43
+ const dotSpmPath = path.join(pkgPath, ".spm")
44
+
45
+ if (existsSync(dotSpmPath)) {
46
+ try {
47
+ const data = await readFile(dotSpmPath, 'utf8');
48
+ return {
49
+ success: true,
50
+ content: parseSPM(data)
51
+ }
52
+ } catch (err) {
53
+ return {
54
+ success: false,
55
+ content: String(err)
56
+ }
57
+ }
58
+ }
59
+ else {
60
+ return {
61
+ success: false,
62
+ content: ".spm file doest not exists inside the package"
63
+ }
64
+ }
65
+ }
66
+ else return {
67
+ success: false,
68
+ content: "Package does not exists"
69
+ }
70
+ }
71
+
72
+ export function parseSPM(content) {
73
+ const result = {};
74
+ const required = ["name", "version"]
75
+ const allowedRoot = new Set(["name", "version", "description"]);
76
+
77
+ const githubSectionAllowed = new Set(["repo", "organization"]);
78
+
79
+ let currentSection = null;
80
+
81
+ for (const line of content.split(/\r?\n/)) {
82
+ const trimmed = line.trim();
83
+
84
+ if (!trimmed || trimmed.startsWith("#")) {
85
+ continue;
86
+ }
87
+
88
+ if (trimmed.startsWith("@")) {
89
+ const section = trimmed.slice(1).trim();
90
+
91
+ if (!section) {
92
+ throw new Error("empty section name");
93
+ }
94
+
95
+ currentSection = section.split("/");
96
+
97
+ let target = result;
98
+
99
+ for (const part of currentSection) {
100
+ if (!target[part]) {
101
+ target[part] = {};
102
+ }
103
+
104
+ target = target[part];
105
+ }
106
+
107
+ continue;
108
+ }
109
+
110
+ const match = trimmed.match(/^([a-zA-Z_][\w-]*)\s*=\s*"([^"]*)"$/);
111
+
112
+ if (!match) {
113
+ throw new Error(`invalid line: ${trimmed}`);
114
+ }
115
+
116
+ const [, key, value] = match;
117
+
118
+ if (!currentSection) {
119
+ if (!allowedRoot.has(key)) {
120
+ throw new Error(`unknown property "${key}"`);
121
+ }
122
+
123
+ if (result[key] !== undefined) {
124
+ throw new Error(`duplicate property "${key}"`);
125
+ }
126
+
127
+ result[key] = value;
128
+ continue;
129
+ }
130
+
131
+ const sectionName = currentSection.join("/");
132
+
133
+ if (sectionName === "github") {
134
+ if (!githubSectionAllowed.has(key)) {
135
+ throw new Error(
136
+ `unknown property "${key}" in @${sectionName} section`
137
+ );
138
+ }
139
+
140
+ if (key == "repo") {
141
+ const repoTest = validateGitHub(value)
142
+ if (!repoTest.success) {
143
+ throw new Error(`@${sectionName}/${key}: ${repoTest.msg}`);
144
+ }
145
+ }
146
+
147
+ if (key === "organization" && value !== "true" && value !== "false") {
148
+ throw new Error(`@${sectionName}/${key} must be "true" or "false"`);
149
+ }
150
+ }
151
+
152
+ let target = result;
153
+
154
+ for (const part of currentSection) {
155
+ target = target[part];
156
+ }
157
+
158
+ if (target[key] !== undefined) {
159
+ throw new Error(`duplicate property "${key}"`);
160
+ }
161
+
162
+ target[key] = (sectionName === "github" && key === "organization")
163
+ ? value === "true"
164
+ : value;
165
+ }
166
+
167
+ for (const key of required) {
168
+ if (!result[key]) {
169
+ throw new Error(`missing required property "${key}"`);
170
+ }
171
+ }
172
+
173
+ return result;
174
+ }
package/src/bin/spm.js ADDED
@@ -0,0 +1,519 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from "commander";
4
+ import { formatError, formatBold, formatItalic, formatSuccess, createFolder, createFile } from "./helpers.js"
5
+ import pkg from "../../package.json" with { type: "json" };
6
+ import { log, isPackageExists, rootPath, deleteDirectory, loading, delay } from "./helpers.js";
7
+
8
+ import fs from "node:fs"
9
+ import path from "node:path"
10
+ import { getSPM } from "./parsers/spm.js";
11
+ import { loginGitHub } from "./api/github_auth.js";
12
+ import { deleteGitHubToken, getGitHubToken, saveGitHubToken } from "./api/storage.js";
13
+ import { downloadGitHubRepo, getGitHubUser } from "./api/github_get.js";
14
+
15
+ import { getPackage, publishPackage, removePackage, updatePackage } from "./api/spm.js"
16
+ import { readLock, setLockEntry, removeLockEntry } from "./api/lock.js"
17
+ import { createGitHubRepo, deleteGitHubRepo, githubHeaders, publishToGitHub, updateGitHubRepo } from "./api/github_req.js";
18
+
19
+ import open from "open";
20
+
21
+ export const spm = new Command();
22
+
23
+ function spmlog(...args) {
24
+ console.log("[SPM]", ...args)
25
+ }
26
+ function errlog(...args) {
27
+ console.log(formatError(...args))
28
+ }
29
+
30
+ const packagesPath = path.join(rootPath, "packages")
31
+
32
+ function renderObject(obj, indent = 0) {
33
+ let output = "";
34
+ const prefix = " ".repeat(indent);
35
+
36
+ Object.entries(obj).forEach(([key, value]) => {
37
+ if (value && typeof value === "object" && !Array.isArray(value)) {
38
+ output += `\n${formatItalic(prefix)}${formatBold(key)}:`;
39
+ output += renderObject(value, indent + 2);
40
+ } else {
41
+ output += `\n${formatItalic(prefix)}${formatBold(key)}: ${value}`;
42
+ }
43
+ });
44
+
45
+ return output;
46
+ }
47
+
48
+ function renderBlock({ name, content }) {
49
+ function generateSepLines(len) {
50
+ let line = `-------------------------------------`
51
+
52
+ if (len) {
53
+ for (let i = 0; i < len; i++) {
54
+ line += "-"
55
+ }
56
+ }
57
+
58
+ return line
59
+ }
60
+
61
+ const nameLen = name.length
62
+
63
+ return `${formatSuccess(name)} ${generateSepLines()}` +
64
+ content +
65
+ `\n${generateSepLines(nameLen + 1)}`
66
+ }
67
+
68
+ spm
69
+ .name("spm")
70
+ .description(`Slim Package Manager (SPM ${pkg.version})`)
71
+ .version(pkg.version);
72
+
73
+ spm
74
+ .command("list")
75
+ .action(() => {
76
+ const targetDir = path.join(rootPath, "packages")
77
+ if (!fs.existsSync(targetDir)) {
78
+ spmlog("No packages installed (the packages/ directory does not exist)")
79
+ return
80
+ }
81
+
82
+ const relativePaths = fs.readdirSync(targetDir);
83
+
84
+ if (relativePaths.length === 0) {
85
+ spmlog("No packages installed")
86
+ return
87
+ }
88
+
89
+ function getPkgPath(pkg) {
90
+ const relative = path.relative(process.cwd(), path.join(targetDir, pkg))
91
+ return relative
92
+ }
93
+
94
+ spmlog(`List of all installed packages:\n${relativePaths.map(item => "- @" + item + ` (${getPkgPath(item)})`).join(",\n")}`)
95
+ });
96
+
97
+ spm
98
+ .command("lock")
99
+ .description("Show resolved package versions from spm.lock.json")
100
+ .action(() => {
101
+ const { packages } = readLock()
102
+ const names = Object.keys(packages)
103
+
104
+ if (names.length === 0) {
105
+ spmlog("No locked packages (spm.lock.json is empty or missing)")
106
+ return
107
+ }
108
+
109
+ spmlog(`Locked packages:\n${names.map(name => `- @${name}:${packages[name].version} (${packages[name].repo})`).join("\n")}`)
110
+ });
111
+
112
+ spm
113
+ .command("get")
114
+ .argument("<name>")
115
+ .action(async (name) => {
116
+ let msg = null;
117
+
118
+ if (isPackageExists(name)) {
119
+ const spmFileContent = await getSPM(name);
120
+
121
+ if (!spmFileContent.success) {
122
+ errlog(spmFileContent.content);
123
+ return;
124
+ }
125
+
126
+ const SPMContent = spmFileContent.content;
127
+ let packageName = "@" + name
128
+
129
+ const infoFromRegistryReq = await getPackage({ name: name })
130
+
131
+ if(infoFromRegistryReq.success) {
132
+ packageName = `${packageName} (${infoFromRegistryReq.content.latest})`
133
+ }
134
+
135
+ msg = renderBlock({
136
+ name: packageName,
137
+ content: renderObject(SPMContent)
138
+ })
139
+ } else {
140
+ msg =
141
+ formatError(`@${name} not installed\n`) +
142
+ `Want to install? Try ${formatItalic(`${spm.name()} i ${name}`)}`;
143
+ }
144
+
145
+ spmlog(msg);
146
+ });
147
+
148
+ spm
149
+ .command("rm")
150
+ .argument('<name>')
151
+ .option("--g", "Removes the project from the project registry and GitHub")
152
+ .action(async (name, opt) => {
153
+ await loading({
154
+ startMsg: `Removing @${name}...`,
155
+ callback: async ({ fail, ok }) => {
156
+ const isGlobally = opt.g == undefined ? false : true
157
+
158
+ if (isGlobally) {
159
+ const spmFileContent = await getSPM(name);
160
+
161
+ if (!spmFileContent.success) {
162
+ return fail("SPM Check:", spmFileContent.content);
163
+ }
164
+
165
+ const SPMContent = spmFileContent.content;
166
+ const githubRepo = SPMContent.github.repo
167
+
168
+ const githubToken = await getGitHubToken()
169
+
170
+ const removePackageRegReq = await removePackage({
171
+ token: githubToken,
172
+ name: name
173
+ })
174
+
175
+ if (!removePackageRegReq.success) {
176
+ return fail("SPM Registry remove:", removePackageRegReq.content)
177
+ }
178
+
179
+ const removeFromGithubReq = await deleteGitHubRepo(githubToken, githubRepo)
180
+
181
+ if (!removeFromGithubReq.success) {
182
+ return fail(`${githubRepo} failed to remove from Github`)
183
+ }
184
+ else {
185
+ return ok(`${githubRepo} removed from Github and registry`)
186
+ }
187
+ }
188
+
189
+ const res = await deleteDirectory(path.join(rootPath, "packages", name))
190
+
191
+ if (res) {
192
+ removeLockEntry(name)
193
+ return ok(`package @${name} successfully removed locally`)
194
+ }
195
+ else {
196
+ return fail(`Failed to remove the @${name} package`)
197
+ }
198
+ }
199
+ })
200
+ });
201
+
202
+ spm
203
+ .command("i")
204
+ .argument('<name>')
205
+ .option("--ver <version>", "Download a specific version")
206
+ .action(async (name, opt) => {
207
+ await loading({
208
+ startMsg: `Installing @${name}...`,
209
+ callback: async ({ fail, ok }) => {
210
+ const getPackageReq = await getPackage({ name: name })
211
+
212
+ if (!getPackageReq.success) {
213
+ return fail("SPM Install:", getPackageReq.content)
214
+ }
215
+ else {
216
+ const githubToken = await getGitHubToken()
217
+ const data = getPackageReq.content
218
+
219
+ const repo = data.repo
220
+ const version = opt.ver == undefined ? data.latest : opt.ver
221
+
222
+ console.log(`\nDownloading ${repo}...`)
223
+
224
+ const downloadRepoReq = await downloadGitHubRepo(githubToken, repo, path.join(packagesPath, name), opt.ver)
225
+
226
+ if (!downloadRepoReq.success) {
227
+ return fail(downloadRepoReq.msg)
228
+ }
229
+ else {
230
+ setLockEntry(name, { version, repo })
231
+ return ok(`Package @${name}:${version} installed`)
232
+ }
233
+ }
234
+ }
235
+ })
236
+ });
237
+
238
+ spm
239
+ .command("create")
240
+ .argument("<name>")
241
+
242
+ .option("--github", "Create repository on Github")
243
+ .option("--local", "Create minimum package template in packages")
244
+
245
+ .option("--github-repo <repoName>", "Fill in '@github/repo' in the configuration")
246
+ .option("--description <description>", "Fill in 'description' in the configuration")
247
+ .option("--ver <version>", "Fill in 'version' in the configuration")
248
+
249
+ .action(async (name, opt) => {
250
+ const defaultGithubRepo = opt.githubRepo == undefined ? "username/repo" : opt.githubRepo
251
+ const defaultDescription = opt.description == undefined ? "My first Slim package" : opt.description
252
+ const defaultVersion = opt.ver == undefined ? "1.0.0" : opt.ver
253
+
254
+ if (opt.local) {
255
+ const isExists = isPackageExists(name)
256
+
257
+ await loading({
258
+ startMsg: `Creating path for ${name}`,
259
+ callback: async ({ fail, ok }) => {
260
+ const packagePath = path.join(rootPath, "packages", name)
261
+
262
+ if (isExists) return fail("Package is already exists")
263
+ else {
264
+ const createFolderReq = await createFolder(packagePath)
265
+
266
+ if (!createFolderReq.success) {
267
+ return fail(createFolderReq.content)
268
+ }
269
+ else {
270
+ const createSPMReq = createFile(path.join(packagePath, ".spm"), `
271
+ name = "${name}"
272
+ description = "${defaultDescription}"
273
+ version = "${defaultVersion}"
274
+
275
+ @ github
276
+ repo = "${defaultGithubRepo}"
277
+ `.trim())
278
+
279
+ if (!createSPMReq.success) {
280
+ return fail(createSPMReq.content)
281
+ }
282
+ else {
283
+ return ok("Local package created")
284
+ }
285
+ }
286
+ }
287
+ }
288
+ })
289
+ }
290
+ else if (opt.github || opt.githubRepo) {
291
+ const spmFileContent = await getSPM(name);
292
+
293
+ if (!spmFileContent.success) {
294
+ spmlog(formatError(spmFileContent.content));
295
+ return;
296
+ }
297
+
298
+ const SPMContent = spmFileContent.content;
299
+ const packageName = "@" + name;
300
+ const githubRepo = SPMContent.github.repo
301
+ const organization = SPMContent.github.organization === true
302
+
303
+ await loading({
304
+ startMsg: `Creating repository for ${packageName}`,
305
+ callback: async ({ fail, ok }) => {
306
+ const githubToken = await getGitHubToken();
307
+
308
+ const createRepoReq = await createGitHubRepo(githubToken, githubRepo, organization)
309
+
310
+ if (!createRepoReq.success) {
311
+ return fail(createRepoReq.msg)
312
+ }
313
+ else {
314
+ return ok(createRepoReq.msg)
315
+ }
316
+ },
317
+ })
318
+ }
319
+ })
320
+
321
+ spm
322
+ .command("publish")
323
+ .argument('<name>')
324
+ .action(async (name) => {
325
+ if (isPackageExists(name)) {
326
+ const spmFileContent = await getSPM(name);
327
+
328
+ if (!spmFileContent.success) {
329
+ spmlog(formatError(spmFileContent.content));
330
+ return;
331
+ }
332
+
333
+ const SPMContent = spmFileContent.content;
334
+ const packageName = "@" + name;
335
+
336
+ const githubRepo = SPMContent.github.repo
337
+ const version = SPMContent.version
338
+ const description = SPMContent.description
339
+ const packagePath = path.join("packages", name)
340
+
341
+ if ("github" in SPMContent && "repo" in SPMContent.github) {
342
+ await loading({
343
+ startMsg: `Publishing ${packageName}`,
344
+ callback: async ({ fail, ok }) => {
345
+ const githubToken = await getGitHubToken()
346
+
347
+ const installationsReq = await fetch(
348
+ "https://api.github.com/user/installations",
349
+ {
350
+ headers: githubHeaders(githubToken),
351
+ },
352
+ );
353
+
354
+ const installations = await installationsReq.json()
355
+
356
+ if ("total_count" in installations) {
357
+ if (installations.total_count == 0) {
358
+ await open(
359
+ "https://github.com/apps/slim-package-manager/installations/new",
360
+ );
361
+
362
+ console.log("\n")
363
+ spmlog(formatError(`No application has been installed on the "${githubRepo}" repository\n`))
364
+ spmlog("Open:", formatBold.underline("https://github.com/apps/slim-package-manager/installations/new"))
365
+ spmlog("Press Enter after installation...\n")
366
+
367
+ return fail(`No application has been installed on the "${githubRepo}" repository`)
368
+ }
369
+ else {
370
+ const githubPublishReq = await publishToGitHub({
371
+ token: githubToken,
372
+ repo: githubRepo,
373
+ contentPath: packagePath
374
+ })
375
+
376
+ const packageInfoReq = await getPackage({
377
+ name: name
378
+ })
379
+
380
+ if (!githubPublishReq.success) {
381
+ return fail("Github publish:", githubPublishReq.msg)
382
+ }
383
+ else {
384
+ const isPackagesExists = packageInfoReq.success
385
+
386
+ if (isPackagesExists) {
387
+ console.log("\nPackage is already exists... updating")
388
+
389
+ const updatePackageReq = await updatePackage({
390
+ token: githubToken,
391
+ name: name,
392
+ description: description,
393
+ version: version,
394
+ github: githubRepo
395
+ })
396
+
397
+ if (!updatePackageReq.success) {
398
+ return fail("Something went wrong while requesting a package update. Please try again later")
399
+ }
400
+ else {
401
+ console.log("\nUpdating package on Github...")
402
+
403
+ const updateReq = await updateGitHubRepo({
404
+ token: githubToken,
405
+ repo: githubRepo,
406
+ packagePath: packagePath,
407
+ version: updatePackageReq.content.latest
408
+ })
409
+
410
+ if (!updateReq.success) {
411
+ return fail("Failed to push the new version to GitHub")
412
+ }
413
+ else {
414
+ const publishedVer = updateReq.version
415
+ return ok(`@${name} ${publishedVer} published on Github`)
416
+ }
417
+ }
418
+ }
419
+ else {
420
+ const publishReq = await publishPackage({
421
+ token: githubToken,
422
+ name: name,
423
+ version: version,
424
+ github: githubRepo,
425
+ description: description
426
+ })
427
+
428
+ if (!publishReq.success) {
429
+ return fail("SPM Registry publishing:", publishReq.content)
430
+ }
431
+ else {
432
+ return ok(`${packageName} published and registred: ${name} v${version}`)
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }
438
+ else {
439
+ return fail("Installations:", installations.message)
440
+ }
441
+ }
442
+ })
443
+ }
444
+ else {
445
+ spmlog(formatError("No GitHub repository specified"));
446
+ }
447
+ } else {
448
+ spmlog(formatError(`No package founded: /packages/${name}`));
449
+ }
450
+ });
451
+
452
+ spm
453
+ .command("login")
454
+ .description("Connect your GitHub account")
455
+ .option("--check", "Check if you logged in")
456
+ .action(async (opt) => {
457
+ if (opt.check) {
458
+ const token = await getGitHubToken()
459
+
460
+ if (token) {
461
+ try {
462
+ const user = await getGitHubUser(token)
463
+
464
+ spmlog(formatSuccess("Github connected"))
465
+ spmlog(`${formatBold("Account")}: ${user.name} (${user.html_url})`)
466
+ }
467
+ catch (e) {
468
+ spmlog(formatError(String(e)))
469
+ }
470
+ }
471
+ else {
472
+ spmlog(formatError(`Github NOT connected. Use ${formatItalic.underline(spm.name() + " login")} to login via Github`))
473
+ }
474
+ }
475
+ else {
476
+ await loading({
477
+ startMsg: `Waiting for GitHub authorization...`,
478
+ callback: async () => {
479
+ try {
480
+ const token = await loginGitHub();
481
+ await saveGitHubToken(token)
482
+
483
+ return {
484
+ success: true,
485
+ msg: "Github account connected"
486
+ }
487
+ }
488
+ catch (error) {
489
+ return {
490
+ success: false,
491
+ msg:`${error.message}`
492
+ }
493
+ }
494
+ }
495
+ })
496
+ }
497
+ });
498
+
499
+ spm
500
+ .command("logout")
501
+ .description("Disconnect GitHub account")
502
+ .action(async () => {
503
+ try {
504
+ await deleteGitHubToken();
505
+
506
+ spmlog(formatSuccess("GitHub account disconnected"));
507
+ } catch (error) {
508
+ spmlog(formatError(`✗ ${error.message}`));
509
+ process.exitCode = 1;
510
+ }
511
+ });
512
+
513
+ try {
514
+ spm.parse();
515
+ }
516
+ catch (err) {
517
+ spmlog(formatError(err?.message ?? String(err)))
518
+ process.exitCode = 1
519
+ }