@swell/cli 2.1.0 → 2.2.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/dist/commands/api/delete.d.ts +15 -0
- package/dist/commands/api/delete.js +26 -0
- package/dist/commands/api/get.d.ts +15 -0
- package/dist/commands/api/get.js +31 -0
- package/dist/commands/api/index.d.ts +7 -0
- package/dist/commands/api/index.js +16 -0
- package/dist/commands/api/post.d.ts +16 -0
- package/dist/commands/api/post.js +33 -0
- package/dist/commands/api/put.d.ts +16 -0
- package/dist/commands/api/put.js +33 -0
- package/dist/commands/app/dev.d.ts +9 -9
- package/dist/commands/app/dev.js +158 -136
- package/dist/commands/app/frontend/dev.d.ts +1 -1
- package/dist/commands/app/frontend/dev.js +84 -78
- package/dist/commands/app/install.js +3 -3
- package/dist/commands/app/pull.d.ts +1 -1
- package/dist/commands/app/pull.js +6 -6
- package/dist/commands/app/push.d.ts +3 -3
- package/dist/commands/app/push.js +8 -6
- package/dist/commands/app/release.d.ts +1 -1
- package/dist/commands/app/release.js +1 -1
- package/dist/commands/app/version.d.ts +1 -1
- package/dist/commands/app/version.js +5 -3
- package/dist/commands/create/app.d.ts +10 -2
- package/dist/commands/create/app.js +226 -57
- package/dist/commands/create/content.d.ts +4 -0
- package/dist/commands/create/content.js +83 -15
- package/dist/commands/create/function.js +27 -5
- package/dist/commands/create/model.d.ts +4 -0
- package/dist/commands/create/model.js +120 -25
- package/dist/commands/inspect/content.d.ts +25 -0
- package/dist/commands/inspect/content.js +159 -0
- package/dist/commands/inspect/index.d.ts +7 -0
- package/dist/commands/inspect/index.js +16 -0
- package/dist/commands/inspect/models.d.ts +27 -0
- package/dist/commands/inspect/models.js +197 -0
- package/dist/commands/schema.d.ts +27 -0
- package/dist/commands/schema.js +231 -0
- package/dist/commands/theme/dev.d.ts +1 -1
- package/dist/commands/theme/dev.js +2 -2
- package/dist/commands/theme/init.d.ts +1 -1
- package/dist/commands/theme/init.js +1 -1
- package/dist/commands/theme/pull.d.ts +2 -2
- package/dist/commands/theme/pull.js +2 -2
- package/dist/commands/theme/push.d.ts +1 -1
- package/dist/commands/theme/push.js +1 -1
- package/dist/create-app-command.d.ts +12 -2
- package/dist/create-app-command.js +103 -80
- package/dist/create-collection-command.js +4 -1
- package/dist/create-config-command.js +4 -0
- package/dist/env/local.d.ts +1 -0
- package/dist/env/local.js +1 -0
- package/dist/env/production.d.ts +1 -0
- package/dist/env/production.js +1 -0
- package/dist/env/review.d.ts +1 -0
- package/dist/env/review.js +1 -0
- package/dist/env/staging.d.ts +1 -0
- package/dist/env/staging.js +1 -0
- package/dist/lib/api.d.ts +16 -5
- package/dist/lib/api.js +67 -25
- package/dist/lib/app-config.js +1 -0
- package/dist/lib/apps/index.d.ts +2 -0
- package/dist/lib/apps/index.js +20 -15
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/constants.d.ts +1 -0
- package/dist/lib/constants.js +1 -0
- package/dist/lib/create/content.d.ts +5 -1
- package/dist/lib/create/content.js +8 -1
- package/dist/lib/create/function.d.ts +2 -1
- package/dist/lib/create/function.js +7 -3
- package/dist/lib/create/model.d.ts +1 -0
- package/dist/lib/create/schemas.d.ts +6 -0
- package/dist/lib/create/schemas.js +6 -0
- package/dist/lib/sessions.js +4 -3
- package/dist/lib/style.js +0 -1
- package/dist/lib/theme-sync.d.ts +6 -6
- package/dist/lib/theme-sync.js +49 -39
- package/dist/push-app-command.d.ts +7 -7
- package/dist/push-app-command.js +38 -36
- package/dist/remote-app-command.d.ts +1 -1
- package/dist/remote-app-command.js +4 -2
- package/dist/swell-api-command.d.ts +13 -0
- package/dist/swell-api-command.js +75 -0
- package/dist/swell-command.d.ts +1 -1
- package/dist/swell-command.js +9 -9
- package/package.json +8 -1
- package/oclif.manifest.json +0 -1936
|
@@ -7,30 +7,49 @@ import { newConfig, swellConfigFileExists } from '../../lib/app-config.js';
|
|
|
7
7
|
import { toAppId, toAppName } from '../../lib/create/index.js';
|
|
8
8
|
import style from '../../lib/style.js';
|
|
9
9
|
export default class CreateApp extends CreateAppCommand {
|
|
10
|
-
appType = '';
|
|
11
10
|
static args = {
|
|
12
11
|
id: Args.string({
|
|
13
12
|
default: '',
|
|
14
|
-
description: `id of the app to create`,
|
|
13
|
+
description: `id of the app to create (required in -y)`,
|
|
15
14
|
}),
|
|
16
15
|
};
|
|
17
16
|
static description = 'Create an app.';
|
|
18
17
|
static examples = [
|
|
19
18
|
{
|
|
20
19
|
command: 'swell create app',
|
|
21
|
-
description: '
|
|
20
|
+
description: 'Interactive: follow prompts to create an app.',
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
command: 'swell create app my_app -t admin -y',
|
|
24
|
+
description: 'Non-interactive: minimal with defaults.',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
command: 'swell create app my_app --type admin --name "My App" --version 1.0.1 --description "" -y',
|
|
28
|
+
description: 'Non-interactive: explicit name, version, description.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
command: 'swell create app my_app --type admin --frontend astro -y',
|
|
32
|
+
description: 'Non-interactive: initialize an Astro frontend.',
|
|
22
33
|
},
|
|
23
34
|
{
|
|
24
|
-
command: 'swell create app
|
|
25
|
-
description: '
|
|
35
|
+
command: 'swell create app my_theme --type theme --storefront-app proxima -y',
|
|
36
|
+
description: 'Non-interactive: theme targeting a specific storefront app.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
command: 'swell create app my_payment --type integration --integration-type payment --integration-id card -y',
|
|
40
|
+
description: 'Non-interactive: payment integration for credit cards.',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
command: 'swell create app my_shipping --type integration --integration-type shipping --integration-id fedex -y',
|
|
44
|
+
description: 'Non-interactive: shipping integration for FedEx.',
|
|
26
45
|
},
|
|
27
46
|
{
|
|
28
47
|
command: 'swell create app -p yarn',
|
|
29
|
-
description: '
|
|
48
|
+
description: 'Package manager: use Yarn instead of npm.',
|
|
30
49
|
},
|
|
31
50
|
{
|
|
32
51
|
command: 'swell create app -p none',
|
|
33
|
-
description: '
|
|
52
|
+
description: 'Package manager: skip installing default dependencies.',
|
|
34
53
|
},
|
|
35
54
|
];
|
|
36
55
|
static flags = {
|
|
@@ -42,34 +61,66 @@ export default class CreateApp extends CreateAppCommand {
|
|
|
42
61
|
}),
|
|
43
62
|
type: Flags.string({
|
|
44
63
|
char: 't',
|
|
45
|
-
description: `create a specific app type`,
|
|
64
|
+
description: `create a specific app type (required in -y)`,
|
|
46
65
|
options: ['admin', 'integration', 'storefront', 'theme'],
|
|
47
66
|
}),
|
|
67
|
+
name: Flags.string({
|
|
68
|
+
char: 'n',
|
|
69
|
+
description: `application name (default: capitalized ID)`,
|
|
70
|
+
}),
|
|
71
|
+
version: Flags.string({
|
|
72
|
+
char: 'v',
|
|
73
|
+
description: `application version (default: 1.0.0)`,
|
|
74
|
+
}),
|
|
75
|
+
description: Flags.string({
|
|
76
|
+
char: 'd',
|
|
77
|
+
description: `application description`,
|
|
78
|
+
}),
|
|
48
79
|
};
|
|
49
|
-
|
|
50
|
-
|
|
80
|
+
appType = '';
|
|
81
|
+
async createSwellConfig({ allowOverwrite = true, inputId = '', inputStorefrontApp, inputType, inputName, inputVersion, inputDescription, inputFrontend, inputIntegrationType, inputIntegrationId, inputYes, nestedPath = true, }) {
|
|
82
|
+
const confirmYes = inputYes;
|
|
83
|
+
let appId = toAppId(inputId || '');
|
|
84
|
+
let storefrontInstalledApps;
|
|
85
|
+
let storefrontAppId;
|
|
86
|
+
let installedStorefrontApp;
|
|
87
|
+
// check missed parameters
|
|
88
|
+
if (confirmYes) {
|
|
89
|
+
if (!appId) {
|
|
90
|
+
this.error('Missing application id for non-interactive mode\n\nYou need to provide an app id value for non-interactive mode\n\nExample: swell create app reviews --type admin -y', {
|
|
91
|
+
exit: 1,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (!inputType) {
|
|
95
|
+
this.error('Missing required flag for non-interactive mode: --type\n\nValid types: admin, integration, storefront, theme\n\nExample: swell create app reviews --type admin -y', {
|
|
96
|
+
exit: 1,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (!inputName) {
|
|
100
|
+
inputName = toAppName(inputId);
|
|
101
|
+
}
|
|
102
|
+
if (!inputVersion) {
|
|
103
|
+
inputVersion = '1.0.0';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
51
106
|
const type = inputType ||
|
|
52
|
-
(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
message: `What is the primary purpose of the app?`,
|
|
62
|
-
}));
|
|
107
|
+
(await select({
|
|
108
|
+
choices: [
|
|
109
|
+
{ name: 'Admin', value: 'admin' },
|
|
110
|
+
{ name: 'Integration', value: 'integration' },
|
|
111
|
+
{ name: 'Storefront', value: 'storefront' },
|
|
112
|
+
{ name: 'Theme', value: 'theme' },
|
|
113
|
+
],
|
|
114
|
+
message: `What is the primary purpose of the app?`,
|
|
115
|
+
}));
|
|
63
116
|
const typeLabel = type === 'theme' ? 'theme' : 'app';
|
|
64
117
|
const typeLabelTitle = type === 'theme' ? 'Theme' : 'App';
|
|
65
|
-
const name =
|
|
66
|
-
|
|
67
|
-
: await input({
|
|
118
|
+
const name = inputName ||
|
|
119
|
+
(await input({
|
|
68
120
|
default: inputId ? toAppName(inputId) : '',
|
|
69
121
|
message: `${typeLabelTitle} name`,
|
|
70
122
|
validate: (answer) => answer.length > 2,
|
|
71
|
-
});
|
|
72
|
-
let appId = toAppId(inputId || '');
|
|
123
|
+
}));
|
|
73
124
|
if (!appId) {
|
|
74
125
|
appId = await input({
|
|
75
126
|
default: toAppId(name),
|
|
@@ -77,39 +128,135 @@ export default class CreateApp extends CreateAppCommand {
|
|
|
77
128
|
validate: (answer) => answer.length > 2,
|
|
78
129
|
});
|
|
79
130
|
appId = toAppId(appId);
|
|
80
|
-
confirmYes = Boolean(inputYes && appId);
|
|
81
131
|
}
|
|
82
|
-
const version =
|
|
83
|
-
|
|
84
|
-
: await input({
|
|
132
|
+
const version = inputVersion ||
|
|
133
|
+
(await input({
|
|
85
134
|
default: '1.0.0',
|
|
86
135
|
message: `${typeLabelTitle} version`,
|
|
87
|
-
});
|
|
88
|
-
const description =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
let
|
|
96
|
-
let
|
|
136
|
+
}));
|
|
137
|
+
const description = inputDescription ||
|
|
138
|
+
(confirmYes
|
|
139
|
+
? ''
|
|
140
|
+
: await input({
|
|
141
|
+
default: 'Something special',
|
|
142
|
+
message: `Describe your ${typeLabel}`,
|
|
143
|
+
}));
|
|
144
|
+
let integrationType;
|
|
145
|
+
let extensions;
|
|
146
|
+
if (type === 'integration') {
|
|
147
|
+
if (confirmYes && !inputIntegrationType) {
|
|
148
|
+
this.error('Missing required flag for non-interactive mode for integration app: --integration-type\n\nValid types: generic, payment, shipping, tax\n\nExample: swell create app reviews --type integration --integration-type generic -y', {
|
|
149
|
+
exit: 1,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
integrationType =
|
|
153
|
+
inputIntegrationType ||
|
|
154
|
+
(await select({
|
|
155
|
+
choices: [
|
|
156
|
+
{ name: 'Generic', value: 'generic' },
|
|
157
|
+
{ name: 'Payment gateway', value: 'payment' },
|
|
158
|
+
{ name: 'Shipping service', value: 'shipping' },
|
|
159
|
+
{ name: 'Tax service', value: 'tax' },
|
|
160
|
+
],
|
|
161
|
+
message: `What type of integration?`,
|
|
162
|
+
}));
|
|
163
|
+
// Create extensions array based on integration type
|
|
164
|
+
switch (integrationType) {
|
|
165
|
+
case 'generic': {
|
|
166
|
+
// No extension needed for generic integrations
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
case 'payment':
|
|
170
|
+
case 'shipping':
|
|
171
|
+
case 'tax': {
|
|
172
|
+
// Unified validation for all extension-based integrations
|
|
173
|
+
if (confirmYes && !inputIntegrationId) {
|
|
174
|
+
const validValues = integrationType === 'payment'
|
|
175
|
+
? "use 'card' for credit card integration or unique custom ID for other payment integrations"
|
|
176
|
+
: 'unique custom ID';
|
|
177
|
+
this.error(`Missing required flag for non-interactive mode for ${integrationType} integration: --integration-id\n\nValid values: ${validValues}\n\nExample: swell create app my_app --type integration --integration-type ${integrationType} --integration-id ${integrationType === 'payment' ? 'card' : 'myid'} -y`, {
|
|
178
|
+
exit: 1,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
// Interactive prompting
|
|
182
|
+
let integrationId = inputIntegrationId;
|
|
183
|
+
if (!integrationId) {
|
|
184
|
+
if (integrationType === 'payment') {
|
|
185
|
+
const methodChoice = await select({
|
|
186
|
+
choices: [
|
|
187
|
+
{ name: 'Credit card', value: 'card' },
|
|
188
|
+
{ name: 'Custom method', value: 'custom' },
|
|
189
|
+
],
|
|
190
|
+
message: `What type of payment method?`,
|
|
191
|
+
});
|
|
192
|
+
integrationId =
|
|
193
|
+
methodChoice === 'custom'
|
|
194
|
+
? await input({
|
|
195
|
+
message: `Enter a unique ID for your payment method`,
|
|
196
|
+
validate: (answer) => answer.length > 2,
|
|
197
|
+
})
|
|
198
|
+
: methodChoice; // 'card'
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// Shipping or tax
|
|
202
|
+
const serviceLabel = integrationType === 'shipping'
|
|
203
|
+
? 'shipping service'
|
|
204
|
+
: 'tax service';
|
|
205
|
+
integrationId = await input({
|
|
206
|
+
message: `Enter a unique ID for your ${serviceLabel}`,
|
|
207
|
+
validate: (answer) => answer.length > 2,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Unified validation
|
|
212
|
+
if (integrationId.length < 3) {
|
|
213
|
+
this.error(`--integration-id must contain at least 3 characters\n\nValid value is a custom ID containing at least 3 characters\n\nExample: swell create app my_app --type integration --integration-type ${integrationType} --integration-id ${integrationType === 'payment' ? 'card' : 'myid'} -y`, {
|
|
214
|
+
exit: 1,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
// Create extension
|
|
218
|
+
extensions = [
|
|
219
|
+
{
|
|
220
|
+
id: integrationId,
|
|
221
|
+
type: integrationType,
|
|
222
|
+
},
|
|
223
|
+
];
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
default: {
|
|
227
|
+
this.error('Incorrect value for --integration-type\n\nValid types: generic, payment, shipping, tax\n\nExample: swell create app reviews --type integration --integration-type generic -y', {
|
|
228
|
+
exit: 1,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
97
233
|
if (type === 'theme') {
|
|
98
234
|
storefrontInstalledApps = await this.getInstalledStorefrontApps();
|
|
99
235
|
if (storefrontInstalledApps === false) {
|
|
100
|
-
|
|
236
|
+
this.error('There are no installed storefront apps\n\nTo use the theme type, you first need to install the storefront app', {
|
|
237
|
+
exit: 1,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
storefrontAppId = inputStorefrontApp || '';
|
|
241
|
+
if (!storefrontAppId) {
|
|
242
|
+
if (storefrontInstalledApps.length === 1) {
|
|
243
|
+
storefrontAppId = storefrontInstalledApps[0].id;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
if (confirmYes) {
|
|
247
|
+
this.error('There are several installed storefront apps to choose from\n\nYou should select a specific storefront app in the non-interactive mode using --storefront-app appid\n\nExample: swell create app reviews --type theme --storefront-app proxima -y', {
|
|
248
|
+
exit: 1,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
storefrontAppId = await select({
|
|
252
|
+
choices: storefrontInstalledApps.map(({ app, id }) => ({
|
|
253
|
+
name: app.name,
|
|
254
|
+
value: id,
|
|
255
|
+
})),
|
|
256
|
+
message: `Which storefront app is your theme for?`,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
101
259
|
}
|
|
102
|
-
storefrontAppId =
|
|
103
|
-
inputStorefrontApp ||
|
|
104
|
-
(storefrontInstalledApps.length > 1
|
|
105
|
-
? await select({
|
|
106
|
-
choices: storefrontInstalledApps.map(({ app, id }) => ({
|
|
107
|
-
name: app.name,
|
|
108
|
-
value: id,
|
|
109
|
-
})),
|
|
110
|
-
message: `Which storefront app is your theme for?`,
|
|
111
|
-
})
|
|
112
|
-
: storefrontInstalledApps[0].id);
|
|
113
260
|
installedStorefrontApp = storefrontInstalledApps.find((installedApp) => inputStorefrontApp
|
|
114
261
|
? inputStorefrontApp === installedApp.app_private_id ||
|
|
115
262
|
inputStorefrontApp === installedApp.app_public_id ||
|
|
@@ -117,7 +264,20 @@ export default class CreateApp extends CreateAppCommand {
|
|
|
117
264
|
inputStorefrontApp === toAppId(installedApp.app_private_id)
|
|
118
265
|
: installedApp.id === storefrontAppId);
|
|
119
266
|
if (!installedStorefrontApp) {
|
|
120
|
-
this.error(`Could not find an installed storefront app by id: ${storefrontAppId}
|
|
267
|
+
this.error(`Could not find an installed storefront app by id: ${storefrontAppId}\n\nThe selected storefront app is not among the installed storefront apps`, {
|
|
268
|
+
exit: 1,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// check inputFrontend value
|
|
273
|
+
if (!installedStorefrontApp &&
|
|
274
|
+
confirmYes &&
|
|
275
|
+
inputFrontend &&
|
|
276
|
+
inputFrontend !== 'none') {
|
|
277
|
+
const projectType = this.getProjectType(inputFrontend);
|
|
278
|
+
if (!projectType) {
|
|
279
|
+
// error should be thrown from getProjectType
|
|
280
|
+
return;
|
|
121
281
|
}
|
|
122
282
|
}
|
|
123
283
|
const swellConfigJson = {
|
|
@@ -137,6 +297,7 @@ export default class CreateApp extends CreateAppCommand {
|
|
|
137
297
|
: {
|
|
138
298
|
permissions: [],
|
|
139
299
|
}),
|
|
300
|
+
...(extensions && { extensions }),
|
|
140
301
|
};
|
|
141
302
|
const appPath = path.join(process.cwd(), inputId || appId);
|
|
142
303
|
if (swellConfigFileExists(appPath)) {
|
|
@@ -151,7 +312,9 @@ export default class CreateApp extends CreateAppCommand {
|
|
|
151
312
|
}
|
|
152
313
|
}
|
|
153
314
|
else {
|
|
154
|
-
this.error(`An app already exists in the target path ${appPath}
|
|
315
|
+
this.error(`An app already exists in the target path ${appPath}.\n\nCannot overwrite existing app`, {
|
|
316
|
+
exit: 1,
|
|
317
|
+
});
|
|
155
318
|
}
|
|
156
319
|
}
|
|
157
320
|
this.log(`\nCreating ${this.appType === 'theme' ? 'theme' : 'app'} ${style.appConfigValue(name)} in ${appPath}/swell.json`);
|
|
@@ -172,13 +335,19 @@ export default class CreateApp extends CreateAppCommand {
|
|
|
172
335
|
async run() {
|
|
173
336
|
const { args, flags } = await this.parse(CreateApp);
|
|
174
337
|
const { id } = args;
|
|
175
|
-
const { pkg, yes } = flags;
|
|
176
|
-
const confirmYes = Boolean(yes
|
|
338
|
+
const { pkg, yes, name, type, version, description, frontend } = flags;
|
|
339
|
+
const confirmYes = Boolean(yes);
|
|
177
340
|
const createParams = await this.createSwellConfig({
|
|
178
341
|
inputId: id,
|
|
179
342
|
inputStorefrontApp: flags['storefront-app'],
|
|
180
|
-
inputType:
|
|
343
|
+
inputType: type,
|
|
181
344
|
inputYes: confirmYes,
|
|
345
|
+
inputName: name,
|
|
346
|
+
inputVersion: version,
|
|
347
|
+
inputDescription: description,
|
|
348
|
+
inputFrontend: frontend,
|
|
349
|
+
inputIntegrationType: flags['integration-type'],
|
|
350
|
+
inputIntegrationId: flags['integration-id'],
|
|
182
351
|
});
|
|
183
352
|
if (!createParams) {
|
|
184
353
|
return;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { CreateCollectionCommand } from '../../create-collection-command.js';
|
|
2
2
|
import { ConfigType } from '../../lib/apps/index.js';
|
|
3
3
|
export default class CreateContent extends CreateCollectionCommand {
|
|
4
|
+
static args: {
|
|
5
|
+
collection: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
4
7
|
static description: string;
|
|
5
8
|
static examples: {
|
|
6
9
|
command: string;
|
|
@@ -12,6 +15,7 @@ export default class CreateContent extends CreateCollectionCommand {
|
|
|
12
15
|
label: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
13
16
|
overwrite: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
14
17
|
views: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
18
|
+
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
15
19
|
};
|
|
16
20
|
static summary: string;
|
|
17
21
|
createType: ConfigType;
|
|
@@ -1,27 +1,40 @@
|
|
|
1
1
|
import { checkbox } from '@inquirer/prompts';
|
|
2
|
-
import { Flags } from '@oclif/core';
|
|
2
|
+
import { Args, Flags } from '@oclif/core';
|
|
3
3
|
import { CreateCollectionCommand } from '../../create-collection-command.js';
|
|
4
4
|
import { ConfigType } from '../../lib/apps/index.js';
|
|
5
|
-
import { parseFields, parseViews, } from '../../lib/create/content.js';
|
|
6
|
-
import {
|
|
5
|
+
import { parseFields, parseViews, validateViews, } from '../../lib/create/content.js';
|
|
6
|
+
import { SCHEMAS } from '../../lib/create/schemas.js';
|
|
7
|
+
import { toCollectionLabel, toCollectionName, toFileName, } from '../../lib/create/index.js';
|
|
7
8
|
export default class CreateContent extends CreateCollectionCommand {
|
|
8
|
-
static
|
|
9
|
+
static args = {
|
|
10
|
+
collection: Args.string({
|
|
11
|
+
default: '',
|
|
12
|
+
description: 'collection ID to create or apply to, e.g., products (required with -y)',
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
static description = `Interactive mode: choose to create a new collection or extend an existing standard collection. The CLI will propose a list of standard collections you can extend.
|
|
16
|
+
|
|
17
|
+
Non-interactive (-y): provide COLLECTION explicitly. To discover standard collections first, run: swell models.`;
|
|
9
18
|
static examples = [
|
|
10
19
|
{
|
|
11
20
|
command: 'swell create content',
|
|
12
21
|
description: 'Create content model interactively.',
|
|
13
22
|
},
|
|
14
23
|
{
|
|
15
|
-
command: 'swell create content
|
|
16
|
-
description: '
|
|
24
|
+
command: 'swell create content products -y',
|
|
25
|
+
description: 'Apply configuration to a standard collection (non-interactive)',
|
|
17
26
|
},
|
|
18
27
|
{
|
|
19
|
-
command: 'swell create content visitors -f name:short_text,
|
|
20
|
-
description: '
|
|
28
|
+
command: 'swell create content visitors -v list,edit,new -f name:short_text,verified:boolean -l "Visitors" -d "" -y',
|
|
29
|
+
description: 'Create custom collection with explicit fields and views (non-interactive)',
|
|
21
30
|
},
|
|
22
31
|
{
|
|
23
|
-
command: 'swell create content visitors
|
|
24
|
-
description: '
|
|
32
|
+
command: 'swell create content visitors --overwrite -y',
|
|
33
|
+
description: 'Non-interactive: force overwrite existing config file',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
command: 'swell create content visitors -f name:short_text,description:long_text,verified:boolean,visitor_count:integer,photo:image,orders:collection',
|
|
37
|
+
description: 'Specify fields with types delimited by commas (interactive mode)',
|
|
25
38
|
},
|
|
26
39
|
];
|
|
27
40
|
static flags = {
|
|
@@ -33,30 +46,85 @@ export default class CreateContent extends CreateCollectionCommand {
|
|
|
33
46
|
fields: Flags.string({
|
|
34
47
|
char: 'f',
|
|
35
48
|
default: '',
|
|
36
|
-
description: '
|
|
49
|
+
description: 'comma-separated id:type pairs (e.g., name:short_text, verified:boolean)',
|
|
37
50
|
}),
|
|
38
51
|
label: Flags.string({
|
|
39
52
|
char: 'l',
|
|
40
53
|
default: '',
|
|
41
|
-
description: 'content model
|
|
54
|
+
description: 'display label for the content model (defaults to capitalized collection name)',
|
|
42
55
|
}),
|
|
43
56
|
overwrite: Flags.boolean({
|
|
44
57
|
default: false,
|
|
45
|
-
description: 'overwrite existing content model configuration file',
|
|
58
|
+
description: 'overwrite existing content model configuration file (use with -y to force overwrite)',
|
|
46
59
|
}),
|
|
47
60
|
views: Flags.string({
|
|
48
61
|
char: 'v',
|
|
49
62
|
default: '',
|
|
50
|
-
description: '
|
|
63
|
+
description: 'comma-separated views - valid: list, edit, new',
|
|
64
|
+
}),
|
|
65
|
+
yes: Flags.boolean({
|
|
66
|
+
char: 'y',
|
|
67
|
+
description: 'accept all default values; non-interactive, never prompts; errors if required values are missing',
|
|
51
68
|
}),
|
|
52
69
|
};
|
|
53
70
|
static summary = 'Initialize a content UI model config file with properties in the content folder.';
|
|
54
71
|
createType = ConfigType.CONTENT;
|
|
55
72
|
async run() {
|
|
56
|
-
const { flags } = await this.parse(CreateContent);
|
|
73
|
+
const { args, flags } = await this.parse(CreateContent);
|
|
74
|
+
const confirmYes = Boolean(flags.yes);
|
|
75
|
+
// NON-INTERACTIVE PATH
|
|
76
|
+
if (confirmYes) {
|
|
77
|
+
// Validate required arguments
|
|
78
|
+
const argCollection = args.collection;
|
|
79
|
+
if (!argCollection) {
|
|
80
|
+
this.error('Missing required argument for non-interactive mode: COLLECTION\n\nExample: swell create content products -y\n\nTip: run `swell models` to list standard collections you can extend.', { exit: 1 });
|
|
81
|
+
}
|
|
82
|
+
// Process and normalize collection name
|
|
83
|
+
const collection = toCollectionName(argCollection);
|
|
84
|
+
// Apply defaults for optional values
|
|
85
|
+
const label = flags.label || toCollectionLabel(collection);
|
|
86
|
+
const description = flags.description || '';
|
|
87
|
+
// Validate and parse views
|
|
88
|
+
let viewsList;
|
|
89
|
+
if (flags.views) {
|
|
90
|
+
viewsList = flags.views
|
|
91
|
+
.split(',')
|
|
92
|
+
.map((v) => v.trim())
|
|
93
|
+
.filter(Boolean);
|
|
94
|
+
validateViews(viewsList, this.error.bind(this));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Default views when not specified
|
|
98
|
+
viewsList = ['list', 'edit', 'new'];
|
|
99
|
+
}
|
|
100
|
+
// Parse fields (no validation - trust user input)
|
|
101
|
+
const fieldsRaw = (flags.fields || '');
|
|
102
|
+
const fieldPairs = fieldsRaw
|
|
103
|
+
? fieldsRaw
|
|
104
|
+
.split(',')
|
|
105
|
+
.map((v) => v.trim())
|
|
106
|
+
.filter(Boolean)
|
|
107
|
+
: [];
|
|
108
|
+
// Build file body
|
|
109
|
+
const fileName = toFileName(collection);
|
|
110
|
+
const fileBody = {
|
|
111
|
+
$schema: SCHEMAS.CONTENT,
|
|
112
|
+
collection,
|
|
113
|
+
description,
|
|
114
|
+
fields: parseFields(fieldPairs),
|
|
115
|
+
label,
|
|
116
|
+
views: parseViews(viewsList),
|
|
117
|
+
};
|
|
118
|
+
// Create file with overwrite handling
|
|
119
|
+
await this.createFile({ fileBody, fileName }, flags.overwrite,
|
|
120
|
+
/* shouldConfirm */ false);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// INTERACTIVE PATH (existing logic)
|
|
57
124
|
const { collection, description, label } = await this.promptCollection();
|
|
58
125
|
const fileName = toFileName(collection);
|
|
59
126
|
const fileBody = {
|
|
127
|
+
$schema: SCHEMAS.CONTENT,
|
|
60
128
|
collection,
|
|
61
129
|
};
|
|
62
130
|
const { fields, overwrite, views } = flags;
|
|
@@ -70,10 +70,13 @@ export default class CreateFunction extends CreateConfigCommand {
|
|
|
70
70
|
let writeFunctionFileParams = {
|
|
71
71
|
description,
|
|
72
72
|
events,
|
|
73
|
+
extension: undefined,
|
|
73
74
|
functionName: functionName || '',
|
|
74
|
-
language: (functionName?.
|
|
75
|
+
language: (functionName?.endsWith('.ts')
|
|
75
76
|
? 'ts'
|
|
76
|
-
: 'js')
|
|
77
|
+
: functionName?.endsWith('.js')
|
|
78
|
+
? 'js'
|
|
79
|
+
: undefined),
|
|
77
80
|
route,
|
|
78
81
|
schedule,
|
|
79
82
|
trigger: trigger,
|
|
@@ -85,6 +88,23 @@ export default class CreateFunction extends CreateConfigCommand {
|
|
|
85
88
|
await this.writeFunctionFile(writeFunctionFileParams, overwrite);
|
|
86
89
|
}
|
|
87
90
|
async gatherInput(params) {
|
|
91
|
+
// Check if there are any extensions in the app configuration
|
|
92
|
+
const extensions = this.swellConfig.get('extensions') || [];
|
|
93
|
+
let { extension } = params;
|
|
94
|
+
const extensionsWithId = extensions.filter((ext) => ext.id);
|
|
95
|
+
if (extensionsWithId.length > 0 && !extension) {
|
|
96
|
+
const extensionChoices = [
|
|
97
|
+
{ name: 'Not for an extension', value: '' },
|
|
98
|
+
...extensionsWithId.map((ext) => ({
|
|
99
|
+
name: ext.id,
|
|
100
|
+
value: ext.id,
|
|
101
|
+
})),
|
|
102
|
+
];
|
|
103
|
+
extension = await select({
|
|
104
|
+
choices: extensionChoices,
|
|
105
|
+
message: 'Is this function for an app extension?',
|
|
106
|
+
});
|
|
107
|
+
}
|
|
88
108
|
let { language } = params;
|
|
89
109
|
if (!language) {
|
|
90
110
|
language = await select({
|
|
@@ -155,9 +175,9 @@ export default class CreateFunction extends CreateConfigCommand {
|
|
|
155
175
|
message: 'Name your function',
|
|
156
176
|
});
|
|
157
177
|
}
|
|
158
|
-
|
|
178
|
+
let { description } = params;
|
|
159
179
|
if (!description) {
|
|
160
|
-
await input({
|
|
180
|
+
description = await input({
|
|
161
181
|
default: 'Does a thing when another other thing happens',
|
|
162
182
|
message: 'Describe what the function does',
|
|
163
183
|
});
|
|
@@ -165,6 +185,7 @@ export default class CreateFunction extends CreateConfigCommand {
|
|
|
165
185
|
return {
|
|
166
186
|
description,
|
|
167
187
|
events: events || '',
|
|
188
|
+
extension: extension || undefined,
|
|
168
189
|
functionName,
|
|
169
190
|
language,
|
|
170
191
|
route: route || '',
|
|
@@ -172,13 +193,14 @@ export default class CreateFunction extends CreateConfigCommand {
|
|
|
172
193
|
trigger,
|
|
173
194
|
};
|
|
174
195
|
}
|
|
175
|
-
async writeFunctionFile({ description, events, functionName, language, route, schedule, trigger, }, overwrite) {
|
|
196
|
+
async writeFunctionFile({ description, events, extension, functionName, language, route, schedule, trigger, }, overwrite) {
|
|
176
197
|
const fileName = toFunctionFileName(functionName);
|
|
177
198
|
await this.createFile({
|
|
178
199
|
extension: language,
|
|
179
200
|
fileBody: getFunctionTemplate({
|
|
180
201
|
description,
|
|
181
202
|
events,
|
|
203
|
+
extension,
|
|
182
204
|
functionName,
|
|
183
205
|
language,
|
|
184
206
|
route,
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { CreateCollectionCommand } from '../../create-collection-command.js';
|
|
2
2
|
import { ConfigType } from '../../lib/apps/index.js';
|
|
3
3
|
export default class CreateModel extends CreateCollectionCommand {
|
|
4
|
+
static args: {
|
|
5
|
+
collection: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
4
7
|
static description: string;
|
|
5
8
|
static examples: {
|
|
6
9
|
command: string;
|
|
@@ -12,6 +15,7 @@ export default class CreateModel extends CreateCollectionCommand {
|
|
|
12
15
|
fields: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
13
16
|
label: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
14
17
|
overwrite: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
18
|
+
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
15
19
|
};
|
|
16
20
|
static summary: string;
|
|
17
21
|
createType: ConfigType;
|