@superblocksteam/cli 0.0.20 → 0.0.22
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 +19 -3
- package/assets/custom-components/example/component.tsx +15 -12
- package/assets/custom-components/setup/gitignore_will_rename +2 -0
- package/assets/custom-components/setup/package.json +1 -1
- package/dist/commands/components/create.d.ts +1 -0
- package/dist/commands/components/create.js +113 -68
- package/dist/commands/components/register.js +5 -1
- package/dist/commands/components/upload.js +4 -1
- package/dist/commands/components/watch.js +6 -3
- package/dist/commands/init.js +16 -3
- package/dist/commands/login.js +1 -1
- package/dist/commands/migrate.d.ts +9 -0
- package/dist/commands/migrate.js +147 -0
- package/dist/commands/pull.js +8 -1
- package/dist/common/authenticated-command.d.ts +2 -1
- package/dist/common/authenticated-command.js +103 -3
- package/dist/common/defaults/create-component-defaults.d.ts +4 -4
- package/dist/common/defaults/create-component-defaults.js +7 -7
- package/dist/common/version-control.js +5 -3
- package/dist/util/migrationWarningsForApplications.d.ts +1 -0
- package/dist/util/migrationWarningsForApplications.js +38 -0
- package/dist/util/migrationsForDotfiles.d.ts +10 -0
- package/dist/util/migrationsForDotfiles.js +55 -0
- package/oclif.manifest.json +16 -2
- package/package.json +2 -1
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.22 linux-x64 node-v18.17.0
|
|
16
16
|
$ superblocks --help [COMMAND]
|
|
17
17
|
USAGE
|
|
18
18
|
$ superblocks COMMAND
|
|
@@ -29,6 +29,7 @@ USAGE
|
|
|
29
29
|
* [`superblocks help [COMMANDS]`](#superblocks-help-commands)
|
|
30
30
|
* [`superblocks init [RESOURCEURL]`](#superblocks-init-resourceurl)
|
|
31
31
|
* [`superblocks login`](#superblocks-login)
|
|
32
|
+
* [`superblocks migrate`](#superblocks-migrate)
|
|
32
33
|
* [`superblocks pull [ONLY]`](#superblocks-pull-only)
|
|
33
34
|
|
|
34
35
|
## `superblocks components create`
|
|
@@ -154,7 +155,7 @@ EXAMPLES
|
|
|
154
155
|
|
|
155
156
|
## `superblocks login`
|
|
156
157
|
|
|
157
|
-
|
|
158
|
+
Authenticate with Superblocks cloud
|
|
158
159
|
|
|
159
160
|
```
|
|
160
161
|
USAGE
|
|
@@ -164,7 +165,22 @@ FLAGS
|
|
|
164
165
|
-t, --token=<value> Superblocks user API key
|
|
165
166
|
|
|
166
167
|
DESCRIPTION
|
|
167
|
-
|
|
168
|
+
Authenticate with Superblocks cloud
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## `superblocks migrate`
|
|
172
|
+
|
|
173
|
+
Migrate files to use the current CLI version
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
USAGE
|
|
177
|
+
$ superblocks migrate
|
|
178
|
+
|
|
179
|
+
DESCRIPTION
|
|
180
|
+
Migrate files to use the current CLI version
|
|
181
|
+
|
|
182
|
+
EXAMPLES
|
|
183
|
+
$ superblocks migrate
|
|
168
184
|
```
|
|
169
185
|
|
|
170
186
|
## `superblocks pull [ONLY]`
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import React, { useCallback, useState } from "react";
|
|
2
|
+
import { useSuperblocksContext } from "@superblocksteam/custom-components";
|
|
3
|
+
import { type Props, type EventTriggers } from "./types";
|
|
2
4
|
import { Task, ErrorComponent, validateTasks } from "./validation";
|
|
3
|
-
import { Props } from "./types";
|
|
4
5
|
import "./main.scss";
|
|
5
6
|
|
|
6
|
-
export default function Component({
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
export default function Component({ tasks }: Props) {
|
|
8
|
+
const {
|
|
9
|
+
updateProperties,
|
|
10
|
+
events : {
|
|
11
|
+
onTaskAdded,
|
|
12
|
+
onTaskStatusChanged,
|
|
13
|
+
},
|
|
14
|
+
} = useSuperblocksContext<Props, EventTriggers>();
|
|
15
|
+
const { validatedTasks, hasError } = validateTasks(tasks ?? {});
|
|
13
16
|
const [value, setValue] = useState("");
|
|
14
17
|
|
|
15
18
|
const onTodoAdded = useCallback(() => {
|
|
16
19
|
const id = Math.random().toString(36).substring(2, 8);
|
|
17
|
-
|
|
20
|
+
updateProperties({
|
|
18
21
|
tasks: {
|
|
19
22
|
...validatedTasks,
|
|
20
23
|
[id]: {
|
|
@@ -24,11 +27,11 @@ export default function Component({
|
|
|
24
27
|
},
|
|
25
28
|
});
|
|
26
29
|
onTaskAdded();
|
|
27
|
-
}, [
|
|
30
|
+
}, [updateProperties, validatedTasks, value, onTaskAdded]);
|
|
28
31
|
|
|
29
32
|
const onTaskStatusChange = useCallback(
|
|
30
33
|
(id: string, status: boolean) => {
|
|
31
|
-
|
|
34
|
+
updateProperties({
|
|
32
35
|
tasks: {
|
|
33
36
|
...validatedTasks,
|
|
34
37
|
[id]: {
|
|
@@ -39,7 +42,7 @@ export default function Component({
|
|
|
39
42
|
});
|
|
40
43
|
onTaskStatusChanged();
|
|
41
44
|
},
|
|
42
|
-
[
|
|
45
|
+
[updateProperties, validatedTasks, tasks, onTaskStatusChanged]
|
|
43
46
|
);
|
|
44
47
|
|
|
45
48
|
return hasError ? (
|
|
@@ -1,6 +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 dataTypeToDefaultControlType;
|
|
4
5
|
private initializeComponentByWizard;
|
|
5
6
|
private initializeComponentByExample;
|
|
6
7
|
run(): Promise<void>;
|
|
@@ -27,11 +27,26 @@ const tsStringify = (obj) => {
|
|
|
27
27
|
return `{\n${stringifiedEntries.join(",\n")}\n},\n`;
|
|
28
28
|
};
|
|
29
29
|
class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCommand {
|
|
30
|
+
constructor() {
|
|
31
|
+
super(...arguments);
|
|
32
|
+
this.dataTypeToDefaultControlType = (propertyType) => {
|
|
33
|
+
switch (propertyType) {
|
|
34
|
+
case "any":
|
|
35
|
+
return "js-expr";
|
|
36
|
+
case "boolean":
|
|
37
|
+
return "switch";
|
|
38
|
+
default:
|
|
39
|
+
return "text";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
30
43
|
async initializeComponentByWizard(isFirstTimeCreate) {
|
|
44
|
+
this.log(`${(0, colorette_1.cyanBright)("ℹ")} Follow this wizard to configure your Custom Component. You can always edit the generated config.ts file directly to modify your properties or events.`);
|
|
45
|
+
this.log();
|
|
31
46
|
const displayName = (await (0, enquirer_1.prompt)({
|
|
32
47
|
type: "input",
|
|
33
48
|
name: "displayName",
|
|
34
|
-
message: "What is the display name of the component you want to create? (e.g.
|
|
49
|
+
message: "What is the display name of the component you want to create? (e.g. To-Do List)",
|
|
35
50
|
validate: (response) => !(0, lodash_1.isEmpty)(response.trim()),
|
|
36
51
|
})).displayName;
|
|
37
52
|
const name = (await (0, enquirer_1.prompt)({
|
|
@@ -39,112 +54,124 @@ class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCo
|
|
|
39
54
|
name: "name",
|
|
40
55
|
message: "What is the machine readable name of the component you want to create?",
|
|
41
56
|
validate: (response) => (0, identifiers_1.isValidIdentifier)(response) || "Invalid identifier",
|
|
42
|
-
initial: (0, identifiers_1.suggestIdentifier)(displayName, true) || "
|
|
57
|
+
initial: (0, identifiers_1.suggestIdentifier)(displayName, true) || "ToDoList",
|
|
43
58
|
})).name;
|
|
44
59
|
this.log();
|
|
45
|
-
this.log(`${(0, colorette_1.cyanBright)("ℹ")}
|
|
60
|
+
this.log(`${(0, colorette_1.cyanBright)("ℹ")} Properties represent the state of the component. You will define how each property is made available to the rest of your App and whether each property is displayed in the Properties Panel. Read more about properties here: ${(0, colorette_1.magenta)("https://docs.superblocks.com/applications/custom-components/#properties--events")}`);
|
|
46
61
|
this.log();
|
|
47
|
-
const
|
|
62
|
+
const hasProperties = (await (0, enquirer_1.prompt)({
|
|
48
63
|
type: "confirm",
|
|
49
|
-
name: "
|
|
50
|
-
message: `Does this component have ${(0, colorette_1.bold)("
|
|
64
|
+
name: "hasProperties",
|
|
65
|
+
message: `Does this component have ${(0, colorette_1.bold)("properties")}?`,
|
|
51
66
|
initial: false,
|
|
52
|
-
})).
|
|
53
|
-
const
|
|
54
|
-
if (
|
|
67
|
+
})).hasProperties;
|
|
68
|
+
const properties = [];
|
|
69
|
+
if (hasProperties) {
|
|
55
70
|
for (;;) {
|
|
56
|
-
const
|
|
57
|
-
type: "input",
|
|
58
|
-
name: "statefulPropName",
|
|
59
|
-
message: "What is the label of the stateful prop? (e.g. Selected Date)",
|
|
60
|
-
validate: (response) => !(0, lodash_1.isEmpty)(response.trim()),
|
|
61
|
-
})).statefulPropName.trim();
|
|
62
|
-
const statefulPropPath = (await (0, enquirer_1.prompt)({
|
|
71
|
+
const propertyPath = (await (0, enquirer_1.prompt)({
|
|
63
72
|
type: "input",
|
|
64
73
|
name: "path",
|
|
65
|
-
|
|
66
|
-
message: "What is the machine readable name of the stateful prop?",
|
|
74
|
+
message: "What is the path of the property? This will be used to access the property in your code (e.g. currentTasks)",
|
|
67
75
|
validate: (response) => {
|
|
68
76
|
if (!(0, identifiers_1.isValidIdentifier)(response))
|
|
69
77
|
return "Invalid identifier";
|
|
70
|
-
if (
|
|
78
|
+
if (properties.some((v) => v.path === response))
|
|
71
79
|
return "Duplicate property name";
|
|
72
80
|
return true;
|
|
73
81
|
},
|
|
74
82
|
})).path;
|
|
75
|
-
const
|
|
83
|
+
const propertyType = (await (0, enquirer_1.prompt)({
|
|
76
84
|
type: "select",
|
|
77
85
|
name: "type",
|
|
78
|
-
message: `What
|
|
79
|
-
choices: Object.entries(util_1.
|
|
86
|
+
message: `What is the type of ${propertyPath}?`,
|
|
87
|
+
choices: Object.entries(util_1.dataTypeDefinions).map(([k, v]) => ({
|
|
80
88
|
name: k,
|
|
81
89
|
message: v.prompt,
|
|
82
90
|
value: k,
|
|
83
91
|
})),
|
|
84
92
|
initial: 0,
|
|
85
93
|
})).type;
|
|
86
|
-
const
|
|
87
|
-
type: "input",
|
|
94
|
+
const showInPropsPanel = (await (0, enquirer_1.prompt)({
|
|
88
95
|
name: "value",
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
type: "confirm",
|
|
97
|
+
message: "Should this property be shown in the properties panel?",
|
|
98
|
+
initial: true,
|
|
91
99
|
})).value;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
100
|
+
let propertiesPanelDisplay = undefined;
|
|
101
|
+
if (showInPropsPanel) {
|
|
102
|
+
this.log();
|
|
103
|
+
this.log((0, colorette_1.bold)("Properties Panel Display Configuration"));
|
|
104
|
+
const propertyLabel = (await (0, enquirer_1.prompt)({
|
|
105
|
+
type: "input",
|
|
106
|
+
name: "label",
|
|
107
|
+
message: "What is the label of the property? (e.g. Tasks)",
|
|
108
|
+
validate: (response) => !(0, lodash_1.isEmpty)(response.trim()),
|
|
109
|
+
})).label.trim();
|
|
110
|
+
propertiesPanelDisplay = {
|
|
111
|
+
label: propertyLabel,
|
|
112
|
+
controlType: this.dataTypeToDefaultControlType(propertyType),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
properties.push({
|
|
116
|
+
path: propertyPath,
|
|
117
|
+
dataType: propertyType,
|
|
118
|
+
propertiesPanelDisplay,
|
|
119
|
+
// Optional but include them by default
|
|
120
|
+
isExternallyReadable: true,
|
|
121
|
+
isExternallySettable: true,
|
|
97
122
|
});
|
|
98
|
-
|
|
123
|
+
this.log(`You can configure the remaining display attributes (placeholder, etc) in the config.ts file directly.`);
|
|
124
|
+
this.log();
|
|
125
|
+
const addAnotherProperty = (await (0, enquirer_1.prompt)({
|
|
99
126
|
type: "confirm",
|
|
100
127
|
name: "value",
|
|
101
|
-
message: "Do you want to add another
|
|
128
|
+
message: "Do you want to add another property?",
|
|
102
129
|
initial: false,
|
|
103
130
|
})).value;
|
|
104
|
-
if (!
|
|
131
|
+
if (!addAnotherProperty) {
|
|
105
132
|
break;
|
|
106
133
|
}
|
|
107
134
|
}
|
|
108
135
|
}
|
|
109
136
|
this.log();
|
|
110
|
-
this.log(`${(0, colorette_1.cyanBright)("ℹ")} ${(0, colorette_1.bold)("
|
|
137
|
+
this.log(`${(0, colorette_1.cyanBright)("ℹ")} ${(0, colorette_1.bold)("Events")} represent the types of events that the component can trigger in Superblocks. Learn more about events: ${(0, colorette_1.magenta)("https://docs.superblocks.com/applications/custom-components/#properties--events")}`);
|
|
111
138
|
this.log();
|
|
112
139
|
const hasEventHandlers = (await (0, enquirer_1.prompt)({
|
|
113
140
|
type: "confirm",
|
|
114
141
|
name: "value",
|
|
115
|
-
message: `Does this component have ${(0, colorette_1.bold)("
|
|
142
|
+
message: `Does this component have ${(0, colorette_1.bold)("events")}? (e.g. a date picker component that triggers an On Change event)`,
|
|
116
143
|
initial: false,
|
|
117
144
|
})).value;
|
|
118
|
-
const
|
|
145
|
+
const events = [];
|
|
119
146
|
if (hasEventHandlers) {
|
|
120
147
|
for (;;) {
|
|
121
148
|
const eventHandlerName = (await (0, enquirer_1.prompt)({
|
|
122
149
|
name: "value",
|
|
123
150
|
type: "input",
|
|
124
|
-
message: "What is the label of the event
|
|
151
|
+
message: "What is the label of the event? (e.g. On Change)",
|
|
125
152
|
validate: (response) => !(0, lodash_1.isEmpty)(response.trim()),
|
|
126
153
|
})).value.trim();
|
|
127
154
|
const eventHandlerPath = (await (0, enquirer_1.prompt)({
|
|
128
155
|
name: "value",
|
|
129
|
-
message: "What is the
|
|
156
|
+
message: "What is the path of the event? This will be used to trigger the event in your code (e.g. onChange)",
|
|
130
157
|
type: "input",
|
|
131
158
|
initial: (0, identifiers_1.suggestIdentifier)(eventHandlerName) || "onChange",
|
|
132
159
|
validate: (response) => {
|
|
133
160
|
if (!(0, identifiers_1.isValidIdentifier)(response))
|
|
134
161
|
return "Invalid identifier";
|
|
135
|
-
if (
|
|
162
|
+
if (events.some((v) => v.path === response))
|
|
136
163
|
return "Duplicate property name";
|
|
137
164
|
return true;
|
|
138
165
|
},
|
|
139
166
|
})).value;
|
|
140
|
-
|
|
167
|
+
events.push({
|
|
141
168
|
label: eventHandlerName,
|
|
142
169
|
path: eventHandlerPath,
|
|
143
170
|
});
|
|
144
171
|
const addAnotherEventHandler = (await (0, enquirer_1.prompt)({
|
|
145
172
|
name: "value",
|
|
146
173
|
type: "confirm",
|
|
147
|
-
message: "Do you want to add another event
|
|
174
|
+
message: "Do you want to add another event?",
|
|
148
175
|
initial: false,
|
|
149
176
|
})).value;
|
|
150
177
|
if (!addAnotherEventHandler) {
|
|
@@ -156,16 +183,19 @@ class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCo
|
|
|
156
183
|
id: (0, node_crypto_1.randomUUID)(),
|
|
157
184
|
name,
|
|
158
185
|
displayName,
|
|
159
|
-
|
|
160
|
-
.map((
|
|
186
|
+
propertiesRendered: properties
|
|
187
|
+
.map((property) => tsStringify(property))
|
|
161
188
|
.join(""),
|
|
162
|
-
eventHandlersRendered:
|
|
189
|
+
eventHandlersRendered: events
|
|
163
190
|
.map((eventHandler) => tsStringify(eventHandler))
|
|
164
191
|
.join(""),
|
|
165
192
|
});
|
|
166
|
-
const componentTsx = (0, create_component_defaults_1.getDefaultComponentTsx)(
|
|
193
|
+
const componentTsx = (0, create_component_defaults_1.getDefaultComponentTsx)(properties, events.map((prop) => prop.path));
|
|
167
194
|
if (isFirstTimeCreate) {
|
|
168
195
|
await fs.copy(DEFAULT_PACKAGE_JSON_TEMPLATE_PATH, ".");
|
|
196
|
+
// There is a very old npm bug where it won't publish .gitignore files
|
|
197
|
+
// https://github.com/npm/npm/issues/3763
|
|
198
|
+
await fs.move("./gitignore_will_rename", "./.gitignore");
|
|
169
199
|
}
|
|
170
200
|
await fs.ensureDir("components/" + name);
|
|
171
201
|
await fs.writeFile(`components/${name}/config.ts`, configTs);
|
|
@@ -173,23 +203,28 @@ class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCo
|
|
|
173
203
|
return {
|
|
174
204
|
name,
|
|
175
205
|
displayName,
|
|
176
|
-
|
|
177
|
-
|
|
206
|
+
properties,
|
|
207
|
+
events,
|
|
178
208
|
};
|
|
179
209
|
}
|
|
180
210
|
async initializeComponentByExample() {
|
|
181
211
|
const response = {
|
|
182
|
-
displayName: "Example
|
|
183
|
-
name: "
|
|
184
|
-
|
|
212
|
+
displayName: "To-Do List (Example)",
|
|
213
|
+
name: "ToDoList",
|
|
214
|
+
properties: [
|
|
185
215
|
{
|
|
186
|
-
label: "Default Tasks",
|
|
187
216
|
path: "tasks",
|
|
188
|
-
|
|
189
|
-
|
|
217
|
+
dataType: "any",
|
|
218
|
+
propertiesPanelDisplay: {
|
|
219
|
+
label: "Default Tasks",
|
|
220
|
+
placeholder: "{ taskId: { taskName: 'Task Name', taskStatus: 'complete' | 'todo' } }",
|
|
221
|
+
controlType: "js-expr",
|
|
222
|
+
},
|
|
223
|
+
isExternallyReadable: true,
|
|
224
|
+
isExternallySettable: true,
|
|
190
225
|
},
|
|
191
226
|
],
|
|
192
|
-
|
|
227
|
+
events: [
|
|
193
228
|
{
|
|
194
229
|
label: "On Task Added",
|
|
195
230
|
path: "onTaskAdded",
|
|
@@ -204,10 +239,10 @@ class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCo
|
|
|
204
239
|
id: (0, node_crypto_1.randomUUID)(),
|
|
205
240
|
name: response.name,
|
|
206
241
|
displayName: response.displayName,
|
|
207
|
-
|
|
208
|
-
.map((
|
|
242
|
+
propertiesRendered: response.properties
|
|
243
|
+
.map((property) => tsStringify(property))
|
|
209
244
|
.join(""),
|
|
210
|
-
eventHandlersRendered: response.
|
|
245
|
+
eventHandlersRendered: response.events
|
|
211
246
|
.map((eventHandler) => tsStringify(eventHandler))
|
|
212
247
|
.join(""),
|
|
213
248
|
});
|
|
@@ -218,31 +253,38 @@ class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCo
|
|
|
218
253
|
return response;
|
|
219
254
|
}
|
|
220
255
|
async run() {
|
|
256
|
+
this.log();
|
|
221
257
|
const isFirstTimeCreate = !(await fs.exists("package.json"));
|
|
222
|
-
|
|
223
|
-
|
|
258
|
+
let response;
|
|
259
|
+
if (isFirstTimeCreate) {
|
|
260
|
+
this.log(`${(0, colorette_1.cyanBright)("ℹ")} Welcome to Custom Components! In order to help you get started, we’ve built an example To-Do List Component. You can use this component as a template as you build your own Custom Component. To work with this component, we recommend following along with our quickstart guide: ${(0, colorette_1.magenta)("https://docs.superblocks.com/applications/custom-components/quickstart")}`);
|
|
261
|
+
this.log();
|
|
262
|
+
this.log(`If you choose to generate the example component, you will not have to provide any additional inputs. You can re-run superblocks components create once you are ready to create your own component from scratch. `);
|
|
263
|
+
this.log();
|
|
264
|
+
const initExampleComponent = (await (0, enquirer_1.prompt)({
|
|
224
265
|
name: "value",
|
|
225
266
|
type: "confirm",
|
|
226
|
-
message: "Would you like to
|
|
267
|
+
message: "Would you like to generate an example custom component?",
|
|
227
268
|
initial: false,
|
|
228
269
|
})).value;
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
270
|
+
response = initExampleComponent
|
|
271
|
+
? await this.initializeComponentByExample()
|
|
272
|
+
: await this.initializeComponentByWizard(isFirstTimeCreate);
|
|
232
273
|
}
|
|
233
274
|
else {
|
|
234
|
-
response = await this.
|
|
275
|
+
response = await this.initializeComponentByWizard(isFirstTimeCreate);
|
|
235
276
|
}
|
|
236
277
|
this.log((0, colorette_1.green)("Successfully created component! Added the following files:"));
|
|
237
278
|
this.log();
|
|
238
279
|
const tree = core_1.ux.tree();
|
|
239
280
|
tree.insert("components");
|
|
240
281
|
tree.nodes.components.insert(response.name);
|
|
241
|
-
tree.nodes.components.nodes[response.name].insert("config.ts # update this file to
|
|
282
|
+
tree.nodes.components.nodes[response.name].insert("config.ts # update this file to manage your component's properties and events");
|
|
283
|
+
tree.nodes.components.nodes[response.name].insert("types.ts # this file contains your react component type definitions and is automatically updated while you run 'superblocks components watch'");
|
|
242
284
|
tree.nodes.components.nodes[response.name].insert("component.tsx # your component's react code");
|
|
243
285
|
tree.display();
|
|
244
286
|
this.log();
|
|
245
|
-
this.log(`${(0, colorette_1.green)("Remember to run $ ")}${(0, colorette_1.
|
|
287
|
+
this.log(`${(0, colorette_1.green)("Remember to run $ ")}${(0, colorette_1.magenta)("superblocks components watch")}${(0, colorette_1.green)(" to watch for changes to your component files")}`);
|
|
246
288
|
this.log();
|
|
247
289
|
this.log(`Edit your component's react code here:
|
|
248
290
|
${(0, colorette_1.green)(`components/${response.name}/component.tsx`)}`);
|
|
@@ -258,7 +300,10 @@ class CreateComponent extends authenticated_command_1.AuthenticatedApplicationCo
|
|
|
258
300
|
}
|
|
259
301
|
core_1.ux.action.stop();
|
|
260
302
|
}
|
|
261
|
-
|
|
303
|
+
const headers = {
|
|
304
|
+
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.CREATE,
|
|
305
|
+
};
|
|
306
|
+
this.registerComponents(headers);
|
|
262
307
|
}
|
|
263
308
|
}
|
|
264
309
|
CreateComponent.description = "Creates a new Superblocks component in the current application folder";
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const util_1 = require("@superblocksteam/util");
|
|
3
4
|
const authenticated_command_1 = require("../../common/authenticated-command");
|
|
4
5
|
class Register extends authenticated_command_1.AuthenticatedApplicationCommand {
|
|
5
6
|
async run() {
|
|
6
|
-
|
|
7
|
+
const headers = {
|
|
8
|
+
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.REGISTER,
|
|
9
|
+
};
|
|
10
|
+
await this.registerComponents(headers);
|
|
7
11
|
}
|
|
8
12
|
}
|
|
9
13
|
Register.description = "Registers all local component config files";
|
|
@@ -20,7 +20,10 @@ class Upload extends authenticated_command_1.AuthenticatedApplicationCommand {
|
|
|
20
20
|
exit: 1,
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
|
-
const
|
|
23
|
+
const headers = {
|
|
24
|
+
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.REGISTER,
|
|
25
|
+
};
|
|
26
|
+
const configs = await this.registerComponents(headers);
|
|
24
27
|
if (!configs)
|
|
25
28
|
return;
|
|
26
29
|
// Map component name to component path- each name is a separate entry point
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.healthEndpointMiddleware = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const react_shim_1 = require("@superblocksteam/react-shim");
|
|
6
|
+
const util_1 = require("@superblocksteam/util");
|
|
6
7
|
const vite_custom_component_reload_plugin_1 = require("@superblocksteam/vite-custom-component-reload-plugin");
|
|
7
8
|
const plugin_react_1 = tslib_1.__importDefault(require("@vitejs/plugin-react"));
|
|
8
9
|
const colorette_1 = require("colorette");
|
|
@@ -29,10 +30,12 @@ function healthEndpointMiddleware() {
|
|
|
29
30
|
exports.healthEndpointMiddleware = healthEndpointMiddleware;
|
|
30
31
|
class Watch extends authenticated_command_1.AuthenticatedApplicationCommand {
|
|
31
32
|
async run() {
|
|
32
|
-
|
|
33
|
+
const headers = {
|
|
34
|
+
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.REGISTER,
|
|
35
|
+
};
|
|
36
|
+
await this.registerComponents(headers);
|
|
33
37
|
this.log((0, colorette_1.yellow)("Remember to refresh your application to see any newly registered components."));
|
|
34
38
|
this.log();
|
|
35
|
-
this.log((0, colorette_1.green)(`Visit ${await this.getBaseUrl()} and access your application in edit mode to see your components! 🐨`));
|
|
36
39
|
const port = 3002;
|
|
37
40
|
const editModeUrl = await this.getEditModeUrl();
|
|
38
41
|
const viteLogger = (0, vite_1.createLogger)();
|
|
@@ -71,7 +74,7 @@ class Watch extends authenticated_command_1.AuthenticatedApplicationCommand {
|
|
|
71
74
|
this.log();
|
|
72
75
|
this.log((0, colorette_1.bold)((0, colorette_1.magenta)(`${editModeUrl}?devMode=true`)));
|
|
73
76
|
this.log();
|
|
74
|
-
this.log((0, colorette_1.yellow)(
|
|
77
|
+
this.log((0, colorette_1.yellow)(`Please ensure that Local Dev Mode is enabled in your Application so that the component is fetched from your local dev server. Learn more about Local Dev Mode here: ${(0, colorette_1.magenta)("https://docs.superblocks.com/applications/custom-components/development-lifecycle#local-development-mode")}`));
|
|
75
78
|
})();
|
|
76
79
|
}
|
|
77
80
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -62,7 +62,13 @@ class Initialize extends authenticated_command_1.AuthenticatedCommand {
|
|
|
62
62
|
task: async (ctx, task) => {
|
|
63
63
|
const [resourceId, resourceType] = getResourceIdFromUrl(args.resourceUrl);
|
|
64
64
|
if (resourceType === "APPLICATION") {
|
|
65
|
-
const
|
|
65
|
+
const headers = {
|
|
66
|
+
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.INIT,
|
|
67
|
+
};
|
|
68
|
+
const application = await this.getSdk().fetchApplication({
|
|
69
|
+
applicationId: resourceId,
|
|
70
|
+
headers,
|
|
71
|
+
});
|
|
66
72
|
ctx.fetchedResources[application.application.id] = {
|
|
67
73
|
resourceType,
|
|
68
74
|
name: application.application.name,
|
|
@@ -104,7 +110,14 @@ class Initialize extends authenticated_command_1.AuthenticatedCommand {
|
|
|
104
110
|
subtasks.push({
|
|
105
111
|
title: `Fetching application ${resource.name}...`,
|
|
106
112
|
task: async (_ctx, task) => {
|
|
107
|
-
const
|
|
113
|
+
const headers = {
|
|
114
|
+
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.INIT,
|
|
115
|
+
};
|
|
116
|
+
const application = await this.getSdk().fetchApplication({
|
|
117
|
+
applicationId: resourceId,
|
|
118
|
+
viewMode: ctx.viewMode,
|
|
119
|
+
headers,
|
|
120
|
+
});
|
|
108
121
|
task.title += `: fetched`;
|
|
109
122
|
ctx.writtenResources[resourceId] =
|
|
110
123
|
await (0, version_control_1.writeResourceToDisk)("APPLICATION", resourceId, application, now, process.cwd());
|
|
@@ -265,7 +278,7 @@ function getResourceIdFromUrl(resourceUrl) {
|
|
|
265
278
|
return [url.pathname.split("/")[2], "APPLICATION"];
|
|
266
279
|
}
|
|
267
280
|
else if (url.pathname.startsWith("/workflows") ||
|
|
268
|
-
url.pathname.startsWith("/
|
|
281
|
+
url.pathname.startsWith("/scheduled_jobs")) {
|
|
269
282
|
return [url.pathname.split("/")[2], "BACKEND"];
|
|
270
283
|
}
|
|
271
284
|
throw new Error(`Failed to parse resource URL: ${resourceUrl}`);
|
package/dist/commands/login.js
CHANGED
|
@@ -49,7 +49,7 @@ class Login extends core_1.Command {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
Login.description = "
|
|
52
|
+
Login.description = "Authenticate with Superblocks cloud";
|
|
53
53
|
Login.flags = {
|
|
54
54
|
// flag with a value (-t, --token=VALUE)
|
|
55
55
|
token: core_1.Flags.string({
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AuthenticatedCommand } from "../common/authenticated-command";
|
|
2
|
+
export default class Migrate extends AuthenticatedCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
run(): Promise<void>;
|
|
6
|
+
private createTasks;
|
|
7
|
+
private getResourceIdsToMigrate;
|
|
8
|
+
private migrateApplication;
|
|
9
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const node_child_process_1 = require("node:child_process");
|
|
5
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
6
|
+
const node_util_1 = tslib_1.__importDefault(require("node:util"));
|
|
7
|
+
const util_1 = require("@superblocksteam/util");
|
|
8
|
+
const colorette_1 = require("colorette");
|
|
9
|
+
const fs = tslib_1.__importStar(require("fs-extra"));
|
|
10
|
+
const listr2_1 = require("listr2");
|
|
11
|
+
const semver_1 = tslib_1.__importDefault(require("semver"));
|
|
12
|
+
const authenticated_command_1 = require("../common/authenticated-command");
|
|
13
|
+
const version_control_1 = require("../common/version-control");
|
|
14
|
+
class Migrate extends authenticated_command_1.AuthenticatedCommand {
|
|
15
|
+
async run() {
|
|
16
|
+
const tasks = this.createTasks();
|
|
17
|
+
await tasks.run();
|
|
18
|
+
}
|
|
19
|
+
createTasks() {
|
|
20
|
+
const tasks = new listr2_1.Listr([
|
|
21
|
+
{
|
|
22
|
+
title: "Checking for existing Superblocks project...",
|
|
23
|
+
task: async (ctx) => {
|
|
24
|
+
try {
|
|
25
|
+
[
|
|
26
|
+
ctx.existingSuperblocksRootConfig,
|
|
27
|
+
ctx.superblocksRootConfigPath,
|
|
28
|
+
] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
|
|
29
|
+
ctx.existingSuperblocksResourceConfig =
|
|
30
|
+
await (0, util_1.getSuperblocksResourceConfigIfExists)();
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// no existing superblocks config
|
|
34
|
+
this.error(`No Superblocks project found in the current folder hierarchy. Run ${(0, colorette_1.cyan)("superblocks init")} to initialize a new project.`);
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
title: "Migrating resources...",
|
|
40
|
+
task: async (ctx, task) => {
|
|
41
|
+
var _a;
|
|
42
|
+
const resourceIdsToMigrate = await this.getResourceIdsToMigrate(ctx, task);
|
|
43
|
+
const subtasks = [];
|
|
44
|
+
const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
|
|
45
|
+
for (const resourceId of resourceIdsToMigrate) {
|
|
46
|
+
const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
|
|
47
|
+
switch (resource === null || resource === void 0 ? void 0 : resource.resourceType) {
|
|
48
|
+
case "APPLICATION": {
|
|
49
|
+
subtasks.push({
|
|
50
|
+
title: `Migrating application ${resource.location}...`,
|
|
51
|
+
task: async (_ctx, task) => {
|
|
52
|
+
await this.migrateApplication(resource, superblocksRootPath);
|
|
53
|
+
task.title += `: done`;
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "BACKEND": {
|
|
59
|
+
subtasks.push({
|
|
60
|
+
title: `Migrating backend ${resource.location}...`,
|
|
61
|
+
task: async (_ctx, task) => {
|
|
62
|
+
task.title += `: done`;
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
default: {
|
|
68
|
+
this.error(`Unsupported resource type, resource: ${JSON.stringify(resource)}
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return task.newListr(subtasks, {
|
|
74
|
+
concurrent: true,
|
|
75
|
+
});
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
], {
|
|
79
|
+
concurrent: false,
|
|
80
|
+
});
|
|
81
|
+
return tasks;
|
|
82
|
+
}
|
|
83
|
+
async getResourceIdsToMigrate(ctx, task) {
|
|
84
|
+
var _a, _b, _c, _d, _e;
|
|
85
|
+
const choices = [];
|
|
86
|
+
const initialSelections = [];
|
|
87
|
+
choices.push({
|
|
88
|
+
name: "All Resources",
|
|
89
|
+
message: "",
|
|
90
|
+
});
|
|
91
|
+
let counter = 1;
|
|
92
|
+
for (const [resourceId, resource] of Object.entries((_b = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources) !== null && _b !== void 0 ? _b : {})) {
|
|
93
|
+
choices.push({
|
|
94
|
+
name: resourceId,
|
|
95
|
+
message: resource.location,
|
|
96
|
+
});
|
|
97
|
+
if (((_c = ctx.existingSuperblocksResourceConfig) === null || _c === void 0 ? void 0 : _c.id) === resourceId) {
|
|
98
|
+
initialSelections.push(counter);
|
|
99
|
+
}
|
|
100
|
+
counter++;
|
|
101
|
+
}
|
|
102
|
+
const resourceIdsToMigrate = choices.length === 1
|
|
103
|
+
? [choices[0].name]
|
|
104
|
+
: await task.prompt([
|
|
105
|
+
{
|
|
106
|
+
type: "MultiSelect",
|
|
107
|
+
name: "resourceIdsToMigrate",
|
|
108
|
+
message: `Select resources to migrate (${version_control_1.MULTI_SELECT_PROMPT_HELP})`,
|
|
109
|
+
choices: choices,
|
|
110
|
+
initial: initialSelections,
|
|
111
|
+
validate: version_control_1.atLeastOneSelection,
|
|
112
|
+
// @ts-expect-error listr2 types are wrong for prefix
|
|
113
|
+
prefix: "▸",
|
|
114
|
+
indicator: "◉",
|
|
115
|
+
},
|
|
116
|
+
]);
|
|
117
|
+
if (resourceIdsToMigrate[0] === "All Resources") {
|
|
118
|
+
return Object.entries((_e = (_d = ctx.existingSuperblocksRootConfig) === null || _d === void 0 ? void 0 : _d.resources) !== null && _e !== void 0 ? _e : {}).map(([id]) => id);
|
|
119
|
+
}
|
|
120
|
+
return resourceIdsToMigrate;
|
|
121
|
+
}
|
|
122
|
+
async migrateApplication(applicationResource, superblocksRootPath) {
|
|
123
|
+
var _a;
|
|
124
|
+
this.log("Checking CLI version compatibility...");
|
|
125
|
+
try {
|
|
126
|
+
const packageJson = await fs.readJson(node_path_1.default.join(superblocksRootPath, applicationResource.location, "package.json"));
|
|
127
|
+
const versionStr = (_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a["@superblocksteam/custom-components"];
|
|
128
|
+
if (!semver_1.default.satisfies(this.config.version, versionStr)) {
|
|
129
|
+
this.log("Migrating application dependencies...");
|
|
130
|
+
await node_util_1.default.promisify(node_child_process_1.exec)(`npm install @superblocksteam/custom-components@${this.config.version}`, {
|
|
131
|
+
cwd: node_path_1.default.join(superblocksRootPath, applicationResource.location),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.log("CLI version matches package.json version");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
this.error(e.message, {
|
|
140
|
+
exit: 1,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
Migrate.description = "Migrate files to use the current CLI version";
|
|
146
|
+
Migrate.examples = ["<%= config.bin %> <%= command.id %>"];
|
|
147
|
+
exports.default = Migrate;
|
package/dist/commands/pull.js
CHANGED
|
@@ -51,7 +51,14 @@ class Pull extends authenticated_command_1.AuthenticatedCommand {
|
|
|
51
51
|
subtasks.push({
|
|
52
52
|
title: `Pulling application ${resource.location}...`,
|
|
53
53
|
task: async (_ctx, task) => {
|
|
54
|
-
const
|
|
54
|
+
const headers = {
|
|
55
|
+
[util_1.COMPONENT_EVENT_HEADER]: util_1.ComponentEvent.PULL,
|
|
56
|
+
};
|
|
57
|
+
const application = await this.getSdk().fetchApplication({
|
|
58
|
+
applicationId: resourceId,
|
|
59
|
+
viewMode,
|
|
60
|
+
headers,
|
|
61
|
+
});
|
|
55
62
|
task.title += `: fetched`;
|
|
56
63
|
ctx.writtenResources[resourceId] =
|
|
57
64
|
await (0, version_control_1.writeResourceToDisk)("APPLICATION", resourceId, application, now, superblocksRootPath, resource.location);
|
|
@@ -6,10 +6,11 @@ export declare abstract class AuthenticatedCommand extends Command {
|
|
|
6
6
|
protected init(): Promise<void>;
|
|
7
7
|
protected getBaseUrl(): Promise<string>;
|
|
8
8
|
protected getSdk(): SuperblocksSdk;
|
|
9
|
+
protected runAutomatedDotfileUpdates(): Promise<void>;
|
|
9
10
|
}
|
|
10
11
|
export declare abstract class AuthenticatedApplicationCommand extends AuthenticatedCommand {
|
|
11
12
|
applicationConfig: SuperblocksApplicationConfig;
|
|
12
13
|
protected getEditModeUrl(): Promise<string>;
|
|
13
14
|
protected init(): Promise<void>;
|
|
14
|
-
protected registerComponents(): Promise<Record<string, any> | undefined>;
|
|
15
|
+
protected registerComponents(injectedHeaders?: Record<string, string>): Promise<Record<string, any> | undefined>;
|
|
15
16
|
}
|
|
@@ -2,10 +2,17 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.AuthenticatedApplicationCommand = exports.AuthenticatedCommand = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const node_path_1 = tslib_1.__importDefault(require("node:path"));
|
|
5
6
|
const core_1 = require("@oclif/core");
|
|
6
7
|
const sdk_1 = require("@superblocksteam/sdk");
|
|
7
8
|
const util_1 = require("@superblocksteam/util");
|
|
9
|
+
const colorette_1 = require("colorette");
|
|
8
10
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
11
|
+
const semver_1 = tslib_1.__importDefault(require("semver"));
|
|
12
|
+
const yaml_1 = require("yaml");
|
|
13
|
+
const migrationWarningsForApplications_1 = require("../util/migrationWarningsForApplications");
|
|
14
|
+
const migrationsForDotfiles_1 = require("../util/migrationsForDotfiles");
|
|
15
|
+
const version_control_1 = require("./version-control");
|
|
9
16
|
class AuthenticatedCommand extends core_1.Command {
|
|
10
17
|
async init() {
|
|
11
18
|
await super.init();
|
|
@@ -16,7 +23,7 @@ class AuthenticatedCommand extends core_1.Command {
|
|
|
16
23
|
}
|
|
17
24
|
const { token, superblocksBaseUrl } = result;
|
|
18
25
|
this.sdk = new sdk_1.SuperblocksSdk(token, superblocksBaseUrl);
|
|
19
|
-
await
|
|
26
|
+
await this.runAutomatedDotfileUpdates();
|
|
20
27
|
}
|
|
21
28
|
catch (e) {
|
|
22
29
|
console.log(e.message);
|
|
@@ -34,6 +41,62 @@ class AuthenticatedCommand extends core_1.Command {
|
|
|
34
41
|
}
|
|
35
42
|
return this.sdk;
|
|
36
43
|
}
|
|
44
|
+
async runAutomatedDotfileUpdates() {
|
|
45
|
+
let previousConfig;
|
|
46
|
+
let rootConfigPath;
|
|
47
|
+
try {
|
|
48
|
+
[previousConfig, rootConfigPath] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// This might fail for a brand-new project folder, which is deliberately ignored
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (previousConfig &&
|
|
55
|
+
rootConfigPath &&
|
|
56
|
+
(0, migrationsForDotfiles_1.isDotfileMigrationNeeded)(previousConfig.metadata.cliVersion, this.config.version)) {
|
|
57
|
+
this.log(`Migrating Superblocks settings from ${previousConfig.metadata.cliVersion} to ${this.config.version}...`);
|
|
58
|
+
try {
|
|
59
|
+
const newConfig = await (0, migrationsForDotfiles_1.migrateSuperblocksMonorepoConfig)(previousConfig, {
|
|
60
|
+
previousVersion: previousConfig.metadata.cliVersion,
|
|
61
|
+
currentVersion: this.config.version,
|
|
62
|
+
});
|
|
63
|
+
await fs_extra_1.default.writeFile(rootConfigPath, JSON.stringify((0, version_control_1.sortByKey)(newConfig), null, 2));
|
|
64
|
+
await Promise.all(Object.entries(newConfig.resources).map(async ([, resource]) => {
|
|
65
|
+
this.log(`Migrating ${resource.location}`);
|
|
66
|
+
try {
|
|
67
|
+
const resourcePath = node_path_1.default.join(rootConfigPath, "../..", resource.location, ".superblocks/superblocks.json");
|
|
68
|
+
const resourceConfig = await fs_extra_1.default.readJSON(resourcePath);
|
|
69
|
+
const newConfig = await (0, migrationsForDotfiles_1.migrateSuperblocksEntityConfig)(resourceConfig, {
|
|
70
|
+
previousVersion: previousConfig
|
|
71
|
+
.metadata.cliVersion,
|
|
72
|
+
currentVersion: this.config.version,
|
|
73
|
+
});
|
|
74
|
+
await fs_extra_1.default.writeFile(resourcePath, JSON.stringify((0, version_control_1.sortByKey)(newConfig), null, 2));
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
this.error(e);
|
|
78
|
+
}
|
|
79
|
+
}));
|
|
80
|
+
this.log(`Finalizing migration`);
|
|
81
|
+
this.log(`After upgrading the CLI version, you may need to perform additional migrations using
|
|
82
|
+
${(0, colorette_1.cyan)("superblocks migrate")}`);
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
this.error(e);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (previousConfig.metadata.cliVersion) {
|
|
89
|
+
const warningMessage = (0, migrationWarningsForApplications_1.getWarningsForApplicationMigration)(previousConfig.metadata.cliVersion, this.config.version);
|
|
90
|
+
if (warningMessage) {
|
|
91
|
+
this.log(warningMessage);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Always upgrade the root config even if there's no migration
|
|
95
|
+
if (rootConfigPath) {
|
|
96
|
+
previousConfig.metadata.cliVersion = this.config.version;
|
|
97
|
+
await fs_extra_1.default.writeFile(rootConfigPath, JSON.stringify((0, version_control_1.sortByKey)(previousConfig), null, 2));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
37
100
|
}
|
|
38
101
|
exports.AuthenticatedCommand = AuthenticatedCommand;
|
|
39
102
|
class AuthenticatedApplicationCommand extends AuthenticatedCommand {
|
|
@@ -52,6 +115,7 @@ class AuthenticatedApplicationCommand extends AuthenticatedCommand {
|
|
|
52
115
|
return new URL(`/applications/${this.applicationConfig.id}/pages/${this.applicationConfig.defaultPageId}/edit`, baseUrl).toString();
|
|
53
116
|
}
|
|
54
117
|
async init() {
|
|
118
|
+
var _a;
|
|
55
119
|
await super.init();
|
|
56
120
|
try {
|
|
57
121
|
this.applicationConfig = await (0, util_1.getSuperblocksApplicationConfigJson)();
|
|
@@ -61,8 +125,44 @@ class AuthenticatedApplicationCommand extends AuthenticatedCommand {
|
|
|
61
125
|
exit: 1,
|
|
62
126
|
});
|
|
63
127
|
}
|
|
128
|
+
try {
|
|
129
|
+
const appYaml = await fs_extra_1.default.readFile("application.yaml", "utf-8");
|
|
130
|
+
const appJson = await (0, yaml_1.parse)(appYaml);
|
|
131
|
+
if ((_a = appJson === null || appJson === void 0 ? void 0 : appJson.settings) === null || _a === void 0 ? void 0 : _a.cliVersion) {
|
|
132
|
+
const warningMessage = (0, migrationWarningsForApplications_1.getWarningsForApplicationMigration)(appJson.settings.cliVersion, this.config.version);
|
|
133
|
+
if (warningMessage) {
|
|
134
|
+
this.log(warningMessage);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
this.warn(`Could not read page.yaml to determine version compatibility: ${e.message}`);
|
|
140
|
+
}
|
|
64
141
|
}
|
|
65
|
-
async registerComponents() {
|
|
142
|
+
async registerComponents(injectedHeaders) {
|
|
143
|
+
var _a;
|
|
144
|
+
core_1.ux.action.start("Checking CLI version compatibility...");
|
|
145
|
+
try {
|
|
146
|
+
const packageJson = await fs_extra_1.default.readJson("package.json");
|
|
147
|
+
const versionStr = (_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a["@superblocksteam/custom-components"];
|
|
148
|
+
if (!versionStr) {
|
|
149
|
+
throw new Error(`You must install the @superblocksteam/custom-components library as a dependency in your package.json file. Run:
|
|
150
|
+
|
|
151
|
+
${(0, colorette_1.cyan)("superblocks migrate")}`);
|
|
152
|
+
}
|
|
153
|
+
if (!semver_1.default.satisfies(this.config.version, versionStr)) {
|
|
154
|
+
throw new Error(`You must upgrade the @superblocksteam/custom-components library. You are using ${versionStr} but the CLI is ${this.config.version}. Run:
|
|
155
|
+
|
|
156
|
+
${(0, colorette_1.cyan)("superblocks migrate")}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
core_1.ux.action.stop();
|
|
161
|
+
this.error(e.message, {
|
|
162
|
+
exit: 1,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
core_1.ux.action.stop();
|
|
66
166
|
core_1.ux.action.start("Scanning for Superblocks components...");
|
|
67
167
|
const exists = await fs_extra_1.default.pathExists(util_1.CUSTOM_COMPONENTS_PATH);
|
|
68
168
|
if (!exists) {
|
|
@@ -85,7 +185,7 @@ class AuthenticatedApplicationCommand extends AuthenticatedCommand {
|
|
|
85
185
|
core_1.ux.action.stop();
|
|
86
186
|
try {
|
|
87
187
|
core_1.ux.action.start("Registering components...");
|
|
88
|
-
await this.getSdk().registerComponents(this.applicationConfig.id, configs);
|
|
188
|
+
await this.getSdk().registerComponents(this.applicationConfig.id, configs, injectedHeaders);
|
|
89
189
|
core_1.ux.action.stop();
|
|
90
190
|
}
|
|
91
191
|
catch (e) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
export declare const getDefaultConfigTs: ({ id, name, displayName,
|
|
1
|
+
import { type Property } from "@superblocksteam/util";
|
|
2
|
+
export declare const getDefaultConfigTs: ({ id, name, displayName, propertiesRendered, eventHandlersRendered, }: {
|
|
3
3
|
id: string;
|
|
4
4
|
name: string;
|
|
5
5
|
displayName: string;
|
|
6
|
-
|
|
6
|
+
propertiesRendered: string;
|
|
7
7
|
eventHandlersRendered: string;
|
|
8
8
|
}) => string;
|
|
9
|
-
export declare const getDefaultComponentTsx: (
|
|
9
|
+
export declare const getDefaultComponentTsx: (properties: Property[], eventHandlers: string[]) => string;
|
|
@@ -8,7 +8,7 @@ const indent = (str, spaces) => {
|
|
|
8
8
|
.map((line) => `${spacesString}${line}`)
|
|
9
9
|
.join("\n");
|
|
10
10
|
};
|
|
11
|
-
const getDefaultConfigTs = ({ id, name, displayName,
|
|
11
|
+
const getDefaultConfigTs = ({ id, name, displayName, propertiesRendered, eventHandlersRendered, }) => `import { type ComponentConfig } from "@superblocksteam/custom-components";
|
|
12
12
|
|
|
13
13
|
export default {
|
|
14
14
|
// DO NOT CHANGE THE ID ONCE THE COMPONENT HAS BEEN REGISTERED!
|
|
@@ -16,20 +16,20 @@ export default {
|
|
|
16
16
|
name: "${name}",
|
|
17
17
|
displayName: "${displayName}",
|
|
18
18
|
componentPath: "components/${name}/component.tsx",
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
properties: [${indent(propertiesRendered, 4).trim()}],
|
|
20
|
+
events: [${indent(eventHandlersRendered, 4).trim()}],
|
|
21
21
|
} satisfies ComponentConfig;
|
|
22
22
|
`;
|
|
23
23
|
exports.getDefaultConfigTs = getDefaultConfigTs;
|
|
24
|
-
const getDefaultComponentTsx = (
|
|
24
|
+
const getDefaultComponentTsx = (properties, eventHandlers) => `import React from "react";
|
|
25
25
|
import { useSuperblocksContext } from "@superblocksteam/custom-components";
|
|
26
26
|
import { type Props, type EventTriggers } from "./types";
|
|
27
27
|
|
|
28
|
-
export default function Component({${
|
|
28
|
+
export default function Component({${properties.length === 0
|
|
29
29
|
? ""
|
|
30
|
-
: "\n" +
|
|
30
|
+
: "\n" + properties.map((v) => indent(v.path, 2) + ",\n").join("")}}: Props) {
|
|
31
31
|
const {
|
|
32
|
-
|
|
32
|
+
updateProperties,
|
|
33
33
|
events: {${"\n" + eventHandlers.map((v) => indent(v, 6) + ",\n").join("")} },
|
|
34
34
|
} = useSuperblocksContext<Props, EventTriggers>();
|
|
35
35
|
|
|
@@ -88,7 +88,7 @@ async function downloadFile(rootDirectory, filepath, url) {
|
|
|
88
88
|
return Promise.resolve("");
|
|
89
89
|
}
|
|
90
90
|
async function writeResourceToDisk(resourceType, resourceId, resource, now, rootPath, existingRelativeLocation) {
|
|
91
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
91
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
92
92
|
switch (resourceType) {
|
|
93
93
|
case "APPLICATION": {
|
|
94
94
|
const parentDirName = "apps";
|
|
@@ -135,7 +135,9 @@ async function writeResourceToDisk(resourceType, resourceId, resource, now, root
|
|
|
135
135
|
}
|
|
136
136
|
await fs.ensureDir(`${appDirName}/${util_1.SUPERBLOCKS_HOME_FOLDER_NAME}`);
|
|
137
137
|
await fs.writeFile(`${appDirName}/${util_1.RESOURCE_CONFIG_PATH}`, JSON.stringify(sortByKey(applicationConfig), null, 2));
|
|
138
|
-
const createdFiles = await Promise.resolve(
|
|
138
|
+
const createdFiles = await Promise.resolve(
|
|
139
|
+
// Defensive check for when application settings are missing componentFiles
|
|
140
|
+
(_f = (_e = resource.componentFiles) === null || _e === void 0 ? void 0 : _e.map((file) => downloadFile(appDirName, file.filename, file.url))) !== null && _f !== void 0 ? _f : []);
|
|
139
141
|
// print out failed downloads synchronously here
|
|
140
142
|
createdFiles
|
|
141
143
|
.filter((createdFiles) => createdFiles.length)
|
|
@@ -149,7 +151,7 @@ async function writeResourceToDisk(resourceType, resourceId, resource, now, root
|
|
|
149
151
|
}
|
|
150
152
|
case "BACKEND": {
|
|
151
153
|
const parentDirName = "backends";
|
|
152
|
-
const apiName = slugifyName((
|
|
154
|
+
const apiName = slugifyName((_j = (_h = (_g = resource.apiPb) === null || _g === void 0 ? void 0 : _g.metadata) === null || _h === void 0 ? void 0 : _h.name) !== null && _j !== void 0 ? _j : resource.actions.name);
|
|
153
155
|
// server is still sending actions for a backwards compatibility
|
|
154
156
|
delete resource.actions;
|
|
155
157
|
const newRelativeLocation = `${parentDirName}/${apiName}`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getWarningsForApplicationMigration(previousVersion: string, newVersion: string): string | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getWarningsForApplicationMigration = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const colorette_1 = require("colorette");
|
|
6
|
+
const semver_1 = tslib_1.__importDefault(require("semver"));
|
|
7
|
+
// If you are changing how application files work on disk, for example changing the directory layout,
|
|
8
|
+
// you need to tell the user how to upgrade manually.
|
|
9
|
+
const BREAKING_APPLICATION_VERSIONS = [
|
|
10
|
+
{
|
|
11
|
+
version: "0.0.20",
|
|
12
|
+
// This is an example message because 0.0.20 does not have a docs page for any migrations. This will be added
|
|
13
|
+
// in a future release
|
|
14
|
+
message: `${(0, colorette_1.red)("Warning")}: Your code must be updated due to a breaking change in custom component definitions. See docs.`,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
version: "0.0.21",
|
|
18
|
+
// This is an example message because 0.0.21 does not have a docs page for any migrations. This will be added
|
|
19
|
+
// in a future release
|
|
20
|
+
message: `${(0, colorette_1.red)("Error")}: This version of the CLI is incompatible with any Custom Components you've previously used,
|
|
21
|
+
due to breaking changes introduced in the config.ts format and Custom Components React API.
|
|
22
|
+
|
|
23
|
+
${(0, colorette_1.bold)("Your existing components are safe.")}
|
|
24
|
+
|
|
25
|
+
To manually migrate:
|
|
26
|
+
|
|
27
|
+
1. Rename "eventHandlers" to "events".
|
|
28
|
+
2. Update your properties to use the new format: ${(0, colorette_1.magenta)("https://docs.superblocks.com/applications/custom-components/development-lifecycle#configts")}
|
|
29
|
+
`,
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
function getWarningsForApplicationMigration(previousVersion, newVersion) {
|
|
33
|
+
const firstBreakingChange = BREAKING_APPLICATION_VERSIONS.find(({ version }) => {
|
|
34
|
+
return (semver_1.default.lt(previousVersion, version) && semver_1.default.lte(version, newVersion));
|
|
35
|
+
});
|
|
36
|
+
return firstBreakingChange === null || firstBreakingChange === void 0 ? void 0 : firstBreakingChange.message;
|
|
37
|
+
}
|
|
38
|
+
exports.getWarningsForApplicationMigration = getWarningsForApplicationMigration;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { SuperblocksApplicationConfig, SuperblocksBackendConfig, SuperblocksMonorepoConfig } from "@superblocksteam/util";
|
|
2
|
+
export declare function migrateSuperblocksMonorepoConfig(config: SuperblocksMonorepoConfig, options: {
|
|
3
|
+
previousVersion: string;
|
|
4
|
+
currentVersion: string;
|
|
5
|
+
}): Promise<SuperblocksMonorepoConfig>;
|
|
6
|
+
export declare function migrateSuperblocksEntityConfig(config: SuperblocksApplicationConfig | SuperblocksBackendConfig, options: {
|
|
7
|
+
previousVersion: string;
|
|
8
|
+
currentVersion: string;
|
|
9
|
+
}): Promise<SuperblocksApplicationConfig | SuperblocksBackendConfig>;
|
|
10
|
+
export declare function isDotfileMigrationNeeded(previousVersion: string, currentVersion: string): boolean;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isDotfileMigrationNeeded = exports.migrateSuperblocksEntityConfig = exports.migrateSuperblocksMonorepoConfig = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const semver_1 = tslib_1.__importDefault(require("semver"));
|
|
6
|
+
// These migrations are used to upgrade the Superblocks config files as needed.
|
|
7
|
+
// For example if we add a new required field
|
|
8
|
+
const MonorepoMigratorsFromOldVersion = [
|
|
9
|
+
{
|
|
10
|
+
migrateWhenUpgradingTo: "0.0.19",
|
|
11
|
+
migrator: (config) => config,
|
|
12
|
+
},
|
|
13
|
+
];
|
|
14
|
+
// Recursively migrate configs to the latest version
|
|
15
|
+
async function migrateSuperblocksMonorepoConfig(config, options) {
|
|
16
|
+
return await sharedMigrator(MonorepoMigratorsFromOldVersion, config, options);
|
|
17
|
+
}
|
|
18
|
+
exports.migrateSuperblocksMonorepoConfig = migrateSuperblocksMonorepoConfig;
|
|
19
|
+
// These migrations are used to upgrade the Superblocks config files as needed.
|
|
20
|
+
// For example if we add a new required field
|
|
21
|
+
const EntityMigratorsFromOldVersion = [
|
|
22
|
+
{
|
|
23
|
+
migrateWhenUpgradingTo: "0.0.19",
|
|
24
|
+
migrator: (config) => config,
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
async function migrateSuperblocksEntityConfig(config, options) {
|
|
28
|
+
return await sharedMigrator(EntityMigratorsFromOldVersion, config, options);
|
|
29
|
+
}
|
|
30
|
+
exports.migrateSuperblocksEntityConfig = migrateSuperblocksEntityConfig;
|
|
31
|
+
function isDotfileMigrationNeeded(previousVersion, currentVersion) {
|
|
32
|
+
return (previousVersion !== currentVersion &&
|
|
33
|
+
(MonorepoMigratorsFromOldVersion.some(({ migrateWhenUpgradingTo }) => semver_1.default.gt(migrateWhenUpgradingTo, previousVersion) &&
|
|
34
|
+
semver_1.default.lte(migrateWhenUpgradingTo, currentVersion)) ||
|
|
35
|
+
EntityMigratorsFromOldVersion.some(({ migrateWhenUpgradingTo }) => semver_1.default.gt(migrateWhenUpgradingTo, previousVersion) &&
|
|
36
|
+
semver_1.default.lte(migrateWhenUpgradingTo, currentVersion))));
|
|
37
|
+
}
|
|
38
|
+
exports.isDotfileMigrationNeeded = isDotfileMigrationNeeded;
|
|
39
|
+
async function sharedMigrator(migratorObject, config, { previousVersion, currentVersion, }) {
|
|
40
|
+
let newConfig = JSON.parse(JSON.stringify(config));
|
|
41
|
+
// Find all migrators in the range between previousVersion and currentVersion
|
|
42
|
+
const migratorFns = migratorObject.filter(({ migrateWhenUpgradingTo }) => semver_1.default.gt(migrateWhenUpgradingTo, previousVersion) &&
|
|
43
|
+
semver_1.default.lte(migrateWhenUpgradingTo, currentVersion));
|
|
44
|
+
migratorFns.forEach(({ migrator, migrateWhenUpgradingTo }) => {
|
|
45
|
+
try {
|
|
46
|
+
newConfig = migrator(newConfig);
|
|
47
|
+
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
// Migrations fail if our preconditions aren't met
|
|
50
|
+
console.warn(`Error while migrating config to version ${migrateWhenUpgradingTo}: ${e.message}`);
|
|
51
|
+
throw e;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return newConfig;
|
|
55
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.0.
|
|
2
|
+
"version": "0.0.22",
|
|
3
3
|
"commands": {
|
|
4
4
|
"init": {
|
|
5
5
|
"id": "init",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"login": {
|
|
39
39
|
"id": "login",
|
|
40
|
-
"description": "
|
|
40
|
+
"description": "Authenticate with Superblocks cloud",
|
|
41
41
|
"strict": true,
|
|
42
42
|
"pluginName": "@superblocksteam/cli",
|
|
43
43
|
"pluginAlias": "@superblocksteam/cli",
|
|
@@ -54,6 +54,20 @@
|
|
|
54
54
|
},
|
|
55
55
|
"args": {}
|
|
56
56
|
},
|
|
57
|
+
"migrate": {
|
|
58
|
+
"id": "migrate",
|
|
59
|
+
"description": "Migrate files to use the current CLI version",
|
|
60
|
+
"strict": true,
|
|
61
|
+
"pluginName": "@superblocksteam/cli",
|
|
62
|
+
"pluginAlias": "@superblocksteam/cli",
|
|
63
|
+
"pluginType": "core",
|
|
64
|
+
"aliases": [],
|
|
65
|
+
"examples": [
|
|
66
|
+
"<%= config.bin %> <%= command.id %>"
|
|
67
|
+
],
|
|
68
|
+
"flags": {},
|
|
69
|
+
"args": {}
|
|
70
|
+
},
|
|
57
71
|
"pull": {
|
|
58
72
|
"id": "pull",
|
|
59
73
|
"description": "Download objects from Superblocks and save them locally",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superblocksteam/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"description": "Official Superblocks CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"superblocks": "bin/run"
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"fs-extra": "^11.1.1",
|
|
30
30
|
"listr2": "6.6.0",
|
|
31
31
|
"lodash": "^4.17.21",
|
|
32
|
+
"semver": "^7.5.4",
|
|
32
33
|
"slugify": "^1.6.6",
|
|
33
34
|
"vite": "^4.4.8",
|
|
34
35
|
"vite-plugin-inspect": "^0.7.28",
|