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