@swell/cli 2.1.0 → 2.2.1
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 +163 -139
- 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/index.js +5 -1
- package/dist/commands/create/model.d.ts +4 -0
- package/dist/commands/create/model.js +120 -25
- package/dist/commands/create/tests.d.ts +15 -0
- package/dist/commands/create/tests.js +79 -0
- 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 -14
- 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/create/tests/templates/env-dts.d.ts +1 -0
- package/dist/lib/create/tests/templates/env-dts.js +14 -0
- package/dist/lib/create/tests/templates/index.d.ts +8 -0
- package/dist/lib/create/tests/templates/index.js +8 -0
- package/dist/lib/create/tests/templates/integration-test.d.ts +1 -0
- package/dist/lib/create/tests/templates/integration-test.js +19 -0
- package/dist/lib/create/tests/templates/mock-request.d.ts +2 -0
- package/dist/lib/create/tests/templates/mock-request.js +112 -0
- package/dist/lib/create/tests/templates/setup-globals.d.ts +1 -0
- package/dist/lib/create/tests/templates/setup-globals.js +23 -0
- package/dist/lib/create/tests/templates/swell-client.d.ts +1 -0
- package/dist/lib/create/tests/templates/swell-client.js +126 -0
- package/dist/lib/create/tests/templates/tsconfig.d.ts +1 -0
- package/dist/lib/create/tests/templates/tsconfig.js +15 -0
- package/dist/lib/create/tests/templates/unit-test.d.ts +1 -0
- package/dist/lib/create/tests/templates/unit-test.js +42 -0
- package/dist/lib/create/tests/templates/vitest-config.d.ts +2 -0
- package/dist/lib/create/tests/templates/vitest-config.js +121 -0
- package/dist/lib/create/tests/types.d.ts +21 -0
- package/dist/lib/create/tests/types.js +1 -0
- package/dist/lib/create/tests.d.ts +4 -0
- package/dist/lib/create/tests.js +113 -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/oclif.manifest.json +639 -46
- package/package.json +8 -1
|
@@ -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 { toFileName } from '../../lib/create/index.js';
|
|
5
|
+
import { parseFields, parseViews, validateViews, } from '../../lib/create/content.js';
|
|
6
|
+
import { toCollectionLabel, toCollectionName, toFileName, } from '../../lib/create/index.js';
|
|
7
|
+
import { SCHEMAS } from '../../lib/create/schemas.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,
|
|
@@ -26,9 +26,13 @@ export default class Create extends Command {
|
|
|
26
26
|
name: 'Notification',
|
|
27
27
|
value: 'notification',
|
|
28
28
|
},
|
|
29
|
+
{
|
|
30
|
+
name: 'Tests',
|
|
31
|
+
value: 'tests',
|
|
32
|
+
},
|
|
29
33
|
],
|
|
30
34
|
message: 'What do you want to create?',
|
|
31
35
|
});
|
|
32
|
-
this.
|
|
36
|
+
await this.config.runCommand(`create:${createTarget}`);
|
|
33
37
|
}
|
|
34
38
|
}
|
|
@@ -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;
|
|
@@ -1,27 +1,36 @@
|
|
|
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 { toFileName } from '../../lib/create/index.js';
|
|
5
|
+
import { toCollectionLabel, toCollectionName, toFileName, } from '../../lib/create/index.js';
|
|
6
6
|
import { parseEvents, parseFields, } from '../../lib/create/model.js';
|
|
7
|
+
import { SCHEMAS } from '../../lib/create/schemas.js';
|
|
7
8
|
export default class CreateModel 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 inspect models.`;
|
|
9
18
|
static examples = [
|
|
10
19
|
{
|
|
11
20
|
command: 'swell create model',
|
|
12
|
-
description: 'Create data model configuration interactively
|
|
21
|
+
description: 'Create data model configuration interactively',
|
|
13
22
|
},
|
|
14
23
|
{
|
|
15
|
-
command: 'swell create model
|
|
16
|
-
description: '
|
|
24
|
+
command: 'swell create model products -y',
|
|
25
|
+
description: 'Apply configuration to a standard collection (non-interactive)',
|
|
17
26
|
},
|
|
18
27
|
{
|
|
19
|
-
command: 'swell create model visitors -f
|
|
20
|
-
description: '
|
|
28
|
+
command: 'swell create model visitors -e created,updated -f title:string,photo:image -l "Visitors" -d "" -y',
|
|
29
|
+
description: 'Create custom collection with explicit fields (non-interactive)',
|
|
21
30
|
},
|
|
22
31
|
{
|
|
23
|
-
command: 'swell
|
|
24
|
-
description: '
|
|
32
|
+
command: 'swell inspect models',
|
|
33
|
+
description: 'List standard collections available for extending',
|
|
25
34
|
},
|
|
26
35
|
];
|
|
27
36
|
static flags = {
|
|
@@ -33,40 +42,129 @@ export default class CreateModel extends CreateCollectionCommand {
|
|
|
33
42
|
events: Flags.string({
|
|
34
43
|
char: 'e',
|
|
35
44
|
default: 'created,updated,deleted',
|
|
36
|
-
description: '
|
|
45
|
+
description: 'comma-separated events - valid: created, updated, deleted',
|
|
37
46
|
}),
|
|
38
47
|
fields: Flags.string({
|
|
39
48
|
char: 'f',
|
|
40
49
|
default: '',
|
|
41
|
-
description: '
|
|
50
|
+
description: 'comma-separated id:type pairs (e.g., title:string, price:currency). Types: collection, currency, image, int, link, string',
|
|
42
51
|
}),
|
|
43
52
|
label: Flags.string({
|
|
44
53
|
char: 'l',
|
|
45
54
|
default: '',
|
|
46
|
-
description: '
|
|
55
|
+
description: 'display label for the model (defaults to capitalized collection name)',
|
|
47
56
|
}),
|
|
48
57
|
overwrite: Flags.boolean({
|
|
49
58
|
default: false,
|
|
50
|
-
description: '
|
|
59
|
+
description: 'overwrite existing data model configuration file (use with -y to force overwrite)',
|
|
60
|
+
}),
|
|
61
|
+
yes: Flags.boolean({
|
|
62
|
+
char: 'y',
|
|
63
|
+
description: 'accept all default values; non-interactive, never prompts; errors if required values are missing',
|
|
51
64
|
}),
|
|
52
65
|
};
|
|
53
|
-
static summary = '
|
|
66
|
+
static summary = 'Create or extend a data model configuration in the models folder.';
|
|
54
67
|
createType = ConfigType.MODEL;
|
|
55
68
|
async run() {
|
|
56
|
-
const { flags } = await this.parse(CreateModel);
|
|
69
|
+
const { args, flags } = await this.parse(CreateModel);
|
|
70
|
+
const confirmYes = Boolean(flags.yes);
|
|
71
|
+
// Helper validators
|
|
72
|
+
const validEventValues = ['created', 'updated', 'deleted'];
|
|
73
|
+
const validFieldTypes = [
|
|
74
|
+
'collection',
|
|
75
|
+
'currency',
|
|
76
|
+
'image',
|
|
77
|
+
'int',
|
|
78
|
+
'link',
|
|
79
|
+
'string',
|
|
80
|
+
];
|
|
81
|
+
const validateEvents = (values) => {
|
|
82
|
+
const invalid = values.filter((v) => !validEventValues.includes(v));
|
|
83
|
+
if (invalid.length > 0) {
|
|
84
|
+
this.error(`Invalid event(s): ${invalid.join(', ')}\n\nValid events: ${validEventValues.join(', ')}\n\nExample: swell create model visitors -e created,updated -y`, { exit: 1 });
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const validateFields = (pairs) => {
|
|
88
|
+
const invalid = [];
|
|
89
|
+
for (const pair of pairs) {
|
|
90
|
+
if (!pair.includes(':')) {
|
|
91
|
+
invalid.push(pair);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const type = pair.split(':')[1]?.trim();
|
|
95
|
+
if (!validFieldTypes.includes(type)) {
|
|
96
|
+
invalid.push(pair);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (invalid.length > 0) {
|
|
100
|
+
this.error(`Invalid field(s): ${invalid.join(', ')}\n\nValid types: ${validFieldTypes.join(', ')}\n\nExample: swell create model visitors -f title:string,price:currency -y`, { exit: 1 });
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
if (confirmYes) {
|
|
104
|
+
const argCollection = args.collection;
|
|
105
|
+
if (!argCollection) {
|
|
106
|
+
this.error('Missing required argument for non-interactive mode: COLLECTION\n\nExample: swell create model products -y\n\nTip: run `swell inspect models` to list standard collections you can extend.', { exit: 1 });
|
|
107
|
+
}
|
|
108
|
+
const collection = toCollectionName(argCollection);
|
|
109
|
+
const label = flags.label || toCollectionLabel(collection);
|
|
110
|
+
const description = flags.description || '';
|
|
111
|
+
const eventsRaw = (flags.events || 'created,updated,deleted');
|
|
112
|
+
const eventsList = eventsRaw
|
|
113
|
+
.split(',')
|
|
114
|
+
.map((v) => v.trim())
|
|
115
|
+
.filter(Boolean);
|
|
116
|
+
validateEvents(eventsList);
|
|
117
|
+
const fieldsRaw = (flags.fields || '');
|
|
118
|
+
const fieldPairs = fieldsRaw
|
|
119
|
+
? fieldsRaw
|
|
120
|
+
.split(',')
|
|
121
|
+
.map((v) => v.trim())
|
|
122
|
+
.filter(Boolean)
|
|
123
|
+
: [];
|
|
124
|
+
validateFields(fieldPairs);
|
|
125
|
+
const fileName = toFileName(collection);
|
|
126
|
+
const fileBody = {
|
|
127
|
+
$schema: SCHEMAS.MODEL,
|
|
128
|
+
collection,
|
|
129
|
+
description,
|
|
130
|
+
fields: parseFields(fieldPairs),
|
|
131
|
+
label,
|
|
132
|
+
events: parseEvents(eventsList),
|
|
133
|
+
};
|
|
134
|
+
await this.createFile({ fileBody, fileName }, flags.overwrite,
|
|
135
|
+
/* shouldConfirm */ false);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Interactive flow (prompts)
|
|
57
139
|
const { collection, description, label } = await this.promptCollection();
|
|
58
140
|
const fileName = toFileName(collection);
|
|
59
141
|
const fileBody = {
|
|
142
|
+
$schema: SCHEMAS.MODEL,
|
|
60
143
|
collection,
|
|
61
144
|
};
|
|
62
145
|
const { events, fields, overwrite } = flags;
|
|
63
|
-
if (label)
|
|
146
|
+
if (label)
|
|
64
147
|
fileBody.label = label;
|
|
65
|
-
}
|
|
66
148
|
fileBody.description = description;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
149
|
+
if (fields) {
|
|
150
|
+
const pairs = fields
|
|
151
|
+
.split(',')
|
|
152
|
+
.map((v) => v.trim())
|
|
153
|
+
.filter(Boolean);
|
|
154
|
+
validateFields(pairs);
|
|
155
|
+
fileBody.fields = parseFields(pairs);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
fileBody.fields = {};
|
|
159
|
+
}
|
|
160
|
+
// If events were provided via flag, use them; otherwise prompt
|
|
161
|
+
if (events && events !== 'created,updated,deleted') {
|
|
162
|
+
const eventsList = events
|
|
163
|
+
.split(',')
|
|
164
|
+
.map((v) => v.trim())
|
|
165
|
+
.filter(Boolean);
|
|
166
|
+
validateEvents(eventsList);
|
|
167
|
+
fileBody.events = parseEvents(eventsList);
|
|
70
168
|
}
|
|
71
169
|
else {
|
|
72
170
|
const eventsInput = await checkbox({
|
|
@@ -79,9 +177,6 @@ export default class CreateModel extends CreateCollectionCommand {
|
|
|
79
177
|
});
|
|
80
178
|
fileBody.events = parseEvents(eventsInput);
|
|
81
179
|
}
|
|
82
|
-
await this.createFile({
|
|
83
|
-
fileBody,
|
|
84
|
-
fileName,
|
|
85
|
-
}, overwrite);
|
|
180
|
+
await this.createFile({ fileBody, fileName }, overwrite);
|
|
86
181
|
}
|
|
87
182
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { AppCommand } from '../../app-command.js';
|
|
2
|
+
export default class CreateTests extends AppCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static summary: string;
|
|
5
|
+
static examples: {
|
|
6
|
+
command: string;
|
|
7
|
+
description: string;
|
|
8
|
+
}[];
|
|
9
|
+
static flags: {
|
|
10
|
+
overwrite: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
|
+
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
12
|
+
};
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
private logScaffoldResult;
|
|
15
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Flags } from '@oclif/core';
|
|
2
|
+
import { AppCommand } from '../../app-command.js';
|
|
3
|
+
import { toAppId } from '../../lib/create/index.js';
|
|
4
|
+
import { createTestsScaffold, } from '../../lib/create/tests.js';
|
|
5
|
+
export default class CreateTests extends AppCommand {
|
|
6
|
+
static description = 'Initialize a vitest + Cloudflare Workers test setup that reuses swell-cli authentication.';
|
|
7
|
+
static summary = 'Create test scaffolding for your Swell app.';
|
|
8
|
+
static examples = [
|
|
9
|
+
{
|
|
10
|
+
command: 'swell create tests',
|
|
11
|
+
description: 'Create a basic test setup using TypeScript.',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
command: 'swell create tests --overwrite',
|
|
15
|
+
description: 'Regenerate test files, overwriting existing ones.',
|
|
16
|
+
},
|
|
17
|
+
];
|
|
18
|
+
static flags = {
|
|
19
|
+
overwrite: Flags.boolean({
|
|
20
|
+
default: false,
|
|
21
|
+
description: 'overwrite existing test files if they already exist',
|
|
22
|
+
}),
|
|
23
|
+
yes: Flags.boolean({
|
|
24
|
+
char: 'y',
|
|
25
|
+
default: false,
|
|
26
|
+
description: 'accept defaults, skip prompts (no prompts currently, included for consistency)',
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
async run() {
|
|
30
|
+
const { flags } = await this.parse(CreateTests);
|
|
31
|
+
const appIdFromConfig = (this.swellConfig.get('id') ||
|
|
32
|
+
this.swellConfig.get('private_id') ||
|
|
33
|
+
this.swellConfig.get('public_id'));
|
|
34
|
+
const appId = appIdFromConfig ||
|
|
35
|
+
toAppId(this.swellConfig.get('name')) ||
|
|
36
|
+
'app';
|
|
37
|
+
const result = await createTestsScaffold({
|
|
38
|
+
appPath: this.appPath,
|
|
39
|
+
appId,
|
|
40
|
+
overwrite: Boolean(flags.overwrite),
|
|
41
|
+
});
|
|
42
|
+
this.logScaffoldResult(result);
|
|
43
|
+
}
|
|
44
|
+
logScaffoldResult(result) {
|
|
45
|
+
const hasChanges = result.createdFiles.length > 0 || result.packageJsonUpdated;
|
|
46
|
+
if (hasChanges) {
|
|
47
|
+
this.log('');
|
|
48
|
+
this.log('✓ Created test scaffolding');
|
|
49
|
+
}
|
|
50
|
+
if (result.createdFiles.length > 0) {
|
|
51
|
+
this.log('');
|
|
52
|
+
this.log('Created:');
|
|
53
|
+
for (const file of result.createdFiles) {
|
|
54
|
+
this.log(` ${file}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (result.skippedFiles.length > 0) {
|
|
58
|
+
this.log('');
|
|
59
|
+
this.log('Skipped (already exist):');
|
|
60
|
+
for (const file of result.skippedFiles) {
|
|
61
|
+
this.log(` ${file}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (result.packageJsonUpdated) {
|
|
65
|
+
this.log('');
|
|
66
|
+
this.log('Updated package.json with test scripts and devDependencies.');
|
|
67
|
+
}
|
|
68
|
+
for (const warning of result.warnings) {
|
|
69
|
+
this.warn(warning);
|
|
70
|
+
}
|
|
71
|
+
if (hasChanges) {
|
|
72
|
+
this.log('');
|
|
73
|
+
this.log('Next steps:');
|
|
74
|
+
this.log(' 1. npm install');
|
|
75
|
+
this.log(' 2. npm test');
|
|
76
|
+
}
|
|
77
|
+
this.log('');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SwellCommand } from '../../swell-command.js';
|
|
2
|
+
export default class Content extends SwellCommand {
|
|
3
|
+
static summary: string;
|
|
4
|
+
static description: string;
|
|
5
|
+
static args: {
|
|
6
|
+
'content-id': import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
live: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
|
+
yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
static examples: string[];
|
|
13
|
+
run(): Promise<void>;
|
|
14
|
+
protected catch(error: Error): Promise<any>;
|
|
15
|
+
private listContent;
|
|
16
|
+
private showContentViews;
|
|
17
|
+
private showContentView;
|
|
18
|
+
private resolveContentId;
|
|
19
|
+
private showContentDetail;
|
|
20
|
+
private reverseResolveContentId;
|
|
21
|
+
private getAppBySlug;
|
|
22
|
+
private getAppSlugId;
|
|
23
|
+
private throwContentNotFound;
|
|
24
|
+
private showContent;
|
|
25
|
+
}
|