@swell/cli
Version:
Swell's command line interface/utility
171 lines (170 loc) • 6.87 kB
JavaScript
import { checkbox } from '@inquirer/prompts';
import { Args, Flags } from '@oclif/core';
import { CreateCollectionCommand } from '../../create-collection-command.js';
import { ConfigType } from '../../lib/apps/index.js';
import { toCollectionLabel, toCollectionName, toFileName, } from '../../lib/create/index.js';
import { parseEvents, parseFields, } from '../../lib/create/model.js';
import { SCHEMAS } from '../../lib/create/schemas.js';
export default class CreateModel extends CreateCollectionCommand {
static args = {
collection: Args.string({
default: '',
description: 'Collection ID to create or extend (e.g., products)',
}),
};
static description = 'Interactive mode lets you choose to create a new collection or extend a standard one.\nTo discover standard collections: swell inspect models';
static examples = [
'$ swell create model',
'$ swell create model products -y',
'$ swell create model visitors -f title:string,photo:string -l "Visitors" -y',
];
static helpMeta = {
usageDirect: '<collection> [...] -y',
};
static flags = {
label: Flags.string({
char: 'l',
default: '',
description: 'Display label (default: capitalized collection)',
}),
description: Flags.string({
char: 'd',
default: '',
description: 'Description',
}),
fields: Flags.string({
char: 'f',
default: '',
description: 'Fields as id:type pairs (e.g., title:string,price:currency)',
}),
events: Flags.string({
char: 'e',
default: 'created,updated,deleted',
description: 'Events to track (default: created,updated,deleted)',
}),
overwrite: Flags.boolean({
default: false,
description: 'Overwrite existing file',
}),
yes: Flags.boolean({
char: 'y',
description: 'Skip prompts, require all arguments',
}),
};
static summary = 'Create or extend a data model configuration in the models folder.';
createType = ConfigType.MODEL;
async run() {
const { args, flags } = await this.parse(CreateModel);
const confirmYes = Boolean(flags.yes);
// Helper validators
const validEventValues = ['created', 'updated', 'deleted'];
const validFieldTypes = [
'collection',
'currency',
'image',
'int',
'link',
'string',
];
const validateEvents = (values) => {
const invalid = values.filter((v) => !validEventValues.includes(v));
if (invalid.length > 0) {
this.error(`Invalid event(s): ${invalid.join(', ')}\n\nValid events: ${validEventValues.join(', ')}\n\nExample: swell create model visitors -e created,updated -y`, { exit: 1 });
}
};
const validateFields = (pairs) => {
const invalid = [];
for (const pair of pairs) {
if (!pair.includes(':')) {
invalid.push(pair);
continue;
}
const type = pair.split(':')[1]?.trim();
if (!validFieldTypes.includes(type)) {
invalid.push(pair);
}
}
if (invalid.length > 0) {
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 });
}
};
if (confirmYes) {
const argCollection = args.collection;
if (!argCollection) {
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 });
}
const collection = toCollectionName(argCollection);
const label = flags.label || toCollectionLabel(collection);
const description = flags.description || '';
const eventsRaw = (flags.events || 'created,updated,deleted');
const eventsList = eventsRaw
.split(',')
.map((v) => v.trim())
.filter(Boolean);
validateEvents(eventsList);
const fieldsRaw = (flags.fields || '');
const fieldPairs = fieldsRaw
? fieldsRaw
.split(',')
.map((v) => v.trim())
.filter(Boolean)
: [];
validateFields(fieldPairs);
const fileName = toFileName(collection);
const fileBody = {
$schema: SCHEMAS.MODEL,
collection,
description,
fields: parseFields(fieldPairs),
label,
events: parseEvents(eventsList),
};
await this.createFile({ fileBody, fileName }, flags.overwrite,
/* shouldConfirm */ false);
return;
}
// Interactive flow (prompts)
const { collection, description, label } = await this.promptCollection();
const fileName = toFileName(collection);
const fileBody = {
$schema: SCHEMAS.MODEL,
collection,
};
const { events, fields, overwrite } = flags;
if (label)
fileBody.label = label;
fileBody.description = description;
if (fields) {
const pairs = fields
.split(',')
.map((v) => v.trim())
.filter(Boolean);
validateFields(pairs);
fileBody.fields = parseFields(pairs);
}
else {
fileBody.fields = {};
}
// If events were provided via flag, use them; otherwise prompt
if (events && events !== 'created,updated,deleted') {
const eventsList = events
.split(',')
.map((v) => v.trim())
.filter(Boolean);
validateEvents(eventsList);
fileBody.events = parseEvents(eventsList);
}
else {
const eventsInput = await checkbox({
choices: [
{ name: 'created', value: 'created' },
{ name: 'updated', value: 'updated' },
{ name: 'deleted', value: 'deleted' },
],
message: 'Which events should be captured by default?',
});
fileBody.events = parseEvents(eventsInput);
}
await this.createFile({ fileBody, fileName }, overwrite);
}
}