@superblocksteam/cli 1.6.0 → 1.7.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.
package/README.md CHANGED
@@ -12,7 +12,7 @@ $ npm install -g @superblocksteam/cli
12
12
  $ superblocks COMMAND
13
13
  running command...
14
14
  $ superblocks (--version)
15
- @superblocksteam/cli/1.6.0 linux-x64 node-v18.20.2
15
+ @superblocksteam/cli/1.7.0 linux-x64 node-v18.20.2
16
16
  $ superblocks --help [COMMAND]
17
17
  USAGE
18
18
  $ superblocks COMMAND
@@ -7,7 +7,7 @@
7
7
  "lint:fix": "npx eslint . --fix"
8
8
  },
9
9
  "dependencies": {
10
- "@superblocksteam/custom-components": "1.6.0",
10
+ "@superblocksteam/custom-components": "1.7.0",
11
11
  "react": "^18",
12
12
  "react-dom": "^18"
13
13
  },
@@ -39,7 +39,7 @@ class Login extends core_1.Command {
39
39
  this.log((0, colorette_1.green)(`Welcome to the Superblocks 🐨 CLI ${user.user.name}!`));
40
40
  }
41
41
  catch (error) {
42
- if (error.name === util_1.ERROR_FILE_ACCESS) {
42
+ if (error instanceof util_1.FileAccessError) {
43
43
  this.log((0, colorette_1.red)("Could not save token, ensure the Superblocks CLI has access to create folders in your home directory."));
44
44
  return;
45
45
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
5
5
  const core_1 = require("@oclif/core");
6
+ const sdk_1 = require("@superblocksteam/sdk");
6
7
  const util_1 = require("@superblocksteam/util");
7
8
  const listr2_1 = require("listr2");
8
9
  const authenticated_command_1 = require("../common/authenticated-command");
@@ -128,14 +129,26 @@ Would you like to also delete these resources from your filesystem?`,
128
129
  var _a, _b;
129
130
  task.title = `Validating git configuration...`;
130
131
  const subtasks = [];
132
+ ctx.resourceIdsToSkip = new Set();
131
133
  for (const resourceId of ctx.resourceIdsToPull) {
132
134
  const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
133
135
  const resourceTitle = `${((_b = resource.resourceType) !== null && _b !== void 0 ? _b : "").toLowerCase()} ${resourceId}`;
134
136
  subtasks.push({
135
137
  title: `Checking ${resourceTitle}...`,
136
138
  task: async () => {
137
- const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PULL, ctx.localBranchName);
138
- ctx.branchToPullFrom.set(resourceId, branchName);
139
+ try {
140
+ const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PULL, ctx.localBranchName);
141
+ ctx.branchToPullFrom.set(resourceId, branchName);
142
+ }
143
+ catch (error) {
144
+ if ((0, version_control_1.isCI)() && error instanceof sdk_1.ValidateGitSetupError) {
145
+ this.log(`WARN: Failed to validate git setup for ${resourceTitle}. Skipping pull.\n\n${error.message}.`);
146
+ }
147
+ else {
148
+ throw error;
149
+ }
150
+ ctx.resourceIdsToSkip.add(resourceId);
151
+ }
139
152
  },
140
153
  });
141
154
  }
@@ -149,6 +162,8 @@ Would you like to also delete these resources from your filesystem?`,
149
162
  var _a, _b;
150
163
  task.title = `Pulling resources from branch ${ctx.localBranchName}...`;
151
164
  const viewMode = await (0, version_control_1.getMode)(task, mode);
165
+ // Remove resources to skip from list of resources to push
166
+ ctx.resourceIdsToPull = ctx.resourceIdsToPull.filter((id) => !ctx.resourceIdsToSkip.has(id));
152
167
  const subtasks = [];
153
168
  for (const resourceId of ctx.resourceIdsToPull) {
154
169
  const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
@@ -135,6 +135,7 @@ Would you like to also delete these resources from your filesystem?`,
135
135
  var _a, _b;
136
136
  task.title = `Validating git configuration...`;
137
137
  const subtasks = [];
138
+ ctx.resourceIdsToSkip = new Set();
138
139
  for (const resourceId of ctx.resourceIdsToPush) {
139
140
  const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
140
141
  // for user messages:
@@ -142,8 +143,43 @@ Would you like to also delete these resources from your filesystem?`,
142
143
  subtasks.push({
143
144
  title: `Checking ${resourceTitle}...`,
144
145
  task: async () => {
145
- const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PUSH, ctx.localBranchName);
146
- ctx.branchToPushTo.set(resourceId, branchName);
146
+ try {
147
+ const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PUSH, ctx.localBranchName);
148
+ ctx.branchToPushTo.set(resourceId, branchName);
149
+ }
150
+ catch (error) {
151
+ if ((0, version_control_1.isCI)() && error instanceof sdk_1.ValidateGitSetupError) {
152
+ this.log(`WARN: Failed to validate git setup for ${resourceTitle}. Skipping push.\n\n${error.message}.`);
153
+ }
154
+ else {
155
+ throw error;
156
+ }
157
+ ctx.resourceIdsToSkip.add(resourceId);
158
+ }
159
+ },
160
+ });
161
+ }
162
+ return task.newListr(subtasks, {
163
+ concurrent: true,
164
+ });
165
+ },
166
+ },
167
+ {
168
+ task: async (ctx, task) => {
169
+ var _a, _b;
170
+ task.title = `Validating project structure...`;
171
+ const subtasks = [];
172
+ const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
173
+ for (const resourceId of ctx.resourceIdsToPush) {
174
+ const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
175
+ const resourceTitle = `${((_b = resource.resourceType) !== null && _b !== void 0 ? _b : "").toLowerCase()} at ${resource.location}`;
176
+ subtasks.push({
177
+ title: `Validating ${resourceTitle}...`,
178
+ task: async () => {
179
+ const validationError = await (0, version_control_1.validateLocalResource)(superblocksRootPath, resource);
180
+ if (validationError) {
181
+ this.error(`Push failed for resource '${resource.location}' (id: ${resourceId}). ${validationError}`);
182
+ }
147
183
  },
148
184
  });
149
185
  }
@@ -156,6 +192,8 @@ Would you like to also delete these resources from your filesystem?`,
156
192
  task: async (ctx, task) => {
157
193
  var _a;
158
194
  task.title = `Pushing resources to branch ${ctx.localBranchName}...`;
195
+ // Remove resources to skip from list of resources to push
196
+ ctx.resourceIdsToPush = ctx.resourceIdsToPush.filter((id) => !ctx.resourceIdsToSkip.has(id));
159
197
  const subtasks = [];
160
198
  const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
161
199
  for (const resourceId of ctx.resourceIdsToPush) {
@@ -231,7 +269,7 @@ Would you like to also delete these resources from your filesystem?`,
231
269
  });
232
270
  }
233
271
  catch (error) {
234
- if ((error === null || error === void 0 ? void 0 : error.name) === "BranchNotCheckedOutError") {
272
+ if (error instanceof sdk_1.BranchNotCheckedOutError) {
235
273
  this.log(`WARN: Workflow/Scheduled Job ${(0, version_control_1.extractApiName)(apiConfig)} failed to push, please check branch out in the Superblocks UI first in order to check this branch out for this resource.`);
236
274
  }
237
275
  else if (error instanceof sdk_1.CommitAlreadyExistsError) {
@@ -36,6 +36,8 @@ export declare function getCurrentGitBranchIfGit(): Promise<string | null>;
36
36
  */
37
37
  export declare function getCurrentGitBranch(): Promise<string>;
38
38
  export declare function getHeadCommit(branch: string): Promise<[string, string]>;
39
+ export declare function isCI(): boolean;
39
40
  export declare function isGitRepoDirty(): Promise<boolean>;
40
41
  export declare function extractApiName(api: ApiWrapper): string;
42
+ export declare function validateLocalResource(superblocksRootPath: string, resource: VersionedResourceConfig): Promise<string | undefined>;
41
43
  export declare function deleteResourcesAndUpdateRootConfig(removedResourceIds: string[], existingSuperblocksRootConfig: SuperblocksMonorepoConfig, superblocksRootPath: string, superblocksRootConfigPath: string): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deleteResourcesAndUpdateRootConfig = exports.extractApiName = exports.isGitRepoDirty = exports.getHeadCommit = exports.getCurrentGitBranch = exports.getCurrentGitBranchIfGit = exports.getLocalGitRepoState = exports.sortByKey = exports.getMode = exports.removeResourceFromDisk = exports.writeMultiPageApplicationToDisk = exports.writeResourceToDisk = exports.readApiFromDisk = exports.readMultiPageApplicationFromDisk = exports.readApplicationFromDisk = exports.getFileStructureType = exports.atLeastOneSelection = exports.MULTI_SELECT_PROMPT_HELP = exports.SELECT_PROMPT_HELP = exports.modeFlagToViewMode = exports.FileStructureType = exports.modeFlagValuesMap = exports.DEFAULT_BRANCH = exports.DEPLOYED_MODE = exports.MOST_RECENT_COMMIT_MODE = exports.LATEST_EDITS_MODE = void 0;
3
+ exports.deleteResourcesAndUpdateRootConfig = exports.validateLocalResource = exports.extractApiName = exports.isGitRepoDirty = exports.isCI = exports.getHeadCommit = exports.getCurrentGitBranch = exports.getCurrentGitBranchIfGit = exports.getLocalGitRepoState = exports.sortByKey = exports.getMode = exports.removeResourceFromDisk = exports.writeMultiPageApplicationToDisk = exports.writeResourceToDisk = exports.readApiFromDisk = exports.readMultiPageApplicationFromDisk = exports.readApplicationFromDisk = exports.getFileStructureType = exports.atLeastOneSelection = exports.MULTI_SELECT_PROMPT_HELP = exports.SELECT_PROMPT_HELP = exports.modeFlagToViewMode = exports.FileStructureType = exports.modeFlagValuesMap = exports.DEFAULT_BRANCH = exports.DEPLOYED_MODE = exports.MOST_RECENT_COMMIT_MODE = exports.LATEST_EDITS_MODE = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const https = tslib_1.__importStar(require("https"));
6
6
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
@@ -574,8 +574,12 @@ async function getHeadCommit(branch) {
574
574
  return [headCommitId, headCommitMessage];
575
575
  }
576
576
  exports.getHeadCommit = getHeadCommit;
577
+ function isCI() {
578
+ return process.env.CI === "true";
579
+ }
580
+ exports.isCI = isCI;
577
581
  async function isGitRepoDirty() {
578
- if (process.env.CI === "true") {
582
+ if (isCI()) {
579
583
  // Skip dirtiness check in CI environments
580
584
  return false;
581
585
  }
@@ -600,6 +604,123 @@ function writeApi(api, originalApiName, appDirName) {
600
604
  };
601
605
  return handleApi();
602
606
  }
607
+ function validateSinglePageApplication(applicationConfig, superblocksRootPath, location) {
608
+ // validate app level APIs
609
+ for (const apiName of Object.values(applicationConfig.apis)) {
610
+ const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "apis", `${slugifyName(apiName)}.yaml`);
611
+ const validateApiError = validateYamlFile(apiPath);
612
+ if (validateApiError) {
613
+ return validateApiError;
614
+ }
615
+ }
616
+ // validate page
617
+ const pagePath = node_path_1.default.resolve(superblocksRootPath, location, "page.yaml");
618
+ const validatePageError = validateYamlFile(pagePath);
619
+ if (validatePageError) {
620
+ return validatePageError;
621
+ }
622
+ }
623
+ async function validateMultiPageApplication(applicationConfig, superblocksRootPath, location) {
624
+ var _a;
625
+ // validate app level APIs
626
+ for (const apiName of Object.values(applicationConfig.apis)) {
627
+ const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "apis", `${slugifyName(apiName)}.yaml`);
628
+ const validateApiError = await validateYamlFile(apiPath);
629
+ if (validateApiError) {
630
+ return validateApiError;
631
+ }
632
+ }
633
+ // validate pages
634
+ for (const page of Object.values((_a = applicationConfig.pages) !== null && _a !== void 0 ? _a : {})) {
635
+ const pagePath = node_path_1.default.resolve(superblocksRootPath, location, "pages", slugifyName(page.name), "page.yaml");
636
+ const validatePageError = await validateYamlFile(pagePath);
637
+ if (validatePageError) {
638
+ return validatePageError;
639
+ }
640
+ // validate page level APIs
641
+ for (const apiName of Object.values(page.apis)) {
642
+ const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "pages", slugifyName(page.name), "apis", `${slugifyName(apiName)}.yaml`);
643
+ const validateApiError = await validateYamlFile(apiPath);
644
+ if (validateApiError) {
645
+ return validateApiError;
646
+ }
647
+ }
648
+ }
649
+ return undefined;
650
+ }
651
+ async function validateLocalResource(superblocksRootPath, resource) {
652
+ switch (resource.resourceType) {
653
+ case "APPLICATION": {
654
+ // make sure application config exists
655
+ const applicationConfigPath = node_path_1.default.resolve(superblocksRootPath, resource.location, util_2.RESOURCE_CONFIG_PATH);
656
+ if (!(await fs.pathExists(applicationConfigPath))) {
657
+ return `File ${relativeToCurrentDir(applicationConfigPath)} not found. Superblocks CLI commands cannot function without it.`;
658
+ }
659
+ let fileStructureType = undefined;
660
+ let applicationConfig = undefined;
661
+ try {
662
+ // make sure it's a well-formed application config
663
+ applicationConfig = await fs.readJSON(applicationConfigPath);
664
+ if (!applicationConfig) {
665
+ throw new Error();
666
+ }
667
+ fileStructureType =
668
+ getFileStructureTypeFromResourceConfig(applicationConfig);
669
+ }
670
+ catch {
671
+ return `File ${relativeToCurrentDir(applicationConfigPath)} is not a valid JSON file. Please be sure it's valid JSON and rerun the command.`;
672
+ }
673
+ const applicationYamlPath = node_path_1.default.resolve(superblocksRootPath, resource.location, "application.yaml");
674
+ // make sure application.yaml is a well-formed yaml file
675
+ try {
676
+ await readYamlFile(applicationYamlPath);
677
+ }
678
+ catch {
679
+ return `File ${relativeToCurrentDir(applicationYamlPath)} is not a valid YAML file. Please be sure it's valid YAML and rerun the command.`;
680
+ }
681
+ switch (fileStructureType) {
682
+ case FileStructureType.SINGLE_PAGE: {
683
+ validateSinglePageApplication(applicationConfig, superblocksRootPath, resource.location);
684
+ break;
685
+ }
686
+ case FileStructureType.MULTI_PAGE: {
687
+ const validationError = validateMultiPageApplication(applicationConfig, superblocksRootPath, resource.location);
688
+ if (validationError) {
689
+ return validationError;
690
+ }
691
+ break;
692
+ }
693
+ default: {
694
+ return `Invalid file structure type ${fileStructureType}`;
695
+ }
696
+ }
697
+ break;
698
+ }
699
+ case "BACKEND": {
700
+ // make sure the backend config exists
701
+ const backendConfigPath = node_path_1.default.resolve(superblocksRootPath, resource.location, util_2.RESOURCE_CONFIG_PATH);
702
+ if (!(await fs.pathExists(backendConfigPath))) {
703
+ return `File ${relativeToCurrentDir(backendConfigPath)} not found. Superblocks CLI commands cannot function without it.`;
704
+ }
705
+ // make sure it's a well-formed backend config
706
+ try {
707
+ await fs.readJSON(backendConfigPath);
708
+ }
709
+ catch {
710
+ return `File ${relativeToCurrentDir(backendConfigPath)} is not a valid JSON file. Please be sure it's valid JSON and rerun the command.`;
711
+ }
712
+ // make sure that api.yaml exists
713
+ const apiYamlPath = node_path_1.default.resolve(superblocksRootPath, resource.location, "api.yaml");
714
+ const validateYamlFileError = await validateYamlFile(apiYamlPath);
715
+ if (validateYamlFileError) {
716
+ return validateYamlFileError;
717
+ }
718
+ break;
719
+ }
720
+ }
721
+ return undefined;
722
+ }
723
+ exports.validateLocalResource = validateLocalResource;
603
724
  async function deleteResourcesAndUpdateRootConfig(removedResourceIds, existingSuperblocksRootConfig, superblocksRootPath, superblocksRootConfigPath) {
604
725
  for (const resourceId of removedResourceIds) {
605
726
  const resource = existingSuperblocksRootConfig === null || existingSuperblocksRootConfig === void 0 ? void 0 : existingSuperblocksRootConfig.resources[resourceId];
@@ -612,3 +733,15 @@ async function deleteResourcesAndUpdateRootConfig(removedResourceIds, existingSu
612
733
  await fs.writeFile(superblocksRootConfigPath, JSON.stringify(sortByKey(existingSuperblocksRootConfig), null, 2));
613
734
  }
614
735
  exports.deleteResourcesAndUpdateRootConfig = deleteResourcesAndUpdateRootConfig;
736
+ async function validateYamlFile(yamlPath) {
737
+ // make sure a yaml file is well-formed
738
+ try {
739
+ await readYamlFile(yamlPath);
740
+ }
741
+ catch {
742
+ return `File ${relativeToCurrentDir(yamlPath)} is not a valid YAML file. Please be sure it's valid YAML and rerun the command.`;
743
+ }
744
+ }
745
+ function relativeToCurrentDir(applicationConfigPath) {
746
+ return `./${node_path_1.default.relative(process.cwd(), applicationConfigPath)}`;
747
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.6.0",
2
+ "version": "1.7.0",
3
3
  "commands": {
4
4
  "init": {
5
5
  "id": "init",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/cli",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Official Superblocks CLI",
5
5
  "bin": {
6
6
  "superblocks": "bin/run"
@@ -18,11 +18,11 @@
18
18
  "@oclif/core": "^2.11.7",
19
19
  "@oclif/plugin-help": "^5.2.16",
20
20
  "@oclif/plugin-plugins": "^3.1.10",
21
- "@superblocksteam/css-plugin": "1.6.0",
22
- "@superblocksteam/react-shim": "1.6.0",
23
- "@superblocksteam/sdk": "1.6.0",
24
- "@superblocksteam/util": "1.6.0",
25
- "@superblocksteam/vite-custom-component-reload-plugin": "1.6.0",
21
+ "@superblocksteam/css-plugin": "1.7.0",
22
+ "@superblocksteam/react-shim": "1.7.0",
23
+ "@superblocksteam/sdk": "1.7.0",
24
+ "@superblocksteam/util": "1.7.0",
25
+ "@superblocksteam/vite-custom-component-reload-plugin": "1.7.0",
26
26
  "@vitejs/plugin-react": "^4.1.0",
27
27
  "colorette": "^2.0.19",
28
28
  "enquirer": "^2.3.6",