@valbuild/cli 0.85.1 → 0.86.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.
@@ -13,6 +13,7 @@ var ts = require('typescript');
13
13
  var z = require('zod');
14
14
  var node_module = require('node:module');
15
15
  var fs$1 = require('fs');
16
+ var child_process = require('child_process');
16
17
 
17
18
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
18
19
 
@@ -486,19 +487,18 @@ function findSimilar(key, targets) {
486
487
  })).sort((a, b) => a.distance - b.distance);
487
488
  }
488
489
 
489
- async function files({
490
- root,
491
- managedDir
490
+ async function listUnusedFiles({
491
+ root
492
492
  }) {
493
- const printFilesUsedByVal = !managedDir;
493
+ const managedDir = "public/val";
494
494
  const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
495
495
  const service = await server.createService(projectRoot, {});
496
496
  const valFiles = await fastGlob.glob("**/*.val.{js,ts}", {
497
497
  ignore: ["node_modules/**"],
498
498
  cwd: projectRoot
499
499
  });
500
- const absoluteFilesPathUsedByVal = [];
501
- async function printOrGetFileRefs(file) {
500
+ const filesUsedByVal = [];
501
+ async function pushFilesUsedByVal(file) {
502
502
  const moduleId = `/${file}`; // TODO: check if this always works? (Windows?)
503
503
  const valModule = await service.get(moduleId, "", {
504
504
  validate: true,
@@ -515,11 +515,7 @@ async function files({
515
515
  const value = error.value;
516
516
  if (isFileRef(value)) {
517
517
  const absoluteFilePathUsedByVal = path__default["default"].join(projectRoot, ...value[core.FILE_REF_PROP].split("/"));
518
- if (printFilesUsedByVal) {
519
- console.log(absoluteFilePathUsedByVal);
520
- } else {
521
- absoluteFilesPathUsedByVal.push(absoluteFilePathUsedByVal);
522
- }
518
+ filesUsedByVal.push(absoluteFilePathUsedByVal);
523
519
  }
524
520
  }
525
521
  }
@@ -527,19 +523,17 @@ async function files({
527
523
  }
528
524
  }
529
525
  for (const file of valFiles) {
530
- await printOrGetFileRefs(file);
526
+ await pushFilesUsedByVal(file);
531
527
  }
532
- if (managedDir) {
533
- const managedRoot = path__default["default"].isAbsolute(managedDir) ? managedDir : path__default["default"].join(projectRoot, managedDir);
534
- const allFilesInManagedDir = await fastGlob.glob("**/*", {
535
- ignore: ["node_modules/**"],
536
- cwd: managedRoot
537
- });
538
- for (const file of allFilesInManagedDir) {
539
- const absoluteFilePath = path__default["default"].join(managedRoot, file);
540
- if (!absoluteFilesPathUsedByVal.includes(absoluteFilePath)) {
541
- console.log(path__default["default"].join(managedRoot, file));
542
- }
528
+ const managedRoot = path__default["default"].join(projectRoot, managedDir);
529
+ const allFilesInManagedDir = await fastGlob.glob("**/*", {
530
+ ignore: ["node_modules/**"],
531
+ cwd: managedRoot
532
+ });
533
+ for (const file of allFilesInManagedDir) {
534
+ const absoluteFilePath = path__default["default"].join(managedRoot, file);
535
+ if (!filesUsedByVal.includes(absoluteFilePath)) {
536
+ console.log(path__default["default"].join(managedRoot, file));
543
537
  }
544
538
  }
545
539
  service.dispose();
@@ -581,7 +575,106 @@ const getVersions = () => {
581
575
  };
582
576
  };
583
577
 
584
- const host = process.env.VAL_BUILD_URL || "https://app.val.build";
578
+ const host$1 = process.env.VAL_BUILD_URL || "https://admin.val.build";
579
+ async function connect(options) {
580
+ const {
581
+ root
582
+ } = options;
583
+ const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
584
+ const maybeProject = await tryGetProject(projectRoot);
585
+ const maybeGitRemote = await tryGetGitRemote(projectRoot);
586
+ const maybeGitOwnerAndRepo = maybeGitRemote !== null ? getGitHubOwnerAndRepo(maybeGitRemote) : null;
587
+ const params = new URLSearchParams();
588
+ if (maybeProject) {
589
+ params.set("org", maybeProject.orgName);
590
+ params.set("project", maybeProject.projectName);
591
+ }
592
+ if (maybeGitOwnerAndRepo) {
593
+ params.set("github_repo", [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"));
594
+ }
595
+ const url = `${host$1}/connect?${params.toString()}`;
596
+
597
+ // Open url in default browser and show fallback instructions:
598
+ console.log(picocolors__default["default"].cyan("\nStarting connect process in browser...\n"));
599
+ console.log(picocolors__default["default"].dim(`\nIf the browser does not open, please visit:\n${url}\n`));
600
+ if (process.platform === "win32") {
601
+ // Windows
602
+ child_process.exec(`start ${url}`);
603
+ } else if (process.platform === "darwin") {
604
+ // macOS
605
+ child_process.exec(`open ${url}`);
606
+ } else {
607
+ // Linux and others
608
+ child_process.exec(`xdg-open ${url}`);
609
+ }
610
+ }
611
+ async function tryGetProject(projectRoot) {
612
+ const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
613
+ if (valConfigFile && valConfigFile.project) {
614
+ const parts = valConfigFile.project.split("/");
615
+ if (parts.length === 2) {
616
+ return {
617
+ orgName: parts[0],
618
+ projectName: parts[1]
619
+ };
620
+ } else {
621
+ console.error(picocolors__default["default"].red(`Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`));
622
+ process.exit(1);
623
+ }
624
+ }
625
+ return null;
626
+ }
627
+ function getGitHubOwnerAndRepo(gitRemote) {
628
+ const sshMatch = gitRemote.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
629
+ if (sshMatch) {
630
+ return {
631
+ owner: sshMatch[1],
632
+ repo: sshMatch[2]
633
+ };
634
+ }
635
+ const httpsMatch = gitRemote.match(/https:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/);
636
+ if (httpsMatch) {
637
+ return {
638
+ owner: httpsMatch[1],
639
+ repo: httpsMatch[2]
640
+ };
641
+ }
642
+ return null;
643
+ }
644
+ async function tryGetGitRemote(root) {
645
+ try {
646
+ const gitConfig = tryGetGitConfig(root);
647
+ if (!gitConfig) {
648
+ return null;
649
+ }
650
+ const remoteMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = (.+)/);
651
+ if (remoteMatch && remoteMatch[1]) {
652
+ return remoteMatch[1].trim();
653
+ }
654
+ return null;
655
+ } catch (error) {
656
+ console.error(picocolors__default["default"].red("Failed to read .git/config file."), error);
657
+ return null;
658
+ }
659
+ }
660
+ function tryGetGitConfig(root) {
661
+ let currentDir = root;
662
+ let lastDir = null;
663
+ while (currentDir !== lastDir) {
664
+ const gitConfigPath = path__default["default"].join(currentDir, ".git", "config");
665
+ if (fs__default$1["default"].existsSync(gitConfigPath)) {
666
+ return fs__default$1["default"].readFileSync(gitConfigPath, "utf-8");
667
+ }
668
+ lastDir = currentDir;
669
+ currentDir = path__default["default"].dirname(currentDir);
670
+ if (lastDir === currentDir) {
671
+ break;
672
+ }
673
+ }
674
+ return null;
675
+ }
676
+
677
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
585
678
  async function login(options) {
586
679
  try {
587
680
  var _response$headers$get;
@@ -598,7 +691,7 @@ async function login(options) {
598
691
  let url;
599
692
  if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
600
693
  const text = await response.text();
601
- console.error(picocolors__default["default"].red("Unexpected failure while trying to login (content type was not JSON). Server response:"), text || "<empty>");
694
+ console.error(picocolors__default["default"].red("Unexpected failure while trying to login (content type was not JSON). "), text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
602
695
  process.exit(1);
603
696
  }
604
697
  const json = await response.json();
@@ -630,7 +723,9 @@ async function pollForConfirmation(token) {
630
723
  const start = Date.now();
631
724
  while (Date.now() - start < MAX_DURATION) {
632
725
  await new Promise(resolve => setTimeout(resolve, 1000));
633
- const response = await fetch(`${host}/api/login?token=${token}&consume=true`);
726
+ const response = await fetch(`${host}/api/login?token=${token}&consume=true`, {
727
+ method: "POST"
728
+ });
634
729
  if (response.status === 500) {
635
730
  console.error(picocolors__default["default"].red("An error occurred on the server."));
636
731
  process.exit(1);
@@ -638,7 +733,7 @@ async function pollForConfirmation(token) {
638
733
  if (response.status === 200) {
639
734
  const json = await response.json();
640
735
  if (json) {
641
- if (typeof json.profile.username === "string" && typeof json.pat === "string") {
736
+ if (typeof json.profile.email === "string" && typeof json.pat === "string") {
642
737
  return json;
643
738
  } else {
644
739
  console.error(picocolors__default["default"].red("Unexpected response from the server."));
@@ -655,7 +750,7 @@ function saveToken(result, filePath) {
655
750
  recursive: true
656
751
  });
657
752
  fs__default$1["default"].writeFileSync(filePath, JSON.stringify(result, null, 2));
658
- console.log(picocolors__default["default"].green(`Token for ${picocolors__default["default"].cyan(result.profile.username)} saved to ${picocolors__default["default"].cyan(filePath)}`));
753
+ console.log(picocolors__default["default"].green(`Token for ${picocolors__default["default"].cyan(result.profile.email)} saved to ${picocolors__default["default"].cyan(filePath)}`));
659
754
  }
660
755
 
661
756
  async function main() {
@@ -673,7 +768,8 @@ async function main() {
673
768
  Commands:
674
769
  validate
675
770
  login
676
- list-files
771
+ files
772
+ connect
677
773
  versions
678
774
 
679
775
  Command: validate
@@ -684,21 +780,21 @@ async function main() {
684
780
 
685
781
 
686
782
  Command: login
687
- Description: login to app.val.build and generate a Personal Access Token
783
+ Description: login to admin.val.build and generate a Personal Access Token
688
784
  Options:
689
785
  --root [root], -r [root] Set project root directory (default process.cwd())
690
786
 
691
787
 
692
- Command: files
693
- Description: EXPERIMENTAL.
694
- Perform file operations in Val.
695
- By default it lists files (images, ...) currently in use by Val.
788
+ Command: connect
789
+ Description: connect your local project to a Val Build project at admin.val.build
790
+ Options:
791
+ --root [root], -r [root] Set project root directory (default process.cwd())
696
792
 
697
- If a managed directory is specified,
698
- it will list all files in the managed directory that ARE NOT currently used by Val.
793
+ Command: list-unused-files
794
+ Description: EXPERIMENTAL.
795
+ List files that are in public/val but not in use by any Val module.
699
796
  This is useful for cleaning up unused files.
700
797
  Options:
701
- --managedDir [dir] If set, list files found in directory that are not managed by Val
702
798
  --root [root], -r [root] Set project root directory (default process.cwd())
703
799
  `, {
704
800
  flags: {
@@ -731,13 +827,12 @@ async function main() {
731
827
  }
732
828
  const [command] = input;
733
829
  switch (command) {
734
- case "files":
830
+ case "list-unused-files":
735
831
  if (flags.fix || flags.noEslint) {
736
- return error(`Command "files" does not support --fix or --noEslint flags`);
832
+ return error(`Command "list-unused-files" does not support --fix or --noEslint flags`);
737
833
  }
738
- return files({
739
- root: flags.root,
740
- managedDir: flags.managedDir
834
+ return listUnusedFiles({
835
+ root: flags.root
741
836
  });
742
837
  case "versions":
743
838
  return versions();
@@ -745,6 +840,13 @@ async function main() {
745
840
  return login({
746
841
  root: flags.root
747
842
  });
843
+ case "connect":
844
+ if (flags.fix || flags.noEslint) {
845
+ return error(`Command "connect" does not support --fix or --noEslint flags`);
846
+ }
847
+ return connect({
848
+ root: flags.root
849
+ });
748
850
  case "validate":
749
851
  case "idate":
750
852
  if (flags.managedDir) {
@@ -13,6 +13,7 @@ var ts = require('typescript');
13
13
  var z = require('zod');
14
14
  var node_module = require('node:module');
15
15
  var fs$1 = require('fs');
16
+ var child_process = require('child_process');
16
17
 
17
18
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
18
19
 
@@ -486,19 +487,18 @@ function findSimilar(key, targets) {
486
487
  })).sort((a, b) => a.distance - b.distance);
487
488
  }
488
489
 
489
- async function files({
490
- root,
491
- managedDir
490
+ async function listUnusedFiles({
491
+ root
492
492
  }) {
493
- const printFilesUsedByVal = !managedDir;
493
+ const managedDir = "public/val";
494
494
  const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
495
495
  const service = await server.createService(projectRoot, {});
496
496
  const valFiles = await fastGlob.glob("**/*.val.{js,ts}", {
497
497
  ignore: ["node_modules/**"],
498
498
  cwd: projectRoot
499
499
  });
500
- const absoluteFilesPathUsedByVal = [];
501
- async function printOrGetFileRefs(file) {
500
+ const filesUsedByVal = [];
501
+ async function pushFilesUsedByVal(file) {
502
502
  const moduleId = `/${file}`; // TODO: check if this always works? (Windows?)
503
503
  const valModule = await service.get(moduleId, "", {
504
504
  validate: true,
@@ -515,11 +515,7 @@ async function files({
515
515
  const value = error.value;
516
516
  if (isFileRef(value)) {
517
517
  const absoluteFilePathUsedByVal = path__default["default"].join(projectRoot, ...value[core.FILE_REF_PROP].split("/"));
518
- if (printFilesUsedByVal) {
519
- console.log(absoluteFilePathUsedByVal);
520
- } else {
521
- absoluteFilesPathUsedByVal.push(absoluteFilePathUsedByVal);
522
- }
518
+ filesUsedByVal.push(absoluteFilePathUsedByVal);
523
519
  }
524
520
  }
525
521
  }
@@ -527,19 +523,17 @@ async function files({
527
523
  }
528
524
  }
529
525
  for (const file of valFiles) {
530
- await printOrGetFileRefs(file);
526
+ await pushFilesUsedByVal(file);
531
527
  }
532
- if (managedDir) {
533
- const managedRoot = path__default["default"].isAbsolute(managedDir) ? managedDir : path__default["default"].join(projectRoot, managedDir);
534
- const allFilesInManagedDir = await fastGlob.glob("**/*", {
535
- ignore: ["node_modules/**"],
536
- cwd: managedRoot
537
- });
538
- for (const file of allFilesInManagedDir) {
539
- const absoluteFilePath = path__default["default"].join(managedRoot, file);
540
- if (!absoluteFilesPathUsedByVal.includes(absoluteFilePath)) {
541
- console.log(path__default["default"].join(managedRoot, file));
542
- }
528
+ const managedRoot = path__default["default"].join(projectRoot, managedDir);
529
+ const allFilesInManagedDir = await fastGlob.glob("**/*", {
530
+ ignore: ["node_modules/**"],
531
+ cwd: managedRoot
532
+ });
533
+ for (const file of allFilesInManagedDir) {
534
+ const absoluteFilePath = path__default["default"].join(managedRoot, file);
535
+ if (!filesUsedByVal.includes(absoluteFilePath)) {
536
+ console.log(path__default["default"].join(managedRoot, file));
543
537
  }
544
538
  }
545
539
  service.dispose();
@@ -581,7 +575,106 @@ const getVersions = () => {
581
575
  };
582
576
  };
583
577
 
584
- const host = process.env.VAL_BUILD_URL || "https://app.val.build";
578
+ const host$1 = process.env.VAL_BUILD_URL || "https://admin.val.build";
579
+ async function connect(options) {
580
+ const {
581
+ root
582
+ } = options;
583
+ const projectRoot = root ? path__default["default"].resolve(root) : process.cwd();
584
+ const maybeProject = await tryGetProject(projectRoot);
585
+ const maybeGitRemote = await tryGetGitRemote(projectRoot);
586
+ const maybeGitOwnerAndRepo = maybeGitRemote !== null ? getGitHubOwnerAndRepo(maybeGitRemote) : null;
587
+ const params = new URLSearchParams();
588
+ if (maybeProject) {
589
+ params.set("org", maybeProject.orgName);
590
+ params.set("project", maybeProject.projectName);
591
+ }
592
+ if (maybeGitOwnerAndRepo) {
593
+ params.set("github_repo", [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"));
594
+ }
595
+ const url = `${host$1}/connect?${params.toString()}`;
596
+
597
+ // Open url in default browser and show fallback instructions:
598
+ console.log(picocolors__default["default"].cyan("\nStarting connect process in browser...\n"));
599
+ console.log(picocolors__default["default"].dim(`\nIf the browser does not open, please visit:\n${url}\n`));
600
+ if (process.platform === "win32") {
601
+ // Windows
602
+ child_process.exec(`start ${url}`);
603
+ } else if (process.platform === "darwin") {
604
+ // macOS
605
+ child_process.exec(`open ${url}`);
606
+ } else {
607
+ // Linux and others
608
+ child_process.exec(`xdg-open ${url}`);
609
+ }
610
+ }
611
+ async function tryGetProject(projectRoot) {
612
+ const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
613
+ if (valConfigFile && valConfigFile.project) {
614
+ const parts = valConfigFile.project.split("/");
615
+ if (parts.length === 2) {
616
+ return {
617
+ orgName: parts[0],
618
+ projectName: parts[1]
619
+ };
620
+ } else {
621
+ console.error(picocolors__default["default"].red(`Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`));
622
+ process.exit(1);
623
+ }
624
+ }
625
+ return null;
626
+ }
627
+ function getGitHubOwnerAndRepo(gitRemote) {
628
+ const sshMatch = gitRemote.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
629
+ if (sshMatch) {
630
+ return {
631
+ owner: sshMatch[1],
632
+ repo: sshMatch[2]
633
+ };
634
+ }
635
+ const httpsMatch = gitRemote.match(/https:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/);
636
+ if (httpsMatch) {
637
+ return {
638
+ owner: httpsMatch[1],
639
+ repo: httpsMatch[2]
640
+ };
641
+ }
642
+ return null;
643
+ }
644
+ async function tryGetGitRemote(root) {
645
+ try {
646
+ const gitConfig = tryGetGitConfig(root);
647
+ if (!gitConfig) {
648
+ return null;
649
+ }
650
+ const remoteMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = (.+)/);
651
+ if (remoteMatch && remoteMatch[1]) {
652
+ return remoteMatch[1].trim();
653
+ }
654
+ return null;
655
+ } catch (error) {
656
+ console.error(picocolors__default["default"].red("Failed to read .git/config file."), error);
657
+ return null;
658
+ }
659
+ }
660
+ function tryGetGitConfig(root) {
661
+ let currentDir = root;
662
+ let lastDir = null;
663
+ while (currentDir !== lastDir) {
664
+ const gitConfigPath = path__default["default"].join(currentDir, ".git", "config");
665
+ if (fs__default$1["default"].existsSync(gitConfigPath)) {
666
+ return fs__default$1["default"].readFileSync(gitConfigPath, "utf-8");
667
+ }
668
+ lastDir = currentDir;
669
+ currentDir = path__default["default"].dirname(currentDir);
670
+ if (lastDir === currentDir) {
671
+ break;
672
+ }
673
+ }
674
+ return null;
675
+ }
676
+
677
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
585
678
  async function login(options) {
586
679
  try {
587
680
  var _response$headers$get;
@@ -598,7 +691,7 @@ async function login(options) {
598
691
  let url;
599
692
  if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
600
693
  const text = await response.text();
601
- console.error(picocolors__default["default"].red("Unexpected failure while trying to login (content type was not JSON). Server response:"), text || "<empty>");
694
+ console.error(picocolors__default["default"].red("Unexpected failure while trying to login (content type was not JSON). "), text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
602
695
  process.exit(1);
603
696
  }
604
697
  const json = await response.json();
@@ -630,7 +723,9 @@ async function pollForConfirmation(token) {
630
723
  const start = Date.now();
631
724
  while (Date.now() - start < MAX_DURATION) {
632
725
  await new Promise(resolve => setTimeout(resolve, 1000));
633
- const response = await fetch(`${host}/api/login?token=${token}&consume=true`);
726
+ const response = await fetch(`${host}/api/login?token=${token}&consume=true`, {
727
+ method: "POST"
728
+ });
634
729
  if (response.status === 500) {
635
730
  console.error(picocolors__default["default"].red("An error occurred on the server."));
636
731
  process.exit(1);
@@ -638,7 +733,7 @@ async function pollForConfirmation(token) {
638
733
  if (response.status === 200) {
639
734
  const json = await response.json();
640
735
  if (json) {
641
- if (typeof json.profile.username === "string" && typeof json.pat === "string") {
736
+ if (typeof json.profile.email === "string" && typeof json.pat === "string") {
642
737
  return json;
643
738
  } else {
644
739
  console.error(picocolors__default["default"].red("Unexpected response from the server."));
@@ -655,7 +750,7 @@ function saveToken(result, filePath) {
655
750
  recursive: true
656
751
  });
657
752
  fs__default$1["default"].writeFileSync(filePath, JSON.stringify(result, null, 2));
658
- console.log(picocolors__default["default"].green(`Token for ${picocolors__default["default"].cyan(result.profile.username)} saved to ${picocolors__default["default"].cyan(filePath)}`));
753
+ console.log(picocolors__default["default"].green(`Token for ${picocolors__default["default"].cyan(result.profile.email)} saved to ${picocolors__default["default"].cyan(filePath)}`));
659
754
  }
660
755
 
661
756
  async function main() {
@@ -673,7 +768,8 @@ async function main() {
673
768
  Commands:
674
769
  validate
675
770
  login
676
- list-files
771
+ files
772
+ connect
677
773
  versions
678
774
 
679
775
  Command: validate
@@ -684,21 +780,21 @@ async function main() {
684
780
 
685
781
 
686
782
  Command: login
687
- Description: login to app.val.build and generate a Personal Access Token
783
+ Description: login to admin.val.build and generate a Personal Access Token
688
784
  Options:
689
785
  --root [root], -r [root] Set project root directory (default process.cwd())
690
786
 
691
787
 
692
- Command: files
693
- Description: EXPERIMENTAL.
694
- Perform file operations in Val.
695
- By default it lists files (images, ...) currently in use by Val.
788
+ Command: connect
789
+ Description: connect your local project to a Val Build project at admin.val.build
790
+ Options:
791
+ --root [root], -r [root] Set project root directory (default process.cwd())
696
792
 
697
- If a managed directory is specified,
698
- it will list all files in the managed directory that ARE NOT currently used by Val.
793
+ Command: list-unused-files
794
+ Description: EXPERIMENTAL.
795
+ List files that are in public/val but not in use by any Val module.
699
796
  This is useful for cleaning up unused files.
700
797
  Options:
701
- --managedDir [dir] If set, list files found in directory that are not managed by Val
702
798
  --root [root], -r [root] Set project root directory (default process.cwd())
703
799
  `, {
704
800
  flags: {
@@ -731,13 +827,12 @@ async function main() {
731
827
  }
732
828
  const [command] = input;
733
829
  switch (command) {
734
- case "files":
830
+ case "list-unused-files":
735
831
  if (flags.fix || flags.noEslint) {
736
- return error(`Command "files" does not support --fix or --noEslint flags`);
832
+ return error(`Command "list-unused-files" does not support --fix or --noEslint flags`);
737
833
  }
738
- return files({
739
- root: flags.root,
740
- managedDir: flags.managedDir
834
+ return listUnusedFiles({
835
+ root: flags.root
741
836
  });
742
837
  case "versions":
743
838
  return versions();
@@ -745,6 +840,13 @@ async function main() {
745
840
  return login({
746
841
  root: flags.root
747
842
  });
843
+ case "connect":
844
+ if (flags.fix || flags.noEslint) {
845
+ return error(`Command "connect" does not support --fix or --noEslint flags`);
846
+ }
847
+ return connect({
848
+ root: flags.root
849
+ });
748
850
  case "validate":
749
851
  case "idate":
750
852
  if (flags.managedDir) {
@@ -11,6 +11,7 @@ import ts from 'typescript';
11
11
  import z from 'zod';
12
12
  import { createRequire } from 'node:module';
13
13
  import fs$1 from 'fs';
14
+ import { exec } from 'child_process';
14
15
 
15
16
  function error(message) {
16
17
  console.error(chalk.red("❌Error: ") + message);
@@ -454,19 +455,18 @@ function findSimilar(key, targets) {
454
455
  })).sort((a, b) => a.distance - b.distance);
455
456
  }
456
457
 
457
- async function files({
458
- root,
459
- managedDir
458
+ async function listUnusedFiles({
459
+ root
460
460
  }) {
461
- const printFilesUsedByVal = !managedDir;
461
+ const managedDir = "public/val";
462
462
  const projectRoot = root ? path.resolve(root) : process.cwd();
463
463
  const service = await createService(projectRoot, {});
464
464
  const valFiles = await glob("**/*.val.{js,ts}", {
465
465
  ignore: ["node_modules/**"],
466
466
  cwd: projectRoot
467
467
  });
468
- const absoluteFilesPathUsedByVal = [];
469
- async function printOrGetFileRefs(file) {
468
+ const filesUsedByVal = [];
469
+ async function pushFilesUsedByVal(file) {
470
470
  const moduleId = `/${file}`; // TODO: check if this always works? (Windows?)
471
471
  const valModule = await service.get(moduleId, "", {
472
472
  validate: true,
@@ -483,11 +483,7 @@ async function files({
483
483
  const value = error.value;
484
484
  if (isFileRef(value)) {
485
485
  const absoluteFilePathUsedByVal = path.join(projectRoot, ...value[FILE_REF_PROP].split("/"));
486
- if (printFilesUsedByVal) {
487
- console.log(absoluteFilePathUsedByVal);
488
- } else {
489
- absoluteFilesPathUsedByVal.push(absoluteFilePathUsedByVal);
490
- }
486
+ filesUsedByVal.push(absoluteFilePathUsedByVal);
491
487
  }
492
488
  }
493
489
  }
@@ -495,19 +491,17 @@ async function files({
495
491
  }
496
492
  }
497
493
  for (const file of valFiles) {
498
- await printOrGetFileRefs(file);
494
+ await pushFilesUsedByVal(file);
499
495
  }
500
- if (managedDir) {
501
- const managedRoot = path.isAbsolute(managedDir) ? managedDir : path.join(projectRoot, managedDir);
502
- const allFilesInManagedDir = await glob("**/*", {
503
- ignore: ["node_modules/**"],
504
- cwd: managedRoot
505
- });
506
- for (const file of allFilesInManagedDir) {
507
- const absoluteFilePath = path.join(managedRoot, file);
508
- if (!absoluteFilesPathUsedByVal.includes(absoluteFilePath)) {
509
- console.log(path.join(managedRoot, file));
510
- }
496
+ const managedRoot = path.join(projectRoot, managedDir);
497
+ const allFilesInManagedDir = await glob("**/*", {
498
+ ignore: ["node_modules/**"],
499
+ cwd: managedRoot
500
+ });
501
+ for (const file of allFilesInManagedDir) {
502
+ const absoluteFilePath = path.join(managedRoot, file);
503
+ if (!filesUsedByVal.includes(absoluteFilePath)) {
504
+ console.log(path.join(managedRoot, file));
511
505
  }
512
506
  }
513
507
  service.dispose();
@@ -549,7 +543,106 @@ const getVersions = () => {
549
543
  };
550
544
  };
551
545
 
552
- const host = process.env.VAL_BUILD_URL || "https://app.val.build";
546
+ const host$1 = process.env.VAL_BUILD_URL || "https://admin.val.build";
547
+ async function connect(options) {
548
+ const {
549
+ root
550
+ } = options;
551
+ const projectRoot = root ? path.resolve(root) : process.cwd();
552
+ const maybeProject = await tryGetProject(projectRoot);
553
+ const maybeGitRemote = await tryGetGitRemote(projectRoot);
554
+ const maybeGitOwnerAndRepo = maybeGitRemote !== null ? getGitHubOwnerAndRepo(maybeGitRemote) : null;
555
+ const params = new URLSearchParams();
556
+ if (maybeProject) {
557
+ params.set("org", maybeProject.orgName);
558
+ params.set("project", maybeProject.projectName);
559
+ }
560
+ if (maybeGitOwnerAndRepo) {
561
+ params.set("github_repo", [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"));
562
+ }
563
+ const url = `${host$1}/connect?${params.toString()}`;
564
+
565
+ // Open url in default browser and show fallback instructions:
566
+ console.log(picocolors.cyan("\nStarting connect process in browser...\n"));
567
+ console.log(picocolors.dim(`\nIf the browser does not open, please visit:\n${url}\n`));
568
+ if (process.platform === "win32") {
569
+ // Windows
570
+ exec(`start ${url}`);
571
+ } else if (process.platform === "darwin") {
572
+ // macOS
573
+ exec(`open ${url}`);
574
+ } else {
575
+ // Linux and others
576
+ exec(`xdg-open ${url}`);
577
+ }
578
+ }
579
+ async function tryGetProject(projectRoot) {
580
+ const valConfigFile = (await evalValConfigFile(projectRoot, "val.config.ts")) || (await evalValConfigFile(projectRoot, "val.config.js"));
581
+ if (valConfigFile && valConfigFile.project) {
582
+ const parts = valConfigFile.project.split("/");
583
+ if (parts.length === 2) {
584
+ return {
585
+ orgName: parts[0],
586
+ projectName: parts[1]
587
+ };
588
+ } else {
589
+ console.error(picocolors.red(`Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`));
590
+ process.exit(1);
591
+ }
592
+ }
593
+ return null;
594
+ }
595
+ function getGitHubOwnerAndRepo(gitRemote) {
596
+ const sshMatch = gitRemote.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
597
+ if (sshMatch) {
598
+ return {
599
+ owner: sshMatch[1],
600
+ repo: sshMatch[2]
601
+ };
602
+ }
603
+ const httpsMatch = gitRemote.match(/https:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/);
604
+ if (httpsMatch) {
605
+ return {
606
+ owner: httpsMatch[1],
607
+ repo: httpsMatch[2]
608
+ };
609
+ }
610
+ return null;
611
+ }
612
+ async function tryGetGitRemote(root) {
613
+ try {
614
+ const gitConfig = tryGetGitConfig(root);
615
+ if (!gitConfig) {
616
+ return null;
617
+ }
618
+ const remoteMatch = gitConfig.match(/\[remote "origin"\][\s\S]*?url = (.+)/);
619
+ if (remoteMatch && remoteMatch[1]) {
620
+ return remoteMatch[1].trim();
621
+ }
622
+ return null;
623
+ } catch (error) {
624
+ console.error(picocolors.red("Failed to read .git/config file."), error);
625
+ return null;
626
+ }
627
+ }
628
+ function tryGetGitConfig(root) {
629
+ let currentDir = root;
630
+ let lastDir = null;
631
+ while (currentDir !== lastDir) {
632
+ const gitConfigPath = path.join(currentDir, ".git", "config");
633
+ if (fs$1.existsSync(gitConfigPath)) {
634
+ return fs$1.readFileSync(gitConfigPath, "utf-8");
635
+ }
636
+ lastDir = currentDir;
637
+ currentDir = path.dirname(currentDir);
638
+ if (lastDir === currentDir) {
639
+ break;
640
+ }
641
+ }
642
+ return null;
643
+ }
644
+
645
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
553
646
  async function login(options) {
554
647
  try {
555
648
  var _response$headers$get;
@@ -566,7 +659,7 @@ async function login(options) {
566
659
  let url;
567
660
  if (!((_response$headers$get = response.headers.get("content-type")) !== null && _response$headers$get !== void 0 && _response$headers$get.includes("application/json"))) {
568
661
  const text = await response.text();
569
- console.error(picocolors.red("Unexpected failure while trying to login (content type was not JSON). Server response:"), text || "<empty>");
662
+ console.error(picocolors.red("Unexpected failure while trying to login (content type was not JSON). "), text ? `Server response: ${text} (status: ${response.status})` : `Status: ${response.status}`);
570
663
  process.exit(1);
571
664
  }
572
665
  const json = await response.json();
@@ -598,7 +691,9 @@ async function pollForConfirmation(token) {
598
691
  const start = Date.now();
599
692
  while (Date.now() - start < MAX_DURATION) {
600
693
  await new Promise(resolve => setTimeout(resolve, 1000));
601
- const response = await fetch(`${host}/api/login?token=${token}&consume=true`);
694
+ const response = await fetch(`${host}/api/login?token=${token}&consume=true`, {
695
+ method: "POST"
696
+ });
602
697
  if (response.status === 500) {
603
698
  console.error(picocolors.red("An error occurred on the server."));
604
699
  process.exit(1);
@@ -606,7 +701,7 @@ async function pollForConfirmation(token) {
606
701
  if (response.status === 200) {
607
702
  const json = await response.json();
608
703
  if (json) {
609
- if (typeof json.profile.username === "string" && typeof json.pat === "string") {
704
+ if (typeof json.profile.email === "string" && typeof json.pat === "string") {
610
705
  return json;
611
706
  } else {
612
707
  console.error(picocolors.red("Unexpected response from the server."));
@@ -623,7 +718,7 @@ function saveToken(result, filePath) {
623
718
  recursive: true
624
719
  });
625
720
  fs$1.writeFileSync(filePath, JSON.stringify(result, null, 2));
626
- console.log(picocolors.green(`Token for ${picocolors.cyan(result.profile.username)} saved to ${picocolors.cyan(filePath)}`));
721
+ console.log(picocolors.green(`Token for ${picocolors.cyan(result.profile.email)} saved to ${picocolors.cyan(filePath)}`));
627
722
  }
628
723
 
629
724
  async function main() {
@@ -641,7 +736,8 @@ async function main() {
641
736
  Commands:
642
737
  validate
643
738
  login
644
- list-files
739
+ files
740
+ connect
645
741
  versions
646
742
 
647
743
  Command: validate
@@ -652,21 +748,21 @@ async function main() {
652
748
 
653
749
 
654
750
  Command: login
655
- Description: login to app.val.build and generate a Personal Access Token
751
+ Description: login to admin.val.build and generate a Personal Access Token
656
752
  Options:
657
753
  --root [root], -r [root] Set project root directory (default process.cwd())
658
754
 
659
755
 
660
- Command: files
661
- Description: EXPERIMENTAL.
662
- Perform file operations in Val.
663
- By default it lists files (images, ...) currently in use by Val.
756
+ Command: connect
757
+ Description: connect your local project to a Val Build project at admin.val.build
758
+ Options:
759
+ --root [root], -r [root] Set project root directory (default process.cwd())
664
760
 
665
- If a managed directory is specified,
666
- it will list all files in the managed directory that ARE NOT currently used by Val.
761
+ Command: list-unused-files
762
+ Description: EXPERIMENTAL.
763
+ List files that are in public/val but not in use by any Val module.
667
764
  This is useful for cleaning up unused files.
668
765
  Options:
669
- --managedDir [dir] If set, list files found in directory that are not managed by Val
670
766
  --root [root], -r [root] Set project root directory (default process.cwd())
671
767
  `, {
672
768
  flags: {
@@ -699,13 +795,12 @@ async function main() {
699
795
  }
700
796
  const [command] = input;
701
797
  switch (command) {
702
- case "files":
798
+ case "list-unused-files":
703
799
  if (flags.fix || flags.noEslint) {
704
- return error(`Command "files" does not support --fix or --noEslint flags`);
800
+ return error(`Command "list-unused-files" does not support --fix or --noEslint flags`);
705
801
  }
706
- return files({
707
- root: flags.root,
708
- managedDir: flags.managedDir
802
+ return listUnusedFiles({
803
+ root: flags.root
709
804
  });
710
805
  case "versions":
711
806
  return versions();
@@ -713,6 +808,13 @@ async function main() {
713
808
  return login({
714
809
  root: flags.root
715
810
  });
811
+ case "connect":
812
+ if (flags.fix || flags.noEslint) {
813
+ return error(`Command "connect" does not support --fix or --noEslint flags`);
814
+ }
815
+ return connect({
816
+ root: flags.root
817
+ });
716
818
  case "validate":
717
819
  case "idate":
718
820
  if (flags.managedDir) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@valbuild/cli",
3
3
  "private": false,
4
- "version": "0.85.1",
4
+ "version": "0.86.0",
5
5
  "description": "Val CLI tools",
6
6
  "bin": {
7
7
  "val": "./bin.js"
@@ -20,9 +20,9 @@
20
20
  "dependencies": {
21
21
  "@inquirer/confirm": "^2.0.15",
22
22
  "@inquirer/prompts": "^3.0.2",
23
- "@valbuild/core": "~0.85.1",
24
- "@valbuild/eslint-plugin": "~0.85.1",
25
- "@valbuild/server": "~0.85.1",
23
+ "@valbuild/core": "~0.86.0",
24
+ "@valbuild/eslint-plugin": "~0.86.0",
25
+ "@valbuild/server": "~0.86.0",
26
26
  "chalk": "^4.1.2",
27
27
  "cors": "^2.8.5",
28
28
  "express": "^4.18.2",
package/src/cli.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import meow from "meow";
2
2
  import { error } from "./logger";
3
3
  import { validate } from "./validate";
4
- import { files as files } from "./files";
4
+ import { listUnusedFiles as listUnusedFiles } from "./listUnusedFiles";
5
5
  import { getVersions } from "./getVersions";
6
+ import { connect } from "./connect";
6
7
  import chalk from "chalk";
7
8
  import { login } from "./login";
8
9
 
@@ -18,7 +19,8 @@ async function main(): Promise<void> {
18
19
  Commands:
19
20
  validate
20
21
  login
21
- list-files
22
+ files
23
+ connect
22
24
  versions
23
25
 
24
26
  Command: validate
@@ -29,21 +31,21 @@ async function main(): Promise<void> {
29
31
 
30
32
 
31
33
  Command: login
32
- Description: login to app.val.build and generate a Personal Access Token
34
+ Description: login to admin.val.build and generate a Personal Access Token
33
35
  Options:
34
36
  --root [root], -r [root] Set project root directory (default process.cwd())
35
37
 
36
38
 
37
- Command: files
38
- Description: EXPERIMENTAL.
39
- Perform file operations in Val.
40
- By default it lists files (images, ...) currently in use by Val.
39
+ Command: connect
40
+ Description: connect your local project to a Val Build project at admin.val.build
41
+ Options:
42
+ --root [root], -r [root] Set project root directory (default process.cwd())
41
43
 
42
- If a managed directory is specified,
43
- it will list all files in the managed directory that ARE NOT currently used by Val.
44
+ Command: list-unused-files
45
+ Description: EXPERIMENTAL.
46
+ List files that are in public/val but not in use by any Val module.
44
47
  This is useful for cleaning up unused files.
45
48
  Options:
46
- --managedDir [dir] If set, list files found in directory that are not managed by Val
47
49
  --root [root], -r [root] Set project root directory (default process.cwd())
48
50
  `,
49
51
  {
@@ -81,15 +83,14 @@ async function main(): Promise<void> {
81
83
 
82
84
  const [command] = input;
83
85
  switch (command) {
84
- case "files":
86
+ case "list-unused-files":
85
87
  if (flags.fix || flags.noEslint) {
86
88
  return error(
87
- `Command "files" does not support --fix or --noEslint flags`,
89
+ `Command "list-unused-files" does not support --fix or --noEslint flags`,
88
90
  );
89
91
  }
90
- return files({
92
+ return listUnusedFiles({
91
93
  root: flags.root,
92
- managedDir: flags.managedDir,
93
94
  });
94
95
  case "versions":
95
96
  return versions();
@@ -97,6 +98,15 @@ async function main(): Promise<void> {
97
98
  return login({
98
99
  root: flags.root,
99
100
  });
101
+ case "connect":
102
+ if (flags.fix || flags.noEslint) {
103
+ return error(
104
+ `Command "connect" does not support --fix or --noEslint flags`,
105
+ );
106
+ }
107
+ return connect({
108
+ root: flags.root,
109
+ });
100
110
  case "validate":
101
111
  case "idate":
102
112
  if (flags.managedDir) {
package/src/connect.ts ADDED
@@ -0,0 +1,126 @@
1
+ import pc from "picocolors";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { evalValConfigFile } from "./utils/evalValConfigFile";
5
+ import { exec } from "child_process";
6
+
7
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
8
+
9
+ export async function connect(options: { root?: string }) {
10
+ const { root } = options;
11
+ const projectRoot = root ? path.resolve(root) : process.cwd();
12
+
13
+ const maybeProject = await tryGetProject(projectRoot);
14
+ const maybeGitRemote = await tryGetGitRemote(projectRoot);
15
+ const maybeGitOwnerAndRepo =
16
+ maybeGitRemote !== null ? getGitHubOwnerAndRepo(maybeGitRemote) : null;
17
+
18
+ const params = new URLSearchParams();
19
+ if (maybeProject) {
20
+ params.set("org", maybeProject.orgName);
21
+ params.set("project", maybeProject.projectName);
22
+ }
23
+ if (maybeGitOwnerAndRepo) {
24
+ params.set(
25
+ "github_repo",
26
+ [maybeGitOwnerAndRepo.owner, maybeGitOwnerAndRepo.repo].join("/"),
27
+ );
28
+ }
29
+ const url = `${host}/connect?${params.toString()}`;
30
+
31
+ // Open url in default browser and show fallback instructions:
32
+ console.log(pc.cyan("\nStarting connect process in browser...\n"));
33
+ console.log(
34
+ pc.dim(`\nIf the browser does not open, please visit:\n${url}\n`),
35
+ );
36
+
37
+ if (process.platform === "win32") {
38
+ // Windows
39
+ exec(`start ${url}`);
40
+ } else if (process.platform === "darwin") {
41
+ // macOS
42
+ exec(`open ${url}`);
43
+ } else {
44
+ // Linux and others
45
+ exec(`xdg-open ${url}`);
46
+ }
47
+ }
48
+
49
+ async function tryGetProject(projectRoot: string): Promise<{
50
+ orgName: string;
51
+ projectName: string;
52
+ } | null> {
53
+ const valConfigFile =
54
+ (await evalValConfigFile(projectRoot, "val.config.ts")) ||
55
+ (await evalValConfigFile(projectRoot, "val.config.js"));
56
+
57
+ if (valConfigFile && valConfigFile.project) {
58
+ const parts = valConfigFile.project.split("/");
59
+ if (parts.length === 2) {
60
+ return {
61
+ orgName: parts[0],
62
+ projectName: parts[1],
63
+ };
64
+ } else {
65
+ console.error(
66
+ pc.red(
67
+ `Invalid project format in val.config file: "${valConfigFile.project}". Expected format "orgName/projectName".`,
68
+ ),
69
+ );
70
+ process.exit(1);
71
+ }
72
+ }
73
+ return null;
74
+ }
75
+
76
+ function getGitHubOwnerAndRepo(
77
+ gitRemote: string,
78
+ ): { owner: string; repo: string } | null {
79
+ const sshMatch = gitRemote.match(/git@github\.com:(.+?)\/(.+?)(\.git)?$/);
80
+ if (sshMatch) {
81
+ return { owner: sshMatch[1], repo: sshMatch[2] };
82
+ }
83
+ const httpsMatch = gitRemote.match(
84
+ /https:\/\/github\.com\/(.+?)\/(.+?)(\.git)?$/,
85
+ );
86
+ if (httpsMatch) {
87
+ return { owner: httpsMatch[1], repo: httpsMatch[2] };
88
+ }
89
+ return null;
90
+ }
91
+
92
+ async function tryGetGitRemote(root: string): Promise<string | null> {
93
+ try {
94
+ const gitConfig = tryGetGitConfig(root);
95
+ if (!gitConfig) {
96
+ return null;
97
+ }
98
+ const remoteMatch = gitConfig.match(
99
+ /\[remote "origin"\][\s\S]*?url = (.+)/,
100
+ );
101
+ if (remoteMatch && remoteMatch[1]) {
102
+ return remoteMatch[1].trim();
103
+ }
104
+ return null;
105
+ } catch (error) {
106
+ console.error(pc.red("Failed to read .git/config file."), error);
107
+ return null;
108
+ }
109
+ }
110
+
111
+ function tryGetGitConfig(root: string): string | null {
112
+ let currentDir = root;
113
+ let lastDir = null;
114
+ while (currentDir !== lastDir) {
115
+ const gitConfigPath = path.join(currentDir, ".git", "config");
116
+ if (fs.existsSync(gitConfigPath)) {
117
+ return fs.readFileSync(gitConfigPath, "utf-8");
118
+ }
119
+ lastDir = currentDir;
120
+ currentDir = path.dirname(currentDir);
121
+ if (lastDir === currentDir) {
122
+ break;
123
+ }
124
+ }
125
+ return null;
126
+ }
@@ -9,14 +9,8 @@ import { createService } from "@valbuild/server";
9
9
  import { glob } from "fast-glob";
10
10
  import path from "path";
11
11
 
12
- export async function files({
13
- root,
14
- managedDir,
15
- }: {
16
- root?: string;
17
- managedDir?: string;
18
- }) {
19
- const printFilesUsedByVal = !managedDir;
12
+ export async function listUnusedFiles({ root }: { root?: string }) {
13
+ const managedDir = "public/val";
20
14
  const projectRoot = root ? path.resolve(root) : process.cwd();
21
15
 
22
16
  const service = await createService(projectRoot, {});
@@ -26,8 +20,8 @@ export async function files({
26
20
  cwd: projectRoot,
27
21
  });
28
22
 
29
- const absoluteFilesPathUsedByVal: string[] = [];
30
- async function printOrGetFileRefs(file: string) {
23
+ const filesUsedByVal: string[] = [];
24
+ async function pushFilesUsedByVal(file: string) {
31
25
  const moduleId = `/${file}` as ModuleFilePath; // TODO: check if this always works? (Windows?)
32
26
  const valModule = await service.get(moduleId, "" as ModulePath, {
33
27
  validate: true,
@@ -47,11 +41,7 @@ export async function files({
47
41
  projectRoot,
48
42
  ...value[FILE_REF_PROP].split("/"),
49
43
  );
50
- if (printFilesUsedByVal) {
51
- console.log(absoluteFilePathUsedByVal);
52
- } else {
53
- absoluteFilesPathUsedByVal.push(absoluteFilePathUsedByVal);
54
- }
44
+ filesUsedByVal.push(absoluteFilePathUsedByVal);
55
45
  }
56
46
  }
57
47
  }
@@ -59,22 +49,18 @@ export async function files({
59
49
  }
60
50
  }
61
51
  for (const file of valFiles) {
62
- await printOrGetFileRefs(file);
52
+ await pushFilesUsedByVal(file);
63
53
  }
64
54
 
65
- if (managedDir) {
66
- const managedRoot = path.isAbsolute(managedDir)
67
- ? managedDir
68
- : path.join(projectRoot, managedDir);
69
- const allFilesInManagedDir = await glob("**/*", {
70
- ignore: ["node_modules/**"],
71
- cwd: managedRoot,
72
- });
73
- for (const file of allFilesInManagedDir) {
74
- const absoluteFilePath = path.join(managedRoot, file);
75
- if (!absoluteFilesPathUsedByVal.includes(absoluteFilePath)) {
76
- console.log(path.join(managedRoot, file));
77
- }
55
+ const managedRoot = path.join(projectRoot, managedDir);
56
+ const allFilesInManagedDir = await glob("**/*", {
57
+ ignore: ["node_modules/**"],
58
+ cwd: managedRoot,
59
+ });
60
+ for (const file of allFilesInManagedDir) {
61
+ const absoluteFilePath = path.join(managedRoot, file);
62
+ if (!filesUsedByVal.includes(absoluteFilePath)) {
63
+ console.log(path.join(managedRoot, file));
78
64
  }
79
65
  }
80
66
 
package/src/login.ts CHANGED
@@ -3,7 +3,7 @@ import fs from "fs";
3
3
  import path from "path";
4
4
  import { getPersonalAccessTokenPath } from "@valbuild/server";
5
5
 
6
- const host = process.env.VAL_BUILD_URL || "https://app.val.build";
6
+ const host = process.env.VAL_BUILD_URL || "https://admin.val.build";
7
7
 
8
8
  export async function login(options: { root?: string }) {
9
9
  try {
@@ -22,9 +22,11 @@ export async function login(options: { root?: string }) {
22
22
  const text = await response.text();
23
23
  console.error(
24
24
  pc.red(
25
- "Unexpected failure while trying to login (content type was not JSON). Server response:",
25
+ "Unexpected failure while trying to login (content type was not JSON). ",
26
26
  ),
27
- text || "<empty>",
27
+ text
28
+ ? `Server response: ${text} (status: ${response.status})`
29
+ : `Status: ${response.status}`,
28
30
  );
29
31
  process.exit(1);
30
32
  }
@@ -61,7 +63,7 @@ export async function login(options: { root?: string }) {
61
63
 
62
64
  const MAX_DURATION = 5 * 60 * 1000; // 5 minutes
63
65
  async function pollForConfirmation(token: string): Promise<{
64
- profile: { username: string };
66
+ profile: { email: string };
65
67
  pat: string;
66
68
  }> {
67
69
  const start = Date.now();
@@ -69,6 +71,7 @@ async function pollForConfirmation(token: string): Promise<{
69
71
  await new Promise((resolve) => setTimeout(resolve, 1000));
70
72
  const response = await fetch(
71
73
  `${host}/api/login?token=${token}&consume=true`,
74
+ { method: "POST" },
72
75
  );
73
76
  if (response.status === 500) {
74
77
  console.error(pc.red("An error occurred on the server."));
@@ -78,7 +81,7 @@ async function pollForConfirmation(token: string): Promise<{
78
81
  const json = await response.json();
79
82
  if (json) {
80
83
  if (
81
- typeof json.profile.username === "string" &&
84
+ typeof json.profile.email === "string" &&
82
85
  typeof json.pat === "string"
83
86
  ) {
84
87
  return json;
@@ -95,7 +98,7 @@ async function pollForConfirmation(token: string): Promise<{
95
98
 
96
99
  function saveToken(
97
100
  result: {
98
- profile: { username: string };
101
+ profile: { email: string };
99
102
  pat: string;
100
103
  },
101
104
  filePath: string,
@@ -105,7 +108,7 @@ function saveToken(
105
108
  console.log(
106
109
  pc.green(
107
110
  `Token for ${pc.cyan(
108
- result.profile.username,
111
+ result.profile.email,
109
112
  )} saved to ${pc.cyan(filePath)}`,
110
113
  ),
111
114
  );