@superblocksteam/cli 0.0.17 → 0.0.18
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 +1 -1
- package/assets/custom-components/example/component.tsx +86 -0
- package/assets/custom-components/example/main.scss +75 -0
- package/assets/custom-components/example/validation.tsx +41 -0
- package/assets/custom-components/{package.json → setup/package.json} +0 -1
- package/dist/commands/components/create.d.ts +2 -0
- package/dist/commands/components/create.js +72 -9
- package/dist/commands/components/upload.js +1 -1
- package/dist/common/authenticated-command.js +2 -1
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
- /package/assets/custom-components/{.eslintrc.js → setup/.eslintrc.js} +0 -0
- /package/assets/custom-components/{tsconfig.json → setup/tsconfig.json} +0 -0
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/0.0.
|
|
15
|
+
@superblocksteam/cli/0.0.18 linux-x64 node-v18.17.0
|
|
16
16
|
$ superblocks --help [COMMAND]
|
|
17
17
|
USAGE
|
|
18
18
|
$ superblocks COMMAND
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import { Task, ErrorComponent, validateTasks } from "./validation";
|
|
3
|
+
import { Props } from "./types";
|
|
4
|
+
import "./main.scss";
|
|
5
|
+
|
|
6
|
+
export default function Component({
|
|
7
|
+
updateStatefulProperties,
|
|
8
|
+
tasks,
|
|
9
|
+
onTaskAdded,
|
|
10
|
+
onTaskStatusChanged,
|
|
11
|
+
}: Props) {
|
|
12
|
+
const { validatedTasks, hasError } = validateTasks(tasks);
|
|
13
|
+
const [value, setValue] = useState("");
|
|
14
|
+
|
|
15
|
+
const onTodoAdded = useCallback(() => {
|
|
16
|
+
const id = Math.random().toString(36).substring(2, 8);
|
|
17
|
+
updateStatefulProperties({
|
|
18
|
+
tasks: {
|
|
19
|
+
...validatedTasks,
|
|
20
|
+
[id]: {
|
|
21
|
+
taskName: value,
|
|
22
|
+
taskStatus: "todo",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
onTaskAdded();
|
|
27
|
+
}, [updateStatefulProperties, validatedTasks, value, onTaskAdded]);
|
|
28
|
+
|
|
29
|
+
const onTaskStatusChange = useCallback(
|
|
30
|
+
(id: string, status: boolean) => {
|
|
31
|
+
updateStatefulProperties({
|
|
32
|
+
tasks: {
|
|
33
|
+
...validatedTasks,
|
|
34
|
+
[id]: {
|
|
35
|
+
...tasks[id],
|
|
36
|
+
taskStatus: status ? "complete" : "todo",
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
onTaskStatusChanged();
|
|
41
|
+
},
|
|
42
|
+
[updateStatefulProperties, validatedTasks, tasks, onTaskStatusChanged]
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
return hasError ? (
|
|
46
|
+
<ErrorComponent />
|
|
47
|
+
) : (
|
|
48
|
+
<div className="sb-example-root">
|
|
49
|
+
<h2>Todo List</h2>
|
|
50
|
+
<div className="horizontal-layout">
|
|
51
|
+
<input
|
|
52
|
+
type="text"
|
|
53
|
+
value={value}
|
|
54
|
+
className="fill"
|
|
55
|
+
placeholder="Add a new item"
|
|
56
|
+
onChange={(e) => setValue(e.target.value)}
|
|
57
|
+
// Prevents the event from bubbling up to the parent (i.e. prevent arrow keys from moving custom component in Superblocks editor)
|
|
58
|
+
onKeyDown={(e) => e.stopPropagation()}
|
|
59
|
+
/>
|
|
60
|
+
<button onClick={onTodoAdded}>Add Todo</button>
|
|
61
|
+
</div>
|
|
62
|
+
<div className="checkboxes">
|
|
63
|
+
{Object.entries(validatedTasks).map(
|
|
64
|
+
([id, task]: [string, Task], idx) => (
|
|
65
|
+
<div key={idx} className="horizontal-layout">
|
|
66
|
+
<input
|
|
67
|
+
type="checkbox"
|
|
68
|
+
checked={task.taskStatus === "complete"}
|
|
69
|
+
onChange={(e) => onTaskStatusChange(id, e.target.checked)}
|
|
70
|
+
/>
|
|
71
|
+
<p>
|
|
72
|
+
<span
|
|
73
|
+
className={
|
|
74
|
+
task.taskStatus === "complete" ? "is-complete" : ""
|
|
75
|
+
}
|
|
76
|
+
>
|
|
77
|
+
{task.taskName}
|
|
78
|
+
</span>
|
|
79
|
+
</p>
|
|
80
|
+
</div>
|
|
81
|
+
)
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// We recommend scoping your styles with a unique classname
|
|
2
|
+
// that won't affect other styles on the page
|
|
3
|
+
// alternatively, you can use CSS modules
|
|
4
|
+
.sb-example-root {
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 100%;
|
|
7
|
+
background-color: #27374d;
|
|
8
|
+
display: flex;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
font-size: 16px;
|
|
11
|
+
font-weight: 600;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
gap: 12px;
|
|
14
|
+
|
|
15
|
+
h1, h2, h3, h4, h5, p, pre {
|
|
16
|
+
color: white;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
p {
|
|
20
|
+
margin: 0px;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
input {
|
|
24
|
+
font: inherit;
|
|
25
|
+
padding: 10px 24px;
|
|
26
|
+
border-radius: 8px;
|
|
27
|
+
color: black;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
button {
|
|
31
|
+
display: inline-block;
|
|
32
|
+
outline: none;
|
|
33
|
+
cursor: pointer;
|
|
34
|
+
line-height: 20px;
|
|
35
|
+
border-radius: 8px;
|
|
36
|
+
padding: 14px 24px;
|
|
37
|
+
border: none;
|
|
38
|
+
transition:
|
|
39
|
+
box-shadow 0.2s ease 0s,
|
|
40
|
+
-ms-transform 0.1s ease 0s,
|
|
41
|
+
-webkit-transform 0.1s ease 0s,
|
|
42
|
+
transform 0.1s ease 0s;
|
|
43
|
+
background: linear-gradient(
|
|
44
|
+
to right,
|
|
45
|
+
rgb(230, 30, 77) 0%,
|
|
46
|
+
rgb(227, 28, 95) 50%,
|
|
47
|
+
rgb(215, 4, 102) 100%
|
|
48
|
+
);
|
|
49
|
+
color: #fff;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
button:active {
|
|
53
|
+
background: linear-gradient(
|
|
54
|
+
to right,
|
|
55
|
+
rgb(198, 20, 61) 0%,
|
|
56
|
+
rgb(188, 8, 68) 50%,
|
|
57
|
+
rgb(208, 0, 97) 100%
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.fill {
|
|
62
|
+
flex-grow: 1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.horizontal-layout {
|
|
66
|
+
display: flex;
|
|
67
|
+
align-items: center;
|
|
68
|
+
text-align: center;
|
|
69
|
+
gap: 8px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.is-complete {
|
|
73
|
+
text-decoration: line-through;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface Task {
|
|
2
|
+
taskName: string;
|
|
3
|
+
taskStatus: "todo" | "complete";
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export const validateTasks = (tasks: any) => {
|
|
7
|
+
return {
|
|
8
|
+
validatedTasks: tasks as Record<string, Task>,
|
|
9
|
+
hasError:
|
|
10
|
+
typeof tasks !== "object" ||
|
|
11
|
+
Array.isArray(tasks) ||
|
|
12
|
+
Object.values(tasks).some((task: any) => {
|
|
13
|
+
return (
|
|
14
|
+
typeof task !== "object" ||
|
|
15
|
+
Array.isArray(task) ||
|
|
16
|
+
!("taskName" in task) ||
|
|
17
|
+
!("taskStatus" in task) ||
|
|
18
|
+
typeof task.taskName !== "string" ||
|
|
19
|
+
typeof task.taskStatus !== "string" ||
|
|
20
|
+
!["todo", "complete"].includes(task.taskStatus)
|
|
21
|
+
);
|
|
22
|
+
}),
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const ErrorComponent: React.FC = () => {
|
|
27
|
+
return (
|
|
28
|
+
<div className="sb-example-root">
|
|
29
|
+
<h3>Invalid Tasks List!</h3>
|
|
30
|
+
<p>Tasks should be of the format:</p>
|
|
31
|
+
<pre>
|
|
32
|
+
{`{
|
|
33
|
+
"<task-id>": {
|
|
34
|
+
taskName: "<task-name>",
|
|
35
|
+
taskStatus: "todo" | "complete",
|
|
36
|
+
},
|
|
37
|
+
...}`}
|
|
38
|
+
</pre>
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AuthenticatedApplicationCommand } from "../../common/authenticated-command";
|
|
2
2
|
export default class CreateComponent extends AuthenticatedApplicationCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
private initializeComponentByWizard;
|
|
5
|
+
private initializeComponentByExample;
|
|
4
6
|
run(): Promise<void>;
|
|
5
7
|
}
|
|
@@ -17,7 +17,8 @@ const create_component_defaults_1 = require("../../common/defaults/create-compon
|
|
|
17
17
|
const identifiers_1 = require("../../util/identifiers");
|
|
18
18
|
// eslint-disable-next-line unicorn/prefer-module
|
|
19
19
|
const rootDirectory = node_path_1.default.resolve(__dirname, "../../../");
|
|
20
|
-
const DEFAULT_PACKAGE_JSON_TEMPLATE_PATH = node_path_1.default.resolve(rootDirectory, "assets/custom-components");
|
|
20
|
+
const DEFAULT_PACKAGE_JSON_TEMPLATE_PATH = node_path_1.default.resolve(rootDirectory, "assets/custom-components/setup");
|
|
21
|
+
const DEFAULT_EXAMPLE_COMPONENT_TEMPLATE_PATH = node_path_1.default.resolve(rootDirectory, "assets/custom-components/example");
|
|
21
22
|
const tsStringify = (obj) => {
|
|
22
23
|
const entries = Object.entries(obj);
|
|
23
24
|
const stringifiedEntries = entries.map(([key, value]) => {
|
|
@@ -26,7 +27,7 @@ const tsStringify = (obj) => {
|
|
|
26
27
|
return `{\n${stringifiedEntries.join(",\n")}\n},\n`;
|
|
27
28
|
};
|
|
28
29
|
class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCommand {
|
|
29
|
-
async
|
|
30
|
+
async initializeComponentByWizard(isFirstTimeCreate) {
|
|
30
31
|
const displayName = (await (0, enquirer_1.prompt)({
|
|
31
32
|
type: "input",
|
|
32
33
|
name: "displayName",
|
|
@@ -163,28 +164,90 @@ class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCo
|
|
|
163
164
|
.join(""),
|
|
164
165
|
});
|
|
165
166
|
const componentTsx = (0, create_component_defaults_1.getDefaultComponentTsx)(statefulProps, eventHandlers.map((prop) => prop.path));
|
|
166
|
-
|
|
167
|
-
if (!packageJsonExists) {
|
|
167
|
+
if (isFirstTimeCreate) {
|
|
168
168
|
await fs.copy(DEFAULT_PACKAGE_JSON_TEMPLATE_PATH, ".");
|
|
169
169
|
}
|
|
170
170
|
await fs.ensureDir("components/" + name);
|
|
171
171
|
await fs.writeFile(`components/${name}/config.ts`, configTs);
|
|
172
172
|
await fs.writeFile(`components/${name}/component.tsx`, componentTsx);
|
|
173
|
+
return {
|
|
174
|
+
name,
|
|
175
|
+
displayName,
|
|
176
|
+
statefulProps,
|
|
177
|
+
eventHandlers,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
async initializeComponentByExample() {
|
|
181
|
+
const response = {
|
|
182
|
+
displayName: "Example Component",
|
|
183
|
+
name: "ExampleComponent",
|
|
184
|
+
statefulProps: [
|
|
185
|
+
{
|
|
186
|
+
label: "Default Tasks",
|
|
187
|
+
path: "tasks",
|
|
188
|
+
inputType: "js",
|
|
189
|
+
placeholder: "{ taskId: { taskName: 'Task Name', taskStatus: 'complete' | 'todo' } }",
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
eventHandlers: [
|
|
193
|
+
{
|
|
194
|
+
label: "On Task Added",
|
|
195
|
+
path: "onTaskAdded",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
label: "On Task Status Changed",
|
|
199
|
+
path: "onTaskStatusChanged",
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
const configTs = (0, create_component_defaults_1.getDefaultConfigTs)({
|
|
204
|
+
id: (0, node_crypto_1.randomUUID)(),
|
|
205
|
+
name: response.name,
|
|
206
|
+
displayName: response.displayName,
|
|
207
|
+
statefulPropsRendered: response.statefulProps
|
|
208
|
+
.map((statefulProp) => tsStringify(statefulProp))
|
|
209
|
+
.join(""),
|
|
210
|
+
eventHandlersRendered: response.eventHandlers
|
|
211
|
+
.map((eventHandler) => tsStringify(eventHandler))
|
|
212
|
+
.join(""),
|
|
213
|
+
});
|
|
214
|
+
await fs.copy(DEFAULT_PACKAGE_JSON_TEMPLATE_PATH, ".");
|
|
215
|
+
await fs.ensureDir("components/" + response.name);
|
|
216
|
+
await fs.copy(DEFAULT_EXAMPLE_COMPONENT_TEMPLATE_PATH, `./components/${response.name}/`);
|
|
217
|
+
await fs.writeFile(`components/${response.name}/config.ts`, configTs);
|
|
218
|
+
return response;
|
|
219
|
+
}
|
|
220
|
+
async run() {
|
|
221
|
+
const isFirstTimeCreate = !(await fs.exists("package.json"));
|
|
222
|
+
const initExampleComponent = isFirstTimeCreate &&
|
|
223
|
+
(await (0, enquirer_1.prompt)({
|
|
224
|
+
name: "value",
|
|
225
|
+
type: "confirm",
|
|
226
|
+
message: "Would you like to use a pre-generated example custom component?",
|
|
227
|
+
initial: false,
|
|
228
|
+
})).value;
|
|
229
|
+
let response;
|
|
230
|
+
if (!initExampleComponent) {
|
|
231
|
+
response = await this.initializeComponentByWizard(isFirstTimeCreate);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
response = await this.initializeComponentByExample();
|
|
235
|
+
}
|
|
173
236
|
this.log((0, colorette_1.green)("Successfully created component! Added the following files:"));
|
|
174
237
|
this.log();
|
|
175
238
|
const tree = core_1.ux.tree();
|
|
176
239
|
tree.insert("components");
|
|
177
|
-
tree.nodes.components.insert(name);
|
|
178
|
-
tree.nodes.components.nodes[name].insert("config.ts # update this file to configure your component's properties panel");
|
|
179
|
-
tree.nodes.components.nodes[name].insert("component.tsx # your component's react code");
|
|
240
|
+
tree.nodes.components.insert(response.name);
|
|
241
|
+
tree.nodes.components.nodes[response.name].insert("config.ts # update this file to configure your component's properties panel");
|
|
242
|
+
tree.nodes.components.nodes[response.name].insert("component.tsx # your component's react code");
|
|
180
243
|
tree.display();
|
|
181
244
|
this.log();
|
|
182
245
|
this.log(`${(0, colorette_1.green)("Remember to run $ ")}${(0, colorette_1.cyan)("superblocks components watch")}${(0, colorette_1.green)(" to watch for changes to your component files")}`);
|
|
183
246
|
this.log();
|
|
184
247
|
this.log(`Edit your component's react code here:
|
|
185
|
-
${(0, colorette_1.green)(`components/${name}/component.tsx`)}`);
|
|
248
|
+
${(0, colorette_1.green)(`components/${response.name}/component.tsx`)}`);
|
|
186
249
|
this.log();
|
|
187
|
-
if (
|
|
250
|
+
if (isFirstTimeCreate) {
|
|
188
251
|
core_1.ux.action.start("Installing dependencies...");
|
|
189
252
|
try {
|
|
190
253
|
await node_util_1.default.promisify(node_child_process_1.exec)("npm i");
|
|
@@ -121,7 +121,7 @@ async function walkThroughDirectory(directory, mutableFiles, excluded) {
|
|
|
121
121
|
const files = await fs.readdir(directory);
|
|
122
122
|
for (const file of files) {
|
|
123
123
|
if (excluded.includes(file))
|
|
124
|
-
|
|
124
|
+
continue;
|
|
125
125
|
const absolute = path_1.default.join(directory, file);
|
|
126
126
|
if ((await fs.stat(absolute)).isDirectory()) {
|
|
127
127
|
await walkThroughDirectory(absolute, mutableFiles, excluded);
|
|
@@ -86,8 +86,9 @@ class AuthenticatedApplicationCommand extends AuthenticatedCommand {
|
|
|
86
86
|
await this.getSdk().registerComponents(this.applicationConfig.id, configs);
|
|
87
87
|
core_1.ux.action.stop();
|
|
88
88
|
}
|
|
89
|
-
catch {
|
|
89
|
+
catch (e) {
|
|
90
90
|
core_1.ux.action.stop();
|
|
91
|
+
console.log(e.message);
|
|
91
92
|
this.error("Failed to register components", { exit: 1 });
|
|
92
93
|
}
|
|
93
94
|
return configs;
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|