@superblocksteam/util 0.0.14 → 0.0.15

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/.eslintrc.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "parser": "@typescript-eslint/parser",
3
+ "plugins": [
4
+ "@typescript-eslint",
5
+ "prettier",
6
+ "import",
7
+ "unicorn"
8
+ ],
9
+ "extends": [
10
+ "eslint:recommended",
11
+ "plugin:@typescript-eslint/recommended",
12
+ "plugin:prettier/recommended",
13
+ "prettier",
14
+ "plugin:import/typescript"
15
+ ],
16
+ "parserOptions": {
17
+ "ecmaVersion": 2020,
18
+ "sourceType": "module",
19
+ "project": true
20
+ },
21
+ "rules": {
22
+ "@typescript-eslint/explicit-module-boundary-types": "off",
23
+ "@typescript-eslint/no-explicit-any": "off",
24
+ "@typescript-eslint/no-unused-vars": "error",
25
+ "@typescript-eslint/no-var-requires": "off",
26
+ "@typescript-eslint/no-unsafe-return": "off",
27
+ "@typescript-eslint/no-unsafe-assignment": "off",
28
+ "@typescript-eslint/no-unsafe-member-access": "off",
29
+ "import/order": [
30
+ "error",
31
+ {
32
+ "alphabetize": { "order": "asc" },
33
+ "groups": [
34
+ "builtin",
35
+ "external",
36
+ "internal",
37
+ "parent",
38
+ "sibling",
39
+ "index",
40
+ "object",
41
+ "type"
42
+ ]
43
+ }
44
+ ],
45
+ "import/no-cycle": "error",
46
+ "no-template-curly-in-string": "off",
47
+ "no-useless-escape": "off",
48
+ "no-void": ["error", { "allowAsStatement": true }]
49
+ },
50
+ "ignorePatterns": ["public", "build", "node_modules", "dist", "tsup.config.ts"]
51
+ }
@@ -1 +1,4 @@
1
- export declare function getComponentConfigs(): Promise<Record<string, any>>;
1
+ export declare function getComponentConfigs(): Promise<{
2
+ configs: Record<string, any>;
3
+ hasError: boolean;
4
+ }>;
@@ -2,17 +2,36 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getComponentConfigs = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const node_path_1 = tslib_1.__importDefault(require("node:path"));
5
+ const child_process_1 = require("child_process");
6
6
  const node_os_1 = tslib_1.__importDefault(require("node:os"));
7
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
7
8
  const fs = tslib_1.__importStar(require("fs-extra"));
8
9
  const constants_1 = require("./constants");
9
- const child_process_1 = require("child_process");
10
+ const validation_1 = require("./validation");
10
11
  const compileTypeScript = (inputFilePath, outputDir) => {
11
12
  return new Promise((resolve, reject) => {
12
13
  try {
13
14
  const tscPath = require.resolve("typescript/bin/tsc");
14
15
  if (tscPath) {
15
- (0, child_process_1.execFile)(tscPath, [node_path_1.default.resolve(inputFilePath), "--outDir", outputDir, "--jsx", "react-jsx", "--target", "es2019", "--esModuleInterop", "true", "--moduleResolution", "node", "--module", "commonjs", "--strict", "true", "--skipLibCheck", "true"], (error, stdout, stderr) => {
16
+ (0, child_process_1.execFile)(tscPath, [
17
+ node_path_1.default.resolve(inputFilePath),
18
+ "--outDir",
19
+ outputDir,
20
+ "--jsx",
21
+ "react-jsx",
22
+ "--target",
23
+ "es2019",
24
+ "--esModuleInterop",
25
+ "true",
26
+ "--moduleResolution",
27
+ "node",
28
+ "--module",
29
+ "commonjs",
30
+ "--strict",
31
+ "true",
32
+ "--skipLibCheck",
33
+ "true",
34
+ ], (error, stdout, stderr) => {
16
35
  if (error) {
17
36
  // Print any Typescript output to the user, such as errors
18
37
  console.log(stdout);
@@ -49,6 +68,7 @@ async function getFolderPaths() {
49
68
  }
50
69
  async function getComponentConfigs() {
51
70
  const folderPaths = await getFolderPaths();
71
+ let hasError = false;
52
72
  const promiseConfigFiles = folderPaths.map(async (ccpath) => {
53
73
  try {
54
74
  const ret = await fs.readJSON(`${ccpath}/config.json`);
@@ -72,7 +92,13 @@ async function getComponentConfigs() {
72
92
  await compileTypeScript(fileName, outputDirectory);
73
93
  console.log(`Typescript compiled to ${ccpath}`);
74
94
  const ret = (await Promise.resolve(`${outputDirectory + "/config.js"}`).then(s => tslib_1.__importStar(require(s)))).default;
75
- console.log(`Found component in ${ccpath}`);
95
+ const isValid = (0, validation_1.validateCustomComponents)(ret);
96
+ if (!isValid.valid) {
97
+ // Not throwing because we don't need a stack trace here
98
+ console.log(`Invalid config.ts file found`);
99
+ console.log(isValid.message);
100
+ hasError = true;
101
+ }
76
102
  return ret;
77
103
  }
78
104
  catch (e) {
@@ -87,6 +113,9 @@ async function getComponentConfigs() {
87
113
  acc[config.id] = config;
88
114
  return acc;
89
115
  }, {});
90
- return configFiles;
116
+ return {
117
+ configs: configFiles,
118
+ hasError,
119
+ };
91
120
  }
92
121
  exports.getComponentConfigs = getComponentConfigs;
@@ -4,10 +4,11 @@ export type SuperblocksMetadata = {
4
4
  lastUpdated: string;
5
5
  created: string;
6
6
  };
7
+ export type SuperblocksResourceType = "APPLICATION" | "BACKEND";
7
8
  export type VersionedResourceConfig = {
8
9
  location: string;
9
10
  lastUpdated: string;
10
- resourceType: "APPLICATION" | "BACKEND";
11
+ resourceType: SuperblocksResourceType;
11
12
  };
12
13
  export type SuperblocksResourceConfig = {
13
14
  id: string;
@@ -70,11 +70,15 @@ async function getSuperblocksResourceConfigIfExists() {
70
70
  try {
71
71
  config = await getSuperblocksApplicationConfigJson();
72
72
  }
73
- catch { }
73
+ catch {
74
+ // Noop
75
+ }
74
76
  try {
75
77
  config = await getSuperblocksBackendConfigJson();
76
78
  }
77
- catch { }
79
+ catch {
80
+ // Noop
81
+ }
78
82
  return config;
79
83
  }
80
84
  exports.getSuperblocksResourceConfigIfExists = getSuperblocksResourceConfigIfExists;
@@ -0,0 +1,4 @@
1
+ export declare function validateCustomComponents(data: any): {
2
+ valid: boolean;
3
+ message?: string;
4
+ };
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateCustomComponents = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const ajv_1 = tslib_1.__importDefault(require("ajv"));
6
+ const better_ajv_errors_1 = tslib_1.__importDefault(require("better-ajv-errors"));
7
+ const ajv = new ajv_1.default();
8
+ const componentSchema = {
9
+ type: "object",
10
+ properties: {
11
+ id: { type: "string" },
12
+ name: { type: "string" },
13
+ displayName: { type: "string" },
14
+ componentPath: { type: "string" },
15
+ statefulProperties: {
16
+ type: "array",
17
+ items: {
18
+ $ref: "#/definitions/StatefulProperty",
19
+ },
20
+ },
21
+ eventHandlers: {
22
+ type: "array",
23
+ items: {
24
+ $ref: "#/definitions/EventHandler",
25
+ },
26
+ },
27
+ },
28
+ required: [
29
+ "id",
30
+ "name",
31
+ "displayName",
32
+ "componentPath",
33
+ "statefulProperties",
34
+ "eventHandlers",
35
+ ],
36
+ additionalProperties: false,
37
+ definitions: {
38
+ StatefulProperty: {
39
+ properties: {
40
+ label: { type: "string" },
41
+ path: { type: "string" },
42
+ inputType: { enum: ["text", "number", "boolean", "js"] },
43
+ placeholder: { type: "string" },
44
+ },
45
+ type: "object",
46
+ additionalProperties: false,
47
+ required: ["label", "path", "inputType"],
48
+ },
49
+ EventHandler: {
50
+ properties: {
51
+ label: { type: "string" },
52
+ path: { type: "string" },
53
+ },
54
+ type: "object",
55
+ additionalProperties: false,
56
+ required: ["label", "path"],
57
+ },
58
+ },
59
+ };
60
+ const validate = ajv.compile(componentSchema);
61
+ function validateCustomComponents(data) {
62
+ const valid = validate(data);
63
+ if (!valid) {
64
+ return {
65
+ valid: false,
66
+ message: (0, better_ajv_errors_1.default)(componentSchema, data, validate.errors, {
67
+ indent: 2,
68
+ }),
69
+ };
70
+ }
71
+ return { valid: true };
72
+ }
73
+ exports.validateCustomComponents = validateCustomComponents;
package/package.json CHANGED
@@ -1,17 +1,28 @@
1
1
  {
2
2
  "name": "@superblocksteam/util",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "main": "dist/index.js",
5
5
  "homepage": "https://www.superblocks.com",
6
6
  "scripts": {
7
7
  "dev": "tsc --watch",
8
8
  "build": "tsc --build",
9
9
  "clean": "npm run clean:build && rm -rf node_modules",
10
- "clean:build": "tsc --build --clean && rm -rf dist tsconfig.tsbuildinfo"
10
+ "clean:build": "tsc --build --clean && rm -rf dist tsconfig.tsbuildinfo",
11
+ "lint": "eslint . --ext .ts --config .eslintrc.json",
12
+ "lint:fix": "eslint . --ext .ts --config .eslintrc.json --fix"
11
13
  },
12
14
  "license": "Superblocks Community Software License",
13
15
  "description": "",
16
+ "dependencies": {
17
+ "ajv": "^8.12.0",
18
+ "better-ajv-errors": "^1.2.0"
19
+ },
14
20
  "devDependencies": {
15
- "typescript": "^5.0.4"
21
+ "@typescript-eslint/eslint-plugin": "^5.60.1",
22
+ "@typescript-eslint/parser": "^5.60.1",
23
+ "eslint": "^8.45.0",
24
+ "eslint-plugin-unicorn": "^47.0.0",
25
+ "prettier": "^2.8.8",
26
+ "typescript": "^5.1.3"
16
27
  }
17
28
  }
@@ -1,8 +1,9 @@
1
- import path from "node:path";
1
+ import { execFile } from "child_process";
2
2
  import os from "node:os";
3
+ import path from "node:path";
3
4
  import * as fs from "fs-extra";
4
5
  import { CUSTOM_COMPONENTS_PATH } from "./constants";
5
- import { execFile } from "child_process";
6
+ import { validateCustomComponents } from "./validation";
6
7
 
7
8
  const compileTypeScript = (inputFilePath: string, outputDir: string) => {
8
9
  return new Promise((resolve, reject) => {
@@ -11,7 +12,25 @@ const compileTypeScript = (inputFilePath: string, outputDir: string) => {
11
12
  if (tscPath) {
12
13
  execFile(
13
14
  tscPath,
14
- [path.resolve(inputFilePath), "--outDir", outputDir, "--jsx", "react-jsx", "--target", "es2019", "--esModuleInterop", "true", "--moduleResolution", "node", "--module", "commonjs", "--strict", "true", "--skipLibCheck", "true"],
15
+ [
16
+ path.resolve(inputFilePath),
17
+ "--outDir",
18
+ outputDir,
19
+ "--jsx",
20
+ "react-jsx",
21
+ "--target",
22
+ "es2019",
23
+ "--esModuleInterop",
24
+ "true",
25
+ "--moduleResolution",
26
+ "node",
27
+ "--module",
28
+ "commonjs",
29
+ "--strict",
30
+ "true",
31
+ "--skipLibCheck",
32
+ "true",
33
+ ],
15
34
  (error, stdout, stderr) => {
16
35
  if (error) {
17
36
  // Print any Typescript output to the user, such as errors
@@ -28,7 +47,11 @@ const compileTypeScript = (inputFilePath: string, outputDir: string) => {
28
47
  );
29
48
  }
30
49
  } catch (e) {
31
- reject(new Error("Could not find TypeScript, please install it globally (npm install -g typescript)"));
50
+ reject(
51
+ new Error(
52
+ "Could not find TypeScript, please install it globally (npm install -g typescript)"
53
+ )
54
+ );
32
55
  }
33
56
  });
34
57
  };
@@ -52,8 +75,12 @@ async function getFolderPaths() {
52
75
  }
53
76
  }
54
77
 
55
- export async function getComponentConfigs(): Promise<Record<string, any>> {
78
+ export async function getComponentConfigs(): Promise<{
79
+ configs: Record<string, any>;
80
+ hasError: boolean;
81
+ }> {
56
82
  const folderPaths = await getFolderPaths();
83
+ let hasError = false;
57
84
  const promiseConfigFiles = folderPaths.map(async (ccpath) => {
58
85
  try {
59
86
  const ret = await fs.readJSON(`${ccpath}/config.json`);
@@ -79,7 +106,14 @@ export async function getComponentConfigs(): Promise<Record<string, any>> {
79
106
  await compileTypeScript(fileName, outputDirectory);
80
107
  console.log(`Typescript compiled to ${ccpath}`);
81
108
  const ret = (await import(outputDirectory + "/config.js")).default;
82
- console.log(`Found component in ${ccpath}`);
109
+
110
+ const isValid = validateCustomComponents(ret);
111
+ if (!isValid.valid) {
112
+ // Not throwing because we don't need a stack trace here
113
+ console.log(`Invalid config.ts file found`);
114
+ console.log(isValid.message);
115
+ hasError = true;
116
+ }
83
117
  return ret;
84
118
  } catch (e: any) {
85
119
  console.error(e);
@@ -93,5 +127,8 @@ export async function getComponentConfigs(): Promise<Record<string, any>> {
93
127
  acc[config.id] = config;
94
128
  return acc;
95
129
  }, {});
96
- return configFiles;
130
+ return {
131
+ configs: configFiles,
132
+ hasError,
133
+ };
97
134
  }
@@ -8,10 +8,12 @@ export type SuperblocksMetadata = {
8
8
  created: string;
9
9
  };
10
10
 
11
+ export type SuperblocksResourceType = "APPLICATION" | "BACKEND";
12
+
11
13
  export type VersionedResourceConfig = {
12
14
  location: string;
13
15
  lastUpdated: string;
14
- resourceType: "APPLICATION" | "BACKEND";
16
+ resourceType: SuperblocksResourceType;
15
17
  };
16
18
 
17
19
  export type SuperblocksResourceConfig = {
@@ -123,9 +125,13 @@ export async function getSuperblocksResourceConfigIfExists(): Promise<
123
125
  let config: SuperblocksResourceConfig | undefined;
124
126
  try {
125
127
  config = await getSuperblocksApplicationConfigJson();
126
- } catch {}
128
+ } catch {
129
+ // Noop
130
+ }
127
131
  try {
128
132
  config = await getSuperblocksBackendConfigJson();
129
- } catch {}
133
+ } catch {
134
+ // Noop
135
+ }
130
136
  return config;
131
137
  }
@@ -0,0 +1,75 @@
1
+ import Ajv, { Schema } from "ajv";
2
+ import betterAjvErrors from "better-ajv-errors";
3
+
4
+ const ajv = new Ajv();
5
+
6
+ const componentSchema: Schema = {
7
+ type: "object",
8
+ properties: {
9
+ id: { type: "string" },
10
+ name: { type: "string" },
11
+ displayName: { type: "string" },
12
+ componentPath: { type: "string" },
13
+ statefulProperties: {
14
+ type: "array",
15
+ items: {
16
+ $ref: "#/definitions/StatefulProperty",
17
+ },
18
+ },
19
+ eventHandlers: {
20
+ type: "array",
21
+ items: {
22
+ $ref: "#/definitions/EventHandler",
23
+ },
24
+ },
25
+ },
26
+ required: [
27
+ "id",
28
+ "name",
29
+ "displayName",
30
+ "componentPath",
31
+ "statefulProperties",
32
+ "eventHandlers",
33
+ ],
34
+ additionalProperties: false,
35
+ definitions: {
36
+ StatefulProperty: {
37
+ properties: {
38
+ label: { type: "string" },
39
+ path: { type: "string" },
40
+ inputType: { enum: ["text", "number", "boolean", "js"] },
41
+ placeholder: { type: "string" },
42
+ },
43
+ type: "object",
44
+ additionalProperties: false,
45
+ required: ["label", "path", "inputType"],
46
+ },
47
+ EventHandler: {
48
+ properties: {
49
+ label: { type: "string" },
50
+ path: { type: "string" },
51
+ },
52
+ type: "object",
53
+ additionalProperties: false,
54
+ required: ["label", "path"],
55
+ },
56
+ },
57
+ };
58
+
59
+ const validate = ajv.compile(componentSchema);
60
+
61
+ export function validateCustomComponents(data: any): {
62
+ valid: boolean;
63
+ message?: string;
64
+ } {
65
+ const valid = validate(data);
66
+ if (!valid) {
67
+ return {
68
+ valid: false,
69
+ message: betterAjvErrors(componentSchema, data, validate.errors as any, {
70
+ indent: 2,
71
+ }),
72
+ };
73
+ }
74
+ return { valid: true };
75
+ }