@valbuild/cli 0.72.4 → 0.73.1

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.
@@ -1,27 +1,101 @@
1
1
  import meow from 'meow';
2
2
  import chalk from 'chalk';
3
3
  import path from 'path';
4
- import { createService, createFixPatch } from '@valbuild/server';
5
- import { Internal, FILE_REF_PROP, VAL_EXTENSION } from '@valbuild/core';
4
+ import { createService, getPersonalAccessTokenPath, parsePersonalAccessTokenFile, getSettings, uploadRemoteFile, createFixPatch } from '@valbuild/server';
5
+ import { DEFAULT_VAL_REMOTE_HOST, Internal, FILE_REF_PROP, VAL_EXTENSION } from '@valbuild/core';
6
6
  import { glob } from 'fast-glob';
7
7
  import picocolors from 'picocolors';
8
8
  import { ESLint } from 'eslint';
9
9
  import fs from 'fs/promises';
10
+ import vm from 'node:vm';
11
+ import ts from 'typescript';
12
+ import z from 'zod';
13
+ import fs$1 from 'fs';
10
14
 
11
15
  function error(message) {
12
16
  console.error(chalk.red("❌Error: ") + message);
13
17
  }
14
18
 
19
+ const ValConfigSchema = z.object({
20
+ project: z.string().optional(),
21
+ root: z.string().optional(),
22
+ files: z.object({
23
+ directory: z.string().refine(val => val.startsWith("/public/val"), {
24
+ message: "files.directory must start with '/public/val'"
25
+ })
26
+ }).optional(),
27
+ gitCommit: z.string().optional(),
28
+ gitBranch: z.string().optional(),
29
+ defaultTheme: z.union([z.literal("light"), z.literal("dark")]).optional(),
30
+ ai: z.object({
31
+ commitMessages: z.object({
32
+ disabled: z.boolean().optional()
33
+ }).optional()
34
+ }).optional()
35
+ });
36
+ async function evalValConfigFile(projectRoot, configFileName) {
37
+ const valConfigPath = path.join(projectRoot, configFileName);
38
+ let code = null;
39
+ try {
40
+ code = await fs.readFile(valConfigPath, "utf-8");
41
+ } catch (err) {
42
+ //
43
+ }
44
+ if (!code) {
45
+ return null;
46
+ }
47
+ const transpiled = ts.transpileModule(code, {
48
+ compilerOptions: {
49
+ target: ts.ScriptTarget.ES2020,
50
+ module: ts.ModuleKind.CommonJS,
51
+ esModuleInterop: true
52
+ },
53
+ fileName: valConfigPath
54
+ });
55
+ const exportsObj = {};
56
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
+ const sandbox = {
58
+ exports: exportsObj,
59
+ module: {
60
+ exports: exportsObj
61
+ },
62
+ require,
63
+ // NOTE: this is a security risk, but this code is running in the users own environment at the CLI level
64
+ __filename: valConfigPath,
65
+ __dirname: path.dirname(valConfigPath),
66
+ console,
67
+ process
68
+ };
69
+ sandbox.global = sandbox;
70
+ const context = vm.createContext(sandbox);
71
+ const script = new vm.Script(transpiled.outputText, {
72
+ filename: valConfigPath
73
+ });
74
+ script.runInContext(context);
75
+ const valConfig = sandbox.module.exports.config;
76
+ if (!valConfig) {
77
+ throw Error(`Val config file at path: '${valConfigPath}' must export a config object. Got: ${valConfig}`);
78
+ }
79
+ const result = ValConfigSchema.safeParse(valConfig);
80
+ if (!result.success) {
81
+ throw Error(`Val config file at path: '${valConfigPath}' has invalid schema: ${result.error.message}`);
82
+ }
83
+ return result.data;
84
+ }
85
+
15
86
  async function validate({
16
87
  root,
17
88
  fix,
18
89
  noEslint
19
90
  }) {
91
+ const valRemoteHost = process.env.VAL_REMOTE_HOST || DEFAULT_VAL_REMOTE_HOST;
20
92
  const projectRoot = root ? path.resolve(root) : process.cwd();
21
93
  const eslint = new ESLint({
22
94
  cwd: projectRoot,
23
95
  ignore: false
24
96
  });
97
+ const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
98
+ console.log(picocolors.greenBright(`Validating project${valConfigFile !== null && valConfigFile !== void 0 && valConfigFile.project ? ` '${picocolors.inverse(valConfigFile === null || valConfigFile === void 0 ? void 0 : valConfigFile.project)}'` : ""}...`));
25
99
  const service = await createService(projectRoot, {});
26
100
  const checkKeyIsValid = async (key, sourcePath) => {
27
101
  const [moduleFilePath, modulePath] = Internal.splitModuleFilePathAndModulePath(sourcePath);
@@ -89,7 +163,8 @@ async function validate({
89
163
  });
90
164
  console.log(errors === 0 ? picocolors.green("✔") : picocolors.red("✘"), "ESlint complete", lintFiles.length, "files");
91
165
  }
92
- console.log("Validating...", valFiles.length, "files");
166
+ console.log(picocolors.greenBright(`Found ${valFiles.length} files...`));
167
+ let publicProjectId;
93
168
  let didFix = false; // TODO: ugly
94
169
  async function validateFile(file) {
95
170
  var _eslintResultsByFile;
@@ -102,6 +177,9 @@ async function validate({
102
177
  });
103
178
  const fileContent = await fs.readFile(path.join(projectRoot, file), "utf-8");
104
179
  const eslintResult = (_eslintResultsByFile = eslintResultsByFile) === null || _eslintResultsByFile === void 0 ? void 0 : _eslintResultsByFile[file];
180
+ const remoteFiles = {};
181
+ let remoteFileBuckets = null;
182
+ let remoteFilesCounter = 0;
105
183
  eslintResult === null || eslintResult === void 0 || eslintResult.messages.forEach(m => {
106
184
  // display surrounding code
107
185
  logEslintMessage(fileContent, moduleFilePath, m);
@@ -112,6 +190,7 @@ async function validate({
112
190
  } else {
113
191
  var _eslintResultsByFile2;
114
192
  let errors = ((_eslintResultsByFile2 = eslintResultsByFile) === null || _eslintResultsByFile2 === void 0 || (_eslintResultsByFile2 = _eslintResultsByFile2[file]) === null || _eslintResultsByFile2 === void 0 ? void 0 : _eslintResultsByFile2.messages.reduce((prev, m) => m.severity >= 2 ? prev + 1 : prev, 0)) || 0;
193
+ let fixedErrors = 0;
115
194
  if (valModule.errors) {
116
195
  if (valModule.errors.validation) {
117
196
  for (const [sourcePath, validationErrors] of Object.entries(valModule.errors.validation)) {
@@ -122,20 +201,24 @@ async function validate({
122
201
  ) || v.fixes.includes("image:check-metadata") || v.fixes.includes("image:add-metadata") || v.fixes.includes("file:check-metadata") || v.fixes.includes("file:add-metadata")) {
123
202
  const [, modulePath] = Internal.splitModuleFilePathAndModulePath(sourcePath);
124
203
  if (valModule.source && valModule.schema) {
125
- var _fileSource$source;
126
204
  const fileSource = Internal.resolvePath(modulePath, valModule.source, valModule.schema);
127
- const filePath = path.join(projectRoot, // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
- (_fileSource$source = fileSource.source) === null || _fileSource$source === void 0 ? void 0 : _fileSource$source[FILE_REF_PROP]);
205
+ let filePath = null;
129
206
  try {
207
+ var _fileSource$source;
208
+ filePath = path.join(projectRoot, // eslint-disable-next-line @typescript-eslint/no-explicit-any
209
+ (_fileSource$source = fileSource.source) === null || _fileSource$source === void 0 ? void 0 : _fileSource$source[FILE_REF_PROP]);
130
210
  await fs.access(filePath);
131
211
  } catch {
132
- console.log(picocolors.red("✘"), `File ${filePath} does not exist`);
212
+ if (filePath) {
213
+ console.log(picocolors.red("✘"), `File ${filePath} does not exist`);
214
+ } else {
215
+ console.log(picocolors.red("✘"), `Expected file to be defined at: ${sourcePath} but no file was found`);
216
+ }
133
217
  errors += 1;
134
218
  continue;
135
219
  }
136
220
  }
137
221
  } else if (v.fixes.includes("keyof:check-keys")) {
138
- const prevErrors = errors;
139
222
  if (v.value && typeof v.value === "object" && "key" in v.value && "sourcePath" in v.value) {
140
223
  const {
141
224
  key,
@@ -158,32 +241,168 @@ async function validate({
158
241
  console.log(picocolors.red("✘"), "Unexpected error in", `${sourcePath}:`, v.message, " (Expected value to be an object with 'key' and 'sourcePath' properties - this is likely a bug in Val)");
159
242
  errors += 1;
160
243
  }
161
- if (prevErrors < errors) {
162
- console.log(picocolors.red("✘"), "Found error in", `${sourcePath}`);
244
+ } else if (v.fixes.includes("image:upload-remote") || v.fixes.includes("file:upload-remote")) {
245
+ if (!fix) {
246
+ console.log(picocolors.red("✘"), `Remote file ${sourcePath} needs to be uploaded (use --fix to upload)`);
247
+ errors += 1;
248
+ continue;
249
+ }
250
+ const [, modulePath] = Internal.splitModuleFilePathAndModulePath(sourcePath);
251
+ if (valModule.source && valModule.schema) {
252
+ const resolvedRemoteFileAtSourcePath = Internal.resolvePath(modulePath, valModule.source, valModule.schema);
253
+ let filePath = null;
254
+ try {
255
+ var _resolvedRemoteFileAt;
256
+ filePath = path.join(projectRoot, // eslint-disable-next-line @typescript-eslint/no-explicit-any
257
+ (_resolvedRemoteFileAt = resolvedRemoteFileAtSourcePath.source) === null || _resolvedRemoteFileAt === void 0 ? void 0 : _resolvedRemoteFileAt[FILE_REF_PROP]);
258
+ await fs.access(filePath);
259
+ } catch {
260
+ if (filePath) {
261
+ console.log(picocolors.red("✘"), `File ${filePath} does not exist`);
262
+ } else {
263
+ console.log(picocolors.red("✘"), `Expected file to be defined at: ${sourcePath} but no file was found`);
264
+ }
265
+ errors += 1;
266
+ continue;
267
+ }
268
+ const patFile = getPersonalAccessTokenPath(projectRoot);
269
+ try {
270
+ await fs.access(patFile);
271
+ } catch {
272
+ // TODO: display this error only once:
273
+ console.log(picocolors.red("✘"), `File: ${path.join(projectRoot, file)} has remote images that are not uploaded and you are not logged in.\n\nFix this error by logging in:\n\t"npx val login"\n`);
274
+ errors += 1;
275
+ continue;
276
+ }
277
+ const parsedPatFile = parsePersonalAccessTokenFile(await fs.readFile(patFile, "utf-8"));
278
+ if (!parsedPatFile.success) {
279
+ console.log(picocolors.red("✘"), `Error parsing personal access token file: ${parsedPatFile.error}. You need to login again.`);
280
+ errors += 1;
281
+ continue;
282
+ }
283
+ const {
284
+ pat
285
+ } = parsedPatFile.data;
286
+ if (remoteFiles[sourcePath]) {
287
+ console.log(picocolors.yellow("⚠"), `Remote file ${filePath} already uploaded`);
288
+ continue;
289
+ }
290
+ // TODO: parallelize this:
291
+ console.log(picocolors.yellow("⚠"), `Uploading remote file ${filePath}...`);
292
+ if (!resolvedRemoteFileAtSourcePath.schema) {
293
+ console.log(picocolors.red("✘"), `Cannot upload remote file: schema not found for ${sourcePath}`);
294
+ errors += 1;
295
+ continue;
296
+ }
297
+ const actualRemoteFileSource = resolvedRemoteFileAtSourcePath.source;
298
+ const fileSourceMetadata = Internal.isFile(actualRemoteFileSource) ? actualRemoteFileSource.metadata : undefined;
299
+ const resolveRemoteFileSchema = resolvedRemoteFileAtSourcePath.schema;
300
+ if (!resolveRemoteFileSchema) {
301
+ console.log(picocolors.red("✘"), `Could not resolve schema for remote file: ${sourcePath}`);
302
+ errors += 1;
303
+ continue;
304
+ }
305
+ if (!publicProjectId || !remoteFileBuckets) {
306
+ let projectName = process.env.VAL_PROJECT;
307
+ if (!projectName) {
308
+ projectName = valConfigFile === null || valConfigFile === void 0 ? void 0 : valConfigFile.project;
309
+ }
310
+ if (!projectName) {
311
+ console.log(picocolors.red("✘"), "Project name not found. Set VAL_PROJECT environment variable or add project name to val.config");
312
+ errors += 1;
313
+ continue;
314
+ }
315
+ const settingsRes = await getSettings(projectName, {
316
+ pat
317
+ });
318
+ if (!settingsRes.success) {
319
+ console.log(picocolors.red("✘"), `Could not get public project id: ${settingsRes.message}.`);
320
+ errors += 1;
321
+ continue;
322
+ }
323
+ publicProjectId = settingsRes.data.publicProjectId;
324
+ remoteFileBuckets = settingsRes.data.remoteFileBuckets.map(b => b.bucket);
325
+ }
326
+ if (!publicProjectId) {
327
+ console.log(picocolors.red("✘"), "Could not get public project id");
328
+ errors += 1;
329
+ continue;
330
+ }
331
+ if (resolveRemoteFileSchema.type !== "image" && resolveRemoteFileSchema.type !== "file") {
332
+ console.log(picocolors.red("✘"), `The schema is the remote is neither image nor file: ${sourcePath}`);
333
+ }
334
+ remoteFilesCounter += 1;
335
+ const bucket = remoteFileBuckets[remoteFilesCounter % remoteFileBuckets.length];
336
+ if (!bucket) {
337
+ console.log(picocolors.red("✘"), `Internal error: could not allocate a bucket for the remote file located at ${sourcePath}`);
338
+ errors += 1;
339
+ continue;
340
+ }
341
+ let fileBuffer;
342
+ try {
343
+ fileBuffer = await fs.readFile(filePath);
344
+ } catch (e) {
345
+ console.log(picocolors.red("✘"), `Error reading file: ${e}`);
346
+ errors += 1;
347
+ continue;
348
+ }
349
+ const relativeFilePath = path.relative(projectRoot, filePath).split(path.sep).join("/");
350
+ if (!relativeFilePath.startsWith("public/val/")) {
351
+ console.log(picocolors.red("✘"), `File path must be within the public/val/ directory (e.g. public/val/path/to/file.txt). Got: ${relativeFilePath}`);
352
+ errors += 1;
353
+ continue;
354
+ }
355
+ const remoteFileUpload = await uploadRemoteFile(valRemoteHost, fileBuffer, publicProjectId, bucket, relativeFilePath, resolveRemoteFileSchema, fileSourceMetadata, {
356
+ pat
357
+ });
358
+ if (!remoteFileUpload.success) {
359
+ console.log(picocolors.red("✘"), `Error uploading remote file: ${remoteFileUpload.error}`);
360
+ errors += 1;
361
+ continue;
362
+ }
363
+ console.log(picocolors.yellow("⚠"), `Uploaded remote file ${filePath}`);
364
+ remoteFiles[sourcePath] = {
365
+ ref: remoteFileUpload.ref,
366
+ metadata: fileSourceMetadata
367
+ };
368
+ }
369
+ } else if (v.fixes.includes("image:download-remote") || v.fixes.includes("file:download-remote")) {
370
+ if (fix) {
371
+ console.log(picocolors.yellow("⚠"), `Downloading remote file in ${sourcePath}...`);
372
+ } else {
373
+ console.log(picocolors.red("✘"), `Remote file ${sourcePath} needs to be downloaded (use --fix to download)`);
374
+ errors += 1;
375
+ continue;
163
376
  }
164
- } else {
165
- console.log(picocolors.red("✘"), "Found error in", `${sourcePath}:`, v.message);
377
+ } else if (v.fixes.includes("image:check-remote") || v.fixes.includes("file:check-remote")) ; else {
378
+ console.log(picocolors.red("✘"), "Unknown fix", v.fixes, "for", sourcePath);
166
379
  errors += 1;
380
+ continue;
167
381
  }
168
382
  const fixPatch = await createFixPatch({
169
- projectRoot
170
- }, !!fix, sourcePath, v);
383
+ projectRoot,
384
+ remoteHost: valRemoteHost
385
+ }, !!fix, sourcePath, v, remoteFiles, valModule.source, valModule.schema);
171
386
  if (fix && fixPatch !== null && fixPatch !== void 0 && fixPatch.patch && (fixPatch === null || fixPatch === void 0 ? void 0 : fixPatch.patch.length) > 0) {
172
387
  await service.patch(moduleFilePath, fixPatch.patch);
173
388
  didFix = true;
389
+ fixedErrors += 1;
174
390
  console.log(picocolors.yellow("⚠"), "Applied fix for", sourcePath);
175
391
  }
176
392
  fixPatch === null || fixPatch === void 0 || (_fixPatch$remainingEr = fixPatch.remainingErrors) === null || _fixPatch$remainingEr === void 0 || _fixPatch$remainingEr.forEach(e => {
177
393
  errors += 1;
178
- console.log(v.fixes ? picocolors.yellow("⚠") : picocolors.red("✘"), `Found ${v.fixes ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
394
+ console.log(e.fixes && e.fixes.length ? picocolors.yellow("⚠") : picocolors.red("✘"), `Got ${e.fixes && e.fixes.length ? "fixable " : ""}error in`, `${sourcePath}:`, e.message);
179
395
  });
180
396
  } else {
181
397
  errors += 1;
182
- console.log(picocolors.red("✘"), "Found error in", `${sourcePath}:`, v.message);
398
+ console.log(picocolors.red("✘"), "Got error in", `${sourcePath}:`, v.message);
183
399
  }
184
400
  }
185
401
  }
186
402
  }
403
+ if (fixedErrors === errors && (!valModule.errors.fatal || valModule.errors.fatal.length == 0)) {
404
+ console.log(picocolors.green("✔"), moduleFilePath, "is valid (" + (Date.now() - start) + "ms)");
405
+ }
187
406
  for (const fatalError of valModule.errors.fatal || []) {
188
407
  errors += 1;
189
408
  console.log(picocolors.red("✘"), moduleFilePath, "is invalid:", fatalError.message);
@@ -191,6 +410,9 @@ async function validate({
191
410
  } else {
192
411
  console.log(picocolors.green("✔"), moduleFilePath, "is valid (" + (Date.now() - start) + "ms)");
193
412
  }
413
+ if (errors > 0) {
414
+ console.log(picocolors.red("✘"), `${`/${file}`} contains ${errors} error${errors > 1 ? "s" : ""}`, " (" + (Date.now() - start) + "ms)");
415
+ }
194
416
  return errors;
195
417
  }
196
418
  }
@@ -208,7 +430,7 @@ async function validate({
208
430
  }
209
431
  }
210
432
  if (errors > 0) {
211
- console.log(picocolors.red("✘"), "Found", errors, "validation error" + (errors > 1 ? "s" : ""));
433
+ console.log(picocolors.red("✘"), "Got", errors, "error" + (errors > 1 ? "s" : ""));
212
434
  process.exit(1);
213
435
  } else {
214
436
  console.log(picocolors.green("✔"), "No validation errors found");
@@ -334,8 +556,8 @@ function isFileRef(value) {
334
556
  return false;
335
557
  }
336
558
 
337
- const getVersions = async () => {
338
- const coreVersion = await (() => {
559
+ const getVersions = () => {
560
+ const coreVersion = (() => {
339
561
  try {
340
562
  var _require;
341
563
  // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
@@ -344,7 +566,7 @@ const getVersions = async () => {
344
566
  return null;
345
567
  }
346
568
  })();
347
- const nextVersion = await (() => {
569
+ const nextVersion = (() => {
348
570
  try {
349
571
  var _require2;
350
572
  // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
@@ -359,6 +581,83 @@ const getVersions = async () => {
359
581
  };
360
582
  };
361
583
 
584
+ const host = process.env.VAL_BUILD_URL || "https://app.val.build";
585
+ async function login(options) {
586
+ try {
587
+ var _response$headers$get;
588
+ console.log(picocolors.cyan("\nStarting login process...\n"));
589
+
590
+ // Step 1: Initiate login and get token and URL
591
+ const response = await fetch(`${host}/api/login`, {
592
+ method: "POST",
593
+ headers: {
594
+ "Content-Type": "application/json"
595
+ }
596
+ });
597
+ let token;
598
+ let url;
599
+ if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
600
+ const text = await response.text();
601
+ console.error(picocolors.red("Unexpected failure while trying to login (content type was not JSON). Server response:"), text || "<empty>");
602
+ process.exit(1);
603
+ }
604
+ const json = await response.json();
605
+ if (json) {
606
+ token = json.nonce;
607
+ url = json.url;
608
+ }
609
+ if (!token || !url) {
610
+ console.error(picocolors.red("Unexpected response from the server."), json);
611
+ process.exit(1);
612
+ }
613
+ console.log(picocolors.green("Open the following URL in your browser to log in:"));
614
+ console.log(picocolors.underline(picocolors.blue(url)));
615
+ console.log(picocolors.dim("\nWaiting for login confirmation...\n"));
616
+
617
+ // Step 2: Poll for login confirmation
618
+ const result = await pollForConfirmation(token);
619
+
620
+ // Step 3: Save the token
621
+ const filePath = getPersonalAccessTokenPath(options.root || process.cwd());
622
+ saveToken(result, filePath);
623
+ } catch (error) {
624
+ console.error(picocolors.red("An error occurred during the login process. Check your internet connection. Details:"), error instanceof Error ? error.message : JSON.stringify(error, null, 2));
625
+ process.exit(1);
626
+ }
627
+ }
628
+ const MAX_DURATION = 5 * 60 * 1000; // 5 minutes
629
+ async function pollForConfirmation(token) {
630
+ const start = Date.now();
631
+ while (Date.now() - start < MAX_DURATION) {
632
+ await new Promise(resolve => setTimeout(resolve, 1000));
633
+ const response = await fetch(`${host}/api/login?token=${token}&consume=true`);
634
+ if (response.status === 500) {
635
+ console.error(picocolors.red("An error occurred on the server."));
636
+ process.exit(1);
637
+ }
638
+ if (response.status === 200) {
639
+ const json = await response.json();
640
+ if (json) {
641
+ if (typeof json.profile.username === "string" && typeof json.pat === "string") {
642
+ return json;
643
+ } else {
644
+ console.error(picocolors.red("Unexpected response from the server."));
645
+ process.exit(1);
646
+ }
647
+ }
648
+ }
649
+ }
650
+ console.error(picocolors.red("Login confirmation timed out."));
651
+ process.exit(1);
652
+ }
653
+ function saveToken(result, filePath) {
654
+ fs$1.mkdirSync(path.dirname(filePath), {
655
+ recursive: true
656
+ });
657
+ fs$1.writeFileSync(filePath, JSON.stringify(result, null, 2));
658
+ console.log(picocolors.green(`Token for ${picocolors.cyan(result.profile.username)} saved to ${picocolors.cyan(filePath)}`));
659
+ }
660
+
362
661
  async function main() {
363
662
  const {
364
663
  input,
@@ -373,6 +672,7 @@ async function main() {
373
672
 
374
673
  Commands:
375
674
  validate
675
+ login
376
676
  list-files
377
677
  versions
378
678
 
@@ -383,6 +683,12 @@ async function main() {
383
683
  --fix [fix] Attempt to fix validation errors
384
684
  --noEslint [noEslint] Disable eslint validation during validate
385
685
 
686
+
687
+ Command: login
688
+ Description: login to app.val.build and generate a Personal Access Token
689
+ Options:
690
+ --root [root], -r [root] Set project root directory (default process.cwd())
691
+
386
692
 
387
693
  Command: files
388
694
  Description: EXPERIMENTAL.
@@ -436,6 +742,10 @@ async function main() {
436
742
  });
437
743
  case "versions":
438
744
  return versions();
745
+ case "login":
746
+ return login({
747
+ root: flags.root
748
+ });
439
749
  case "validate":
440
750
  case "idate":
441
751
  if (flags.managedDir) {
@@ -455,7 +765,7 @@ void main().catch(err => {
455
765
  process.exitCode = 1;
456
766
  });
457
767
  async function versions() {
458
- const foundVersions = await getVersions();
768
+ const foundVersions = getVersions();
459
769
  console.log(`${chalk.cyan("@valbuild/core")}: ${foundVersions.coreVersion}`);
460
770
  console.log(`${chalk.cyan("@valbuild/next")}: ${foundVersions.nextVersion}`);
461
771
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@valbuild/cli",
3
3
  "private": false,
4
- "version": "0.72.4",
4
+ "version": "0.73.1",
5
5
  "description": "Val CLI tools",
6
6
  "bin": {
7
7
  "val": "./bin.js"
@@ -18,9 +18,9 @@
18
18
  "typecheck": "tsc --noEmit"
19
19
  },
20
20
  "dependencies": {
21
- "@valbuild/core": "~0.72.4",
22
- "@valbuild/server": "~0.72.4",
23
- "@valbuild/eslint-plugin": "~0.72.4",
21
+ "@valbuild/core": "~0.73.1",
22
+ "@valbuild/server": "~0.73.1",
23
+ "@valbuild/eslint-plugin": "~0.73.1",
24
24
  "eslint": "^8.31.0",
25
25
  "@inquirer/confirm": "^2.0.15",
26
26
  "@inquirer/prompts": "^3.0.2",
@@ -34,7 +34,8 @@
34
34
  "zod": "^3.22.4"
35
35
  },
36
36
  "peerDependencies": {
37
- "prettier": "*"
37
+ "prettier": "*",
38
+ "typescript": ">=5.0.0"
38
39
  },
39
40
  "preconstruct": {
40
41
  "entrypoints": [
package/src/cli.ts CHANGED
@@ -4,6 +4,7 @@ import { validate } from "./validate";
4
4
  import { files as files } from "./files";
5
5
  import { getVersions } from "./getVersions";
6
6
  import chalk from "chalk";
7
+ import { login } from "./login";
7
8
 
8
9
  async function main(): Promise<void> {
9
10
  const { input, flags, showHelp } = meow(
@@ -16,6 +17,7 @@ async function main(): Promise<void> {
16
17
 
17
18
  Commands:
18
19
  validate
20
+ login
19
21
  list-files
20
22
  versions
21
23
 
@@ -26,6 +28,12 @@ async function main(): Promise<void> {
26
28
  --fix [fix] Attempt to fix validation errors
27
29
  --noEslint [noEslint] Disable eslint validation during validate
28
30
 
31
+
32
+ Command: login
33
+ Description: login to app.val.build and generate a Personal Access Token
34
+ Options:
35
+ --root [root], -r [root] Set project root directory (default process.cwd())
36
+
29
37
 
30
38
  Command: files
31
39
  Description: EXPERIMENTAL.
@@ -86,6 +94,10 @@ async function main(): Promise<void> {
86
94
  });
87
95
  case "versions":
88
96
  return versions();
97
+ case "login":
98
+ return login({
99
+ root: flags.root,
100
+ });
89
101
  case "validate":
90
102
  case "idate":
91
103
  if (flags.managedDir) {
@@ -113,7 +125,7 @@ void main().catch((err) => {
113
125
  });
114
126
 
115
127
  async function versions() {
116
- const foundVersions = await getVersions();
128
+ const foundVersions = getVersions();
117
129
  console.log(`${chalk.cyan("@valbuild/core")}: ${foundVersions.coreVersion}`);
118
130
  console.log(`${chalk.cyan("@valbuild/next")}: ${foundVersions.nextVersion}`);
119
131
  }
@@ -1,8 +1,8 @@
1
- export const getVersions = async (): Promise<{
1
+ export const getVersions = (): {
2
2
  coreVersion?: string;
3
3
  nextVersion?: string;
4
- }> => {
5
- const coreVersion = await (() => {
4
+ } => {
5
+ const coreVersion = (() => {
6
6
  try {
7
7
  // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
8
8
  return require("@valbuild/core")?.Internal?.VERSION?.core;
@@ -10,7 +10,7 @@ export const getVersions = async (): Promise<{
10
10
  return null;
11
11
  }
12
12
  })();
13
- const nextVersion = await (() => {
13
+ const nextVersion = (() => {
14
14
  try {
15
15
  // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-require-imports
16
16
  return require("@valbuild/next")?.Internal?.VERSION?.next;