@swell/cli 2.2.0 → 2.3.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/app-command.js +1 -1
- package/dist/commands/api/delete.js +4 -1
- package/dist/commands/api/get.js +1 -0
- package/dist/commands/api/index.js +4 -1
- package/dist/commands/api/post.js +1 -0
- package/dist/commands/api/put.js +1 -0
- package/dist/commands/app/dev.js +5 -3
- package/dist/commands/app/init.d.ts +8 -7
- package/dist/commands/app/init.js +36 -18
- package/dist/commands/app/pull.js +2 -2
- package/dist/commands/app/push.js +2 -2
- package/dist/commands/app/version.d.ts +1 -0
- package/dist/commands/app/version.js +14 -4
- package/dist/commands/create/app.d.ts +8 -4
- package/dist/commands/create/app.js +68 -51
- package/dist/commands/create/content.d.ts +5 -6
- package/dist/commands/create/content.js +22 -38
- package/dist/commands/create/function.d.ts +6 -7
- package/dist/commands/create/function.js +94 -31
- package/dist/commands/create/index.js +13 -1
- package/dist/commands/create/model.d.ts +5 -6
- package/dist/commands/create/model.js +21 -33
- package/dist/commands/create/notification.d.ts +9 -9
- package/dist/commands/create/notification.js +117 -95
- package/dist/commands/create/setting.d.ts +21 -0
- package/dist/commands/create/setting.js +120 -0
- package/dist/commands/create/tests.d.ts +14 -0
- package/dist/commands/create/tests.js +76 -0
- package/dist/commands/create/webhook.d.ts +22 -0
- package/dist/commands/create/webhook.js +176 -0
- package/dist/commands/inspect/content.js +1 -1
- package/dist/commands/schema.d.ts +1 -0
- package/dist/commands/schema.js +51 -5
- package/dist/commands/theme/init.d.ts +8 -4
- package/dist/commands/theme/init.js +21 -8
- package/dist/create-app-command.d.ts +1 -0
- package/dist/create-app-command.js +15 -10
- package/dist/create-config-command.js +2 -2
- package/dist/help/custom-help.d.ts +89 -0
- package/dist/help/custom-help.js +337 -0
- package/dist/help/types.d.ts +75 -0
- package/dist/help/types.js +1 -0
- package/dist/lib/apps/app-config.js +2 -2
- package/dist/lib/apps/index.d.ts +2 -1
- package/dist/lib/apps/index.js +21 -4
- package/dist/lib/apps/paths.js +7 -6
- package/dist/lib/create/notification.d.ts +1 -0
- package/dist/lib/create/schemas.d.ts +1 -0
- package/dist/lib/create/schemas.js +1 -0
- package/dist/lib/create/setting.d.ts +15 -0
- package/dist/lib/create/setting.js +27 -0
- package/dist/lib/create/tests/templates/env-dts.d.ts +1 -0
- package/dist/lib/create/tests/templates/env-dts.js +15 -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 +128 -0
- package/dist/lib/create/tests/templates/tsconfig.d.ts +1 -0
- package/dist/lib/create/tests/templates/tsconfig.js +17 -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 +122 -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/create/webhook.d.ts +32 -0
- package/dist/lib/create/webhook.js +52 -0
- package/dist/swell-api-command.d.ts +14 -0
- package/dist/swell-api-command.js +113 -7
- package/oclif.manifest.json +2879 -0
- package/package.json +2 -1
|
@@ -13,24 +13,26 @@ import style from './lib/style.js';
|
|
|
13
13
|
import { SwellCommand } from './swell-command.js';
|
|
14
14
|
const execAsync = promisify(exec);
|
|
15
15
|
export class CreateAppCommand extends SwellCommand {
|
|
16
|
+
// Command name used in error message examples; override in subclasses
|
|
17
|
+
commandExample = 'swell create app';
|
|
16
18
|
static baseFlags = {
|
|
17
19
|
frontend: Flags.string({
|
|
18
|
-
description:
|
|
20
|
+
description: 'Framework: astro | angular | hono | nuxt | nextjs | none',
|
|
19
21
|
options: getFrontendProjectSlugs(true, false),
|
|
20
22
|
}),
|
|
21
23
|
'storefront-app': Flags.string({
|
|
22
|
-
description:
|
|
24
|
+
description: 'Target storefront app ID',
|
|
23
25
|
}),
|
|
24
26
|
'integration-type': Flags.string({
|
|
25
|
-
description:
|
|
27
|
+
description: 'Integration: generic | payment | shipping | tax',
|
|
26
28
|
options: ['generic', 'payment', 'shipping', 'tax'],
|
|
27
29
|
}),
|
|
28
30
|
'integration-id': Flags.string({
|
|
29
|
-
description:
|
|
31
|
+
description: 'Service ID (e.g., card, fedex)',
|
|
30
32
|
}),
|
|
31
33
|
yes: Flags.boolean({
|
|
32
34
|
char: 'y',
|
|
33
|
-
description:
|
|
35
|
+
description: 'Skip prompts, require all arguments',
|
|
34
36
|
}),
|
|
35
37
|
};
|
|
36
38
|
devApi;
|
|
@@ -354,12 +356,12 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
354
356
|
getProjectType(frameworkType) {
|
|
355
357
|
const projectType = FrontendProjectTypes.find((type) => type.slug === frameworkType);
|
|
356
358
|
if (!projectType) {
|
|
357
|
-
this.error(`Could not find project type: ${frameworkType}\n\nValid values: ${getFrontendProjectValidValues(true, false)}\n\nExample:
|
|
359
|
+
this.error(`Could not find project type: ${frameworkType}\n\nValid values: ${getFrontendProjectValidValues(true, false)}\n\nExample: ${this.commandExample} reviews --type admin --frontend astro -y`, {
|
|
358
360
|
exit: 1,
|
|
359
361
|
});
|
|
360
362
|
}
|
|
361
363
|
if (!projectType.installCommand) {
|
|
362
|
-
this.error(`Project type ${projectType.name} cannot be installed (legacy type)\n\nValid values: ${getFrontendProjectValidValues(true, false)}\n\nExample:
|
|
364
|
+
this.error(`Project type ${projectType.name} cannot be installed (legacy type)\n\nValid values: ${getFrontendProjectValidValues(true, false)}\n\nExample: ${this.commandExample} reviews --type admin --frontend astro -y`, {
|
|
363
365
|
exit: 1,
|
|
364
366
|
});
|
|
365
367
|
}
|
|
@@ -378,9 +380,12 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
378
380
|
workspaces: ['frontend'],
|
|
379
381
|
devDependencies: {
|
|
380
382
|
'@swell/app-types': '^1.0.5',
|
|
383
|
+
typescript: '^5.9.3',
|
|
381
384
|
},
|
|
382
385
|
name,
|
|
383
|
-
scripts: {
|
|
386
|
+
scripts: {
|
|
387
|
+
typecheck: '([ -z "$(find functions -name \'*.ts\' 2>/dev/null | head -1)" ] || tsc --noEmit) && ([ ! -f test/tsconfig.json ] || tsc --noEmit -p test) && ([ ! -f frontend/tsconfig.json ] || tsc --noEmit -p frontend)',
|
|
388
|
+
},
|
|
384
389
|
version: config.get('version'),
|
|
385
390
|
};
|
|
386
391
|
const tsConfig = {
|
|
@@ -388,10 +393,10 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
388
393
|
lib: ['esnext', 'webworker'],
|
|
389
394
|
module: 'esnext',
|
|
390
395
|
target: 'esnext',
|
|
396
|
+
moduleResolution: 'bundler',
|
|
391
397
|
types: ['@swell/app-types'],
|
|
392
398
|
},
|
|
393
|
-
exclude: ['node_modules', 'frontend'],
|
|
394
|
-
include: ['**/*.ts'],
|
|
399
|
+
exclude: ['node_modules', 'frontend', 'test', 'vitest.config.ts'],
|
|
395
400
|
};
|
|
396
401
|
await writeJsonFile(path.join(configPath, 'package.json'), packageJson);
|
|
397
402
|
await writeJsonFile(path.join(configPath, 'tsconfig.json'), tsConfig);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { confirm } from '@inquirer/prompts';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { AppCommand } from './app-command.js';
|
|
4
|
-
import {
|
|
4
|
+
import { AllConfigPaths, filePathExists, writeFile, writeJsonFile, } from './lib/apps/index.js';
|
|
5
5
|
/**
|
|
6
6
|
* A base class for Swell CLI Create commands for file and input handling.
|
|
7
7
|
*
|
|
@@ -14,7 +14,7 @@ export class CreateConfigCommand extends AppCommand {
|
|
|
14
14
|
createType = '';
|
|
15
15
|
async createFile({ extension = 'json', fileBody, fileName }, overwrite, shouldConfirm = true) {
|
|
16
16
|
const isJson = extension === 'json';
|
|
17
|
-
const filePath = path.join(this.appPath,
|
|
17
|
+
const filePath = path.join(this.appPath, AllConfigPaths[this.createType.toUpperCase()], `${fileName}.${extension}`);
|
|
18
18
|
if (shouldConfirm) {
|
|
19
19
|
this.log(`\nCreating app ${this.createType} in ${filePath}`);
|
|
20
20
|
this.log(`\n${isJson ? JSON.stringify(fileBody, null, 4) : fileBody}\n`);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Command, Help } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Custom Help class for Swell CLI
|
|
4
|
+
*
|
|
5
|
+
* Renders help in a cleaner format with:
|
|
6
|
+
* - Two-line USAGE with "# interactive" comment
|
|
7
|
+
* - Grouped flags by category (type options vs general)
|
|
8
|
+
* - Custom sections for conditional dependencies (TYPE OPTIONS, APP TYPES, etc.)
|
|
9
|
+
*
|
|
10
|
+
* Commands opt-in to custom formatting by defining a static `helpMeta` property.
|
|
11
|
+
* Commands without helpMeta use standard oclif formatting.
|
|
12
|
+
*/
|
|
13
|
+
export default class CustomHelp extends Help {
|
|
14
|
+
/**
|
|
15
|
+
* Override showCommandHelp to prevent duplicate summary for commands with helpMeta.
|
|
16
|
+
* The default implementation logs summary separately, but our formatCommand includes it.
|
|
17
|
+
* @param command - The command to display help for
|
|
18
|
+
* @returns Promise that resolves when help is displayed
|
|
19
|
+
*/
|
|
20
|
+
showCommandHelp(command: Command.Loadable): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Override formatCommand to provide custom help output for commands with helpMeta.
|
|
23
|
+
* Falls back to default formatting for commands without helpMeta.
|
|
24
|
+
* @param command - The command to format help for
|
|
25
|
+
* @returns Formatted help text as a string
|
|
26
|
+
*/
|
|
27
|
+
protected formatCommand(command: Command.Loadable): string;
|
|
28
|
+
/**
|
|
29
|
+
* Get helpMeta from a command, handling the Command.Loadable type.
|
|
30
|
+
* The helpMeta is stored on the command class itself, accessible via the cached command.
|
|
31
|
+
* @param command - The command to extract helpMeta from
|
|
32
|
+
* @returns The helpMeta object if present on the command, otherwise undefined
|
|
33
|
+
*/
|
|
34
|
+
private getHelpMeta;
|
|
35
|
+
/**
|
|
36
|
+
* Format USAGE section with two-line pattern:
|
|
37
|
+
* - Line 1: Interactive mode (no args) with "# interactive" comment
|
|
38
|
+
* - Line 2: Direct/non-interactive mode with args
|
|
39
|
+
* @param command - The command to format usage for
|
|
40
|
+
* @param helpMeta - The help metadata for the command
|
|
41
|
+
* @returns Formatted USAGE section as an array of strings
|
|
42
|
+
*/
|
|
43
|
+
private formatUsageSection;
|
|
44
|
+
/**
|
|
45
|
+
* Auto-generate direct usage line from command definition.
|
|
46
|
+
* @param command - The command to generate direct usage for
|
|
47
|
+
* @returns The generated direct usage line as a string
|
|
48
|
+
*/
|
|
49
|
+
private generateDirectUsage;
|
|
50
|
+
/**
|
|
51
|
+
* Format ARGUMENTS section with compact display.
|
|
52
|
+
* @param command - The command to format arguments for
|
|
53
|
+
* @returns Formatted ARGUMENTS section as an array of strings
|
|
54
|
+
*/
|
|
55
|
+
private formatArgumentsSection;
|
|
56
|
+
/**
|
|
57
|
+
* Format TYPE section (APP TYPES, etc.) with requires/optional indicators.
|
|
58
|
+
* @param section - The type section configuration with types and their requirements
|
|
59
|
+
* @returns Formatted TYPE section as an array of strings
|
|
60
|
+
*/
|
|
61
|
+
private formatTypeSection;
|
|
62
|
+
/**
|
|
63
|
+
* Format VARIANT section (TRIGGER OPTIONS, etc.) - compact inline format.
|
|
64
|
+
* @param section - The variant section configuration with variants and their descriptions
|
|
65
|
+
* @returns Formatted VARIANT section as an array of strings
|
|
66
|
+
*/
|
|
67
|
+
private formatVariantSection;
|
|
68
|
+
/**
|
|
69
|
+
* Format FLAGS section with filtering and sorting.
|
|
70
|
+
* @param command - The command to format flags for
|
|
71
|
+
* @param title - The section title to display
|
|
72
|
+
* @param filter - Function to filter flags by name
|
|
73
|
+
* @returns Formatted FLAGS section as an array of strings
|
|
74
|
+
*/
|
|
75
|
+
private formatFlagsSection;
|
|
76
|
+
/**
|
|
77
|
+
* Format DESCRIPTION section - shows description if it differs from summary.
|
|
78
|
+
* Only shown when there's meaningful additional content.
|
|
79
|
+
* @param command - The command to format description for
|
|
80
|
+
* @returns Formatted DESCRIPTION section as an array of strings
|
|
81
|
+
*/
|
|
82
|
+
private formatDescriptionSection;
|
|
83
|
+
/**
|
|
84
|
+
* Format EXAMPLES section - clean, label-free examples.
|
|
85
|
+
* @param command - The command to format examples for
|
|
86
|
+
* @returns Formatted EXAMPLES section as an array of strings
|
|
87
|
+
*/
|
|
88
|
+
private formatExamplesSection;
|
|
89
|
+
}
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import { Help } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Custom Help class for Swell CLI
|
|
4
|
+
*
|
|
5
|
+
* Renders help in a cleaner format with:
|
|
6
|
+
* - Two-line USAGE with "# interactive" comment
|
|
7
|
+
* - Grouped flags by category (type options vs general)
|
|
8
|
+
* - Custom sections for conditional dependencies (TYPE OPTIONS, APP TYPES, etc.)
|
|
9
|
+
*
|
|
10
|
+
* Commands opt-in to custom formatting by defining a static `helpMeta` property.
|
|
11
|
+
* Commands without helpMeta use standard oclif formatting.
|
|
12
|
+
*/
|
|
13
|
+
export default class CustomHelp extends Help {
|
|
14
|
+
/**
|
|
15
|
+
* Override showCommandHelp to prevent duplicate summary for commands with helpMeta.
|
|
16
|
+
* The default implementation logs summary separately, but our formatCommand includes it.
|
|
17
|
+
* @param command - The command to display help for
|
|
18
|
+
* @returns Promise that resolves when help is displayed
|
|
19
|
+
*/
|
|
20
|
+
async showCommandHelp(command) {
|
|
21
|
+
const helpMeta = this.getHelpMeta(command);
|
|
22
|
+
if (helpMeta) {
|
|
23
|
+
// For commands with helpMeta, just log formatCommand output
|
|
24
|
+
// (which includes the description/summary already)
|
|
25
|
+
this.log(this.formatCommand(command));
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// For commands without helpMeta, use default behavior
|
|
29
|
+
await super.showCommandHelp(command);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Override formatCommand to provide custom help output for commands with helpMeta.
|
|
34
|
+
* Falls back to default formatting for commands without helpMeta.
|
|
35
|
+
* @param command - The command to format help for
|
|
36
|
+
* @returns Formatted help text as a string
|
|
37
|
+
*/
|
|
38
|
+
formatCommand(command) {
|
|
39
|
+
const helpMeta = this.getHelpMeta(command);
|
|
40
|
+
// No helpMeta - use default oclif formatting
|
|
41
|
+
if (!helpMeta) {
|
|
42
|
+
return super.formatCommand(command);
|
|
43
|
+
}
|
|
44
|
+
// Custom formatting for commands with helpMeta
|
|
45
|
+
const lines = [];
|
|
46
|
+
// Description (single line) - use summary if available, else first line of description
|
|
47
|
+
const desc = command.summary || command.description?.split('\n')[0];
|
|
48
|
+
if (desc) {
|
|
49
|
+
lines.push(desc, '');
|
|
50
|
+
}
|
|
51
|
+
// USAGE section
|
|
52
|
+
lines.push(...this.formatUsageSection(command, helpMeta));
|
|
53
|
+
// ARGUMENTS section
|
|
54
|
+
const argsSection = this.formatArgumentsSection(command);
|
|
55
|
+
if (argsSection.length > 0) {
|
|
56
|
+
lines.push('', ...argsSection);
|
|
57
|
+
}
|
|
58
|
+
// TYPE SECTION (e.g., APP TYPES) - for commands with type-based conditional deps
|
|
59
|
+
if (helpMeta.typeSection) {
|
|
60
|
+
lines.push('', ...this.formatTypeSection(helpMeta.typeSection));
|
|
61
|
+
}
|
|
62
|
+
// VARIANT SECTION (e.g., TRIGGER OPTIONS) - for simpler variant-based deps
|
|
63
|
+
if (helpMeta.variantSection) {
|
|
64
|
+
lines.push('', ...this.formatVariantSection(helpMeta.variantSection));
|
|
65
|
+
}
|
|
66
|
+
// TYPE-SPECIFIC FLAGS (when using typeSection)
|
|
67
|
+
if (helpMeta.typeFlags && helpMeta.typeFlags.length > 0) {
|
|
68
|
+
const typeSpecificFlags = this.formatFlagsSection(command, 'TYPE OPTIONS', (name) => helpMeta.typeFlags.includes(name));
|
|
69
|
+
if (typeSpecificFlags.length > 0) {
|
|
70
|
+
lines.push('', ...typeSpecificFlags);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// GENERAL FLAGS section
|
|
74
|
+
const excludeFlags = new Set([
|
|
75
|
+
...(helpMeta.variantFlags || []),
|
|
76
|
+
...(helpMeta.typeFlags || []),
|
|
77
|
+
]);
|
|
78
|
+
const flagsSection = this.formatFlagsSection(command, 'FLAGS', (name) => !excludeFlags.has(name));
|
|
79
|
+
if (flagsSection.length > 0) {
|
|
80
|
+
lines.push('', ...flagsSection);
|
|
81
|
+
}
|
|
82
|
+
// DESCRIPTION section (if description differs from summary)
|
|
83
|
+
const descSection = this.formatDescriptionSection(command);
|
|
84
|
+
if (descSection.length > 0) {
|
|
85
|
+
lines.push('', ...descSection);
|
|
86
|
+
}
|
|
87
|
+
// EXAMPLES section
|
|
88
|
+
const examplesSection = this.formatExamplesSection(command);
|
|
89
|
+
if (examplesSection.length > 0) {
|
|
90
|
+
lines.push('', ...examplesSection);
|
|
91
|
+
}
|
|
92
|
+
return lines.join('\n');
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get helpMeta from a command, handling the Command.Loadable type.
|
|
96
|
+
* The helpMeta is stored on the command class itself, accessible via the cached command.
|
|
97
|
+
* @param command - The command to extract helpMeta from
|
|
98
|
+
* @returns The helpMeta object if present on the command, otherwise undefined
|
|
99
|
+
*/
|
|
100
|
+
getHelpMeta(command) {
|
|
101
|
+
// Command.Loadable stores all static properties on the object itself
|
|
102
|
+
// We need to cast to access the helpMeta property
|
|
103
|
+
return command.helpMeta;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Format USAGE section with two-line pattern:
|
|
107
|
+
* - Line 1: Interactive mode (no args) with "# interactive" comment
|
|
108
|
+
* - Line 2: Direct/non-interactive mode with args
|
|
109
|
+
* @param command - The command to format usage for
|
|
110
|
+
* @param helpMeta - The help metadata for the command
|
|
111
|
+
* @returns Formatted USAGE section as an array of strings
|
|
112
|
+
*/
|
|
113
|
+
formatUsageSection(command, helpMeta) {
|
|
114
|
+
const lines = ['USAGE'];
|
|
115
|
+
// Convert colon separator to space (e.g., "create:app" -> "create app")
|
|
116
|
+
const cmdId = command.id.replaceAll(':', ' ');
|
|
117
|
+
// First line: interactive (no args)
|
|
118
|
+
const interactiveLine = ` $ swell ${cmdId}`;
|
|
119
|
+
const padding = Math.max(50 - interactiveLine.length, 2);
|
|
120
|
+
lines.push(`${interactiveLine}${' '.repeat(padding)}# interactive`);
|
|
121
|
+
// Second line: direct mode with args
|
|
122
|
+
if (helpMeta.usageDirect) {
|
|
123
|
+
lines.push(` $ swell ${cmdId} ${helpMeta.usageDirect}`);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
// Auto-generate from args and flags
|
|
127
|
+
const directLine = this.generateDirectUsage(command);
|
|
128
|
+
if (directLine !== interactiveLine) {
|
|
129
|
+
lines.push(directLine);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return lines;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Auto-generate direct usage line from command definition.
|
|
136
|
+
* @param command - The command to generate direct usage for
|
|
137
|
+
* @returns The generated direct usage line as a string
|
|
138
|
+
*/
|
|
139
|
+
generateDirectUsage(command) {
|
|
140
|
+
const cmdId = command.id.replaceAll(':', ' ');
|
|
141
|
+
const parts = [` $ swell ${cmdId}`];
|
|
142
|
+
// Add args
|
|
143
|
+
if (command.args) {
|
|
144
|
+
const argEntries = Object.entries(command.args);
|
|
145
|
+
for (const [name] of argEntries) {
|
|
146
|
+
parts.push(`<${name.toLowerCase()}>`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// Check if there are any optional flags
|
|
150
|
+
const flags = command.flags || {};
|
|
151
|
+
const hasOptionalFlags = Object.keys(flags).some((f) => f !== 'yes' && f !== 'help');
|
|
152
|
+
if (hasOptionalFlags) {
|
|
153
|
+
parts.push('[...]');
|
|
154
|
+
}
|
|
155
|
+
// Add -y if the command has it
|
|
156
|
+
if (flags.yes) {
|
|
157
|
+
parts.push('-y');
|
|
158
|
+
}
|
|
159
|
+
return parts.join(' ');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Format ARGUMENTS section with compact display.
|
|
163
|
+
* @param command - The command to format arguments for
|
|
164
|
+
* @returns Formatted ARGUMENTS section as an array of strings
|
|
165
|
+
*/
|
|
166
|
+
formatArgumentsSection(command) {
|
|
167
|
+
if (!command.args || Object.keys(command.args).length === 0) {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
const lines = ['ARGUMENTS'];
|
|
171
|
+
const argEntries = Object.entries(command.args);
|
|
172
|
+
// Calculate padding for alignment
|
|
173
|
+
const maxNameLength = Math.max(...argEntries.map(([name]) => name.length));
|
|
174
|
+
const padding = Math.max(maxNameLength + 4, 8);
|
|
175
|
+
for (const [name, arg] of argEntries) {
|
|
176
|
+
const argName = name.toUpperCase();
|
|
177
|
+
const desc = arg.description || '';
|
|
178
|
+
lines.push(` ${argName.padEnd(padding)}${desc}`);
|
|
179
|
+
}
|
|
180
|
+
return lines;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Format TYPE section (APP TYPES, etc.) with requires/optional indicators.
|
|
184
|
+
* @param section - The type section configuration with types and their requirements
|
|
185
|
+
* @returns Formatted TYPE section as an array of strings
|
|
186
|
+
*/
|
|
187
|
+
formatTypeSection(section) {
|
|
188
|
+
if (!section)
|
|
189
|
+
return [];
|
|
190
|
+
const lines = [section.title];
|
|
191
|
+
for (let i = 0; i < section.types.length; i++) {
|
|
192
|
+
const type = section.types[i];
|
|
193
|
+
lines.push(` ${type.name.padEnd(12)} ${type.description}`);
|
|
194
|
+
if (type.requires) {
|
|
195
|
+
for (const req of type.requires) {
|
|
196
|
+
lines.push(`${''.padEnd(15)}Requires: ${req}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (type.optional) {
|
|
200
|
+
for (const opt of type.optional) {
|
|
201
|
+
lines.push(`${''.padEnd(15)}Optional: ${opt}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Add blank line between types for readability (except last)
|
|
205
|
+
if (i < section.types.length - 1) {
|
|
206
|
+
lines.push('');
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return lines;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Format VARIANT section (TRIGGER OPTIONS, etc.) - compact inline format.
|
|
213
|
+
* @param section - The variant section configuration with variants and their descriptions
|
|
214
|
+
* @returns Formatted VARIANT section as an array of strings
|
|
215
|
+
*/
|
|
216
|
+
formatVariantSection(section) {
|
|
217
|
+
if (!section)
|
|
218
|
+
return [];
|
|
219
|
+
const lines = [section.title];
|
|
220
|
+
for (const variant of section.variants) {
|
|
221
|
+
// Format: " model -e, --events=<value> Description"
|
|
222
|
+
const flagPart = variant.flag.padEnd(28);
|
|
223
|
+
lines.push(` ${variant.name.padEnd(8)} ${flagPart} ${variant.description}`);
|
|
224
|
+
}
|
|
225
|
+
return lines;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Format FLAGS section with filtering and sorting.
|
|
229
|
+
* @param command - The command to format flags for
|
|
230
|
+
* @param title - The section title to display
|
|
231
|
+
* @param filter - Function to filter flags by name
|
|
232
|
+
* @returns Formatted FLAGS section as an array of strings
|
|
233
|
+
*/
|
|
234
|
+
formatFlagsSection(command, title, filter) {
|
|
235
|
+
if (!command.flags) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
const flagEntries = Object.entries(command.flags).filter(([name, flag]) => filter(name) && name !== 'help' && !flag.hidden);
|
|
239
|
+
if (flagEntries.length === 0) {
|
|
240
|
+
return [];
|
|
241
|
+
}
|
|
242
|
+
const lines = [title];
|
|
243
|
+
// Sort flags: short flags first, then long-only flags, -y last
|
|
244
|
+
flagEntries.sort(([aName, aFlag], [bName, bFlag]) => {
|
|
245
|
+
// -y always last
|
|
246
|
+
if (aName === 'yes')
|
|
247
|
+
return 1;
|
|
248
|
+
if (bName === 'yes')
|
|
249
|
+
return -1;
|
|
250
|
+
// Flags with short char before flags without
|
|
251
|
+
const aHasChar = Boolean(aFlag.char);
|
|
252
|
+
const bHasChar = Boolean(bFlag.char);
|
|
253
|
+
if (aHasChar && !bHasChar)
|
|
254
|
+
return -1;
|
|
255
|
+
if (!aHasChar && bHasChar)
|
|
256
|
+
return 1;
|
|
257
|
+
// Alphabetical
|
|
258
|
+
return aName.localeCompare(bName);
|
|
259
|
+
});
|
|
260
|
+
for (const [name, flag] of flagEntries) {
|
|
261
|
+
const shortChar = flag.char ? `-${flag.char}, ` : ' ';
|
|
262
|
+
const longFlag = `--${name}`;
|
|
263
|
+
const isBoolean = flag.type === 'boolean';
|
|
264
|
+
const hasOptions = 'options' in flag && flag.options;
|
|
265
|
+
const valueStr = isBoolean ? '' : hasOptions ? '=<option>' : '=<value>';
|
|
266
|
+
const flagStr = `${shortChar}${longFlag}${valueStr}`;
|
|
267
|
+
// Get description, clean up redundant "(required in -y)" text
|
|
268
|
+
let desc = flag.summary || flag.description || '';
|
|
269
|
+
desc = desc
|
|
270
|
+
.replaceAll(/\s*\(required (?:with|in) -y\)/gi, '')
|
|
271
|
+
.replaceAll(/\s*\(required for .+ with -y\)/gi, '')
|
|
272
|
+
.replaceAll(/\s*\(default in -y: .+\)/gi, '')
|
|
273
|
+
.split('\n')[0]
|
|
274
|
+
.trim();
|
|
275
|
+
lines.push(` ${flagStr.padEnd(30)} ${desc}`);
|
|
276
|
+
}
|
|
277
|
+
return lines;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Format DESCRIPTION section - shows description if it differs from summary.
|
|
281
|
+
* Only shown when there's meaningful additional content.
|
|
282
|
+
* @param command - The command to format description for
|
|
283
|
+
* @returns Formatted DESCRIPTION section as an array of strings
|
|
284
|
+
*/
|
|
285
|
+
formatDescriptionSection(command) {
|
|
286
|
+
// Get description (may be multi-line)
|
|
287
|
+
const { description } = command;
|
|
288
|
+
if (!description) {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
// Skip if description equals summary (no additional info)
|
|
292
|
+
const summary = command.summary || description.split('\n')[0];
|
|
293
|
+
if (description === summary) {
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
const lines = ['DESCRIPTION'];
|
|
297
|
+
// Format each line of description with proper indentation
|
|
298
|
+
const descLines = description.split('\n');
|
|
299
|
+
for (const line of descLines) {
|
|
300
|
+
lines.push(` ${line}`);
|
|
301
|
+
}
|
|
302
|
+
return lines;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Format EXAMPLES section - clean, label-free examples.
|
|
306
|
+
* @param command - The command to format examples for
|
|
307
|
+
* @returns Formatted EXAMPLES section as an array of strings
|
|
308
|
+
*/
|
|
309
|
+
formatExamplesSection(command) {
|
|
310
|
+
if (!command.examples || command.examples.length === 0) {
|
|
311
|
+
return [];
|
|
312
|
+
}
|
|
313
|
+
const lines = ['EXAMPLES'];
|
|
314
|
+
for (const example of command.examples) {
|
|
315
|
+
if (typeof example === 'string') {
|
|
316
|
+
// String example - check if it's a command
|
|
317
|
+
if (example.trim().startsWith('$')) {
|
|
318
|
+
lines.push(` ${example.trim()}`);
|
|
319
|
+
}
|
|
320
|
+
else if (example.includes('<%= command.id %>')) {
|
|
321
|
+
// oclif template syntax
|
|
322
|
+
const cmd = example.replace('<%= command.id %>', command.id);
|
|
323
|
+
lines.push(` $ swell ${cmd.trim()}`);
|
|
324
|
+
}
|
|
325
|
+
// Skip description-only lines
|
|
326
|
+
}
|
|
327
|
+
else if (typeof example === 'object' && example.command) {
|
|
328
|
+
// Object example with command property
|
|
329
|
+
const cmd = example.command.startsWith('swell ')
|
|
330
|
+
? example.command
|
|
331
|
+
: `swell ${example.command}`;
|
|
332
|
+
lines.push(` $ ${cmd}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return lines;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a type variant with conditional flag requirements.
|
|
4
|
+
* Used for complex commands like `create app` where different types
|
|
5
|
+
* have different required/optional flags.
|
|
6
|
+
*/
|
|
7
|
+
export interface TypeVariant {
|
|
8
|
+
/** Type name (e.g., 'admin', 'theme', 'integration') */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Short description of what this type does */
|
|
11
|
+
description: string;
|
|
12
|
+
/** Flags required when using this type */
|
|
13
|
+
requires?: string[];
|
|
14
|
+
/** Flags optionally available for this type */
|
|
15
|
+
optional?: string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Represents a simpler variant option where one argument maps to one flag.
|
|
19
|
+
* Used for commands like `create function` where trigger type determines the flag.
|
|
20
|
+
*/
|
|
21
|
+
export interface VariantOption {
|
|
22
|
+
/** Variant name (e.g., 'model', 'cron', 'route') */
|
|
23
|
+
name: string;
|
|
24
|
+
/** Flag signature (e.g., '-e, --events=<value>') */
|
|
25
|
+
flag: string;
|
|
26
|
+
/** Description of what this flag does */
|
|
27
|
+
description: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Metadata for custom help formatting.
|
|
31
|
+
* Add this as a static property on commands that need enhanced help output.
|
|
32
|
+
*/
|
|
33
|
+
export interface HelpMeta {
|
|
34
|
+
/**
|
|
35
|
+
* Custom usage line for direct/non-interactive mode.
|
|
36
|
+
* Example: '<id> -t <type> [...] -y'
|
|
37
|
+
*/
|
|
38
|
+
usageDirect?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Type section for complex conditional dependencies.
|
|
41
|
+
* Renders as a detailed breakdown showing each type with its requirements.
|
|
42
|
+
*/
|
|
43
|
+
typeSection?: {
|
|
44
|
+
/** Section title (e.g., 'APP TYPES') */
|
|
45
|
+
title: string;
|
|
46
|
+
/** List of type variants */
|
|
47
|
+
types: TypeVariant[];
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Flags to group into a separate "TYPE OPTIONS" section.
|
|
51
|
+
* These are excluded from the general FLAGS section.
|
|
52
|
+
*/
|
|
53
|
+
typeFlags?: string[];
|
|
54
|
+
/**
|
|
55
|
+
* Variant section for simpler conditional dependencies.
|
|
56
|
+
* Renders as a compact table: variant -> flag -> description
|
|
57
|
+
*/
|
|
58
|
+
variantSection?: {
|
|
59
|
+
/** Section title (e.g., 'TRIGGER OPTIONS') */
|
|
60
|
+
title: string;
|
|
61
|
+
/** List of variant options */
|
|
62
|
+
variants: VariantOption[];
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Flags to exclude from the general FLAGS section when using variantSection.
|
|
66
|
+
*/
|
|
67
|
+
variantFlags?: string[];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Command class with optional helpMeta static property.
|
|
71
|
+
* Uses Command.Cached since that's what Help receives from Command.Loadable.
|
|
72
|
+
*/
|
|
73
|
+
export type CommandClassWithMeta = Command.Cached & {
|
|
74
|
+
helpMeta?: HelpMeta;
|
|
75
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -4,7 +4,7 @@ import { detectFilenameMime } from 'mime-detect';
|
|
|
4
4
|
import * as fs from 'node:fs';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import { bundleFunction } from '../bundle.js';
|
|
7
|
-
import {
|
|
7
|
+
import { AllConfigPaths, ConfigType, filePathExists, hashFile, } from './index.js';
|
|
8
8
|
export class IgnoringFileError extends Error {
|
|
9
9
|
constructor(message) {
|
|
10
10
|
super(message);
|
|
@@ -68,7 +68,7 @@ export class AppConfig {
|
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
70
|
// Note this shouldn't be necessary unless the config was created before file_path was added
|
|
71
|
-
const typePath =
|
|
71
|
+
const typePath = AllConfigPaths[String(type).toUpperCase()];
|
|
72
72
|
filePath = `${typePath}/${name}`;
|
|
73
73
|
if (!typePath) {
|
|
74
74
|
throw new Error(`Missing path for app config type: ${type}`);
|
package/dist/lib/apps/index.d.ts
CHANGED
|
@@ -106,7 +106,8 @@ export declare const ThemeConfigTypes: ConfigTypeKey[];
|
|
|
106
106
|
export type ConfigPathsType = {
|
|
107
107
|
[key in ConfigTypeKey]: string;
|
|
108
108
|
};
|
|
109
|
-
export declare const
|
|
109
|
+
export declare const AllConfigPaths: ConfigPathsType;
|
|
110
|
+
export declare const StandardConfigPaths: ConfigPathsType;
|
|
110
111
|
export declare const StorefrontConfigPaths: ConfigPathsType;
|
|
111
112
|
export declare const ThemeConfigPaths: ConfigPathsType;
|
|
112
113
|
export declare enum ConfigInputFields {
|