@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.
Files changed (112) hide show
  1. package/dist/commands/api/delete.d.ts +15 -0
  2. package/dist/commands/api/delete.js +26 -0
  3. package/dist/commands/api/get.d.ts +15 -0
  4. package/dist/commands/api/get.js +31 -0
  5. package/dist/commands/api/index.d.ts +7 -0
  6. package/dist/commands/api/index.js +16 -0
  7. package/dist/commands/api/post.d.ts +16 -0
  8. package/dist/commands/api/post.js +33 -0
  9. package/dist/commands/api/put.d.ts +16 -0
  10. package/dist/commands/api/put.js +33 -0
  11. package/dist/commands/app/dev.d.ts +9 -9
  12. package/dist/commands/app/dev.js +163 -139
  13. package/dist/commands/app/frontend/dev.d.ts +1 -1
  14. package/dist/commands/app/frontend/dev.js +84 -78
  15. package/dist/commands/app/install.js +3 -3
  16. package/dist/commands/app/pull.d.ts +1 -1
  17. package/dist/commands/app/pull.js +6 -6
  18. package/dist/commands/app/push.d.ts +3 -3
  19. package/dist/commands/app/push.js +8 -6
  20. package/dist/commands/app/release.d.ts +1 -1
  21. package/dist/commands/app/release.js +1 -1
  22. package/dist/commands/app/version.d.ts +1 -1
  23. package/dist/commands/app/version.js +5 -3
  24. package/dist/commands/create/app.d.ts +10 -2
  25. package/dist/commands/create/app.js +226 -57
  26. package/dist/commands/create/content.d.ts +4 -0
  27. package/dist/commands/create/content.js +83 -15
  28. package/dist/commands/create/function.js +27 -5
  29. package/dist/commands/create/index.js +5 -1
  30. package/dist/commands/create/model.d.ts +4 -0
  31. package/dist/commands/create/model.js +120 -25
  32. package/dist/commands/create/tests.d.ts +15 -0
  33. package/dist/commands/create/tests.js +79 -0
  34. package/dist/commands/inspect/content.d.ts +25 -0
  35. package/dist/commands/inspect/content.js +159 -0
  36. package/dist/commands/inspect/index.d.ts +7 -0
  37. package/dist/commands/inspect/index.js +16 -0
  38. package/dist/commands/inspect/models.d.ts +27 -0
  39. package/dist/commands/inspect/models.js +197 -0
  40. package/dist/commands/schema.d.ts +27 -0
  41. package/dist/commands/schema.js +231 -0
  42. package/dist/commands/theme/dev.d.ts +1 -1
  43. package/dist/commands/theme/dev.js +2 -2
  44. package/dist/commands/theme/init.d.ts +1 -1
  45. package/dist/commands/theme/init.js +1 -1
  46. package/dist/commands/theme/pull.d.ts +2 -2
  47. package/dist/commands/theme/pull.js +2 -2
  48. package/dist/commands/theme/push.d.ts +1 -1
  49. package/dist/commands/theme/push.js +1 -1
  50. package/dist/create-app-command.d.ts +12 -2
  51. package/dist/create-app-command.js +103 -80
  52. package/dist/create-collection-command.js +4 -1
  53. package/dist/create-config-command.js +4 -0
  54. package/dist/env/local.d.ts +1 -0
  55. package/dist/env/local.js +1 -0
  56. package/dist/env/production.d.ts +1 -0
  57. package/dist/env/production.js +1 -0
  58. package/dist/env/review.d.ts +1 -0
  59. package/dist/env/review.js +1 -0
  60. package/dist/env/staging.d.ts +1 -0
  61. package/dist/env/staging.js +1 -0
  62. package/dist/lib/api.d.ts +16 -5
  63. package/dist/lib/api.js +67 -25
  64. package/dist/lib/app-config.js +1 -0
  65. package/dist/lib/apps/index.d.ts +2 -0
  66. package/dist/lib/apps/index.js +20 -14
  67. package/dist/lib/config.d.ts +1 -1
  68. package/dist/lib/constants.d.ts +1 -0
  69. package/dist/lib/constants.js +1 -0
  70. package/dist/lib/create/content.d.ts +5 -1
  71. package/dist/lib/create/content.js +8 -1
  72. package/dist/lib/create/function.d.ts +2 -1
  73. package/dist/lib/create/function.js +7 -3
  74. package/dist/lib/create/model.d.ts +1 -0
  75. package/dist/lib/create/schemas.d.ts +6 -0
  76. package/dist/lib/create/schemas.js +6 -0
  77. package/dist/lib/create/tests/templates/env-dts.d.ts +1 -0
  78. package/dist/lib/create/tests/templates/env-dts.js +14 -0
  79. package/dist/lib/create/tests/templates/index.d.ts +8 -0
  80. package/dist/lib/create/tests/templates/index.js +8 -0
  81. package/dist/lib/create/tests/templates/integration-test.d.ts +1 -0
  82. package/dist/lib/create/tests/templates/integration-test.js +19 -0
  83. package/dist/lib/create/tests/templates/mock-request.d.ts +2 -0
  84. package/dist/lib/create/tests/templates/mock-request.js +112 -0
  85. package/dist/lib/create/tests/templates/setup-globals.d.ts +1 -0
  86. package/dist/lib/create/tests/templates/setup-globals.js +23 -0
  87. package/dist/lib/create/tests/templates/swell-client.d.ts +1 -0
  88. package/dist/lib/create/tests/templates/swell-client.js +126 -0
  89. package/dist/lib/create/tests/templates/tsconfig.d.ts +1 -0
  90. package/dist/lib/create/tests/templates/tsconfig.js +15 -0
  91. package/dist/lib/create/tests/templates/unit-test.d.ts +1 -0
  92. package/dist/lib/create/tests/templates/unit-test.js +42 -0
  93. package/dist/lib/create/tests/templates/vitest-config.d.ts +2 -0
  94. package/dist/lib/create/tests/templates/vitest-config.js +121 -0
  95. package/dist/lib/create/tests/types.d.ts +21 -0
  96. package/dist/lib/create/tests/types.js +1 -0
  97. package/dist/lib/create/tests.d.ts +4 -0
  98. package/dist/lib/create/tests.js +113 -0
  99. package/dist/lib/sessions.js +4 -3
  100. package/dist/lib/style.js +0 -1
  101. package/dist/lib/theme-sync.d.ts +6 -6
  102. package/dist/lib/theme-sync.js +49 -39
  103. package/dist/push-app-command.d.ts +7 -7
  104. package/dist/push-app-command.js +38 -36
  105. package/dist/remote-app-command.d.ts +1 -1
  106. package/dist/remote-app-command.js +4 -2
  107. package/dist/swell-api-command.d.ts +13 -0
  108. package/dist/swell-api-command.js +75 -0
  109. package/dist/swell-command.d.ts +1 -1
  110. package/dist/swell-command.js +9 -9
  111. package/oclif.manifest.json +639 -46
  112. package/package.json +8 -1
@@ -13,6 +13,6 @@ export default class AppPull extends PushAppCommand {
13
13
  env: string;
14
14
  };
15
15
  static summary: string;
16
- run(): Promise<void>;
17
16
  logPulledChanges(pulled: string[], removed: string[]): void;
17
+ run(): Promise<void>;
18
18
  }
@@ -36,6 +36,12 @@ ${Object.values(ConfigPaths)
36
36
  env: 'test',
37
37
  };
38
38
  static summary = 'Pull app files from Swell to your local machine.';
39
+ logPulledChanges(pulled, removed) {
40
+ const typeLabel = this.appType === 'theme' ? 'theme' : 'app';
41
+ const changeCount = pulled.length + removed.length;
42
+ const pluralFiles = changeCount === 1 ? 'file' : 'files';
43
+ this.log(`Pulled ${changeCount} ${pluralFiles} from ${style.appConfigValue(this.app.name)} ${typeLabel} in ${style.path(path.resolve(this.appPath))}.`);
44
+ }
39
45
  async run() {
40
46
  const { args, flags } = await this.parse(AppPull);
41
47
  const { force } = flags;
@@ -44,10 +50,4 @@ ${Object.values(ConfigPaths)
44
50
  const { pulled, removed } = await this.pullAppConfigs(force);
45
51
  this.logPulledChanges(pulled, removed);
46
52
  }
47
- logPulledChanges(pulled, removed) {
48
- const typeLabel = this.appType === 'theme' ? 'theme' : 'app';
49
- const changeCount = pulled.length + removed.length;
50
- const pluralFiles = changeCount === 1 ? 'file' : 'files';
51
- this.log(`Pulled ${changeCount} ${pluralFiles} from ${style.appConfigValue(this.app.name)} ${typeLabel} in ${style.path(path.resolve(this.appPath))}.`);
52
- }
53
53
  }
@@ -3,15 +3,15 @@ export default class AppPush extends PushAppCommand {
3
3
  static args: {
4
4
  file: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
5
5
  };
6
+ static delayOrientation: boolean;
6
7
  static description: string;
7
8
  static examples: string[];
8
9
  static flags: any;
9
- static summary: string;
10
- static delayOrientation: boolean;
11
10
  static orientation: {
12
11
  env: string;
13
12
  };
14
- resolvePushPaths(file: string): {
13
+ static summary: string;
14
+ resolvePushPaths(file: string, appPathFlag?: string): {
15
15
  filePath: string;
16
16
  relativePath: string;
17
17
  };
@@ -10,6 +10,7 @@ export default class AppPush extends PushAppCommand {
10
10
  description: 'relative path to a configuration file or directory to push',
11
11
  }),
12
12
  };
13
+ static delayOrientation = true;
13
14
  static description = `Push all app files, a specific file, or a specific configuration
14
15
  type to an app in your store's test environment.
15
16
 
@@ -38,14 +39,15 @@ ${Object.values(ConfigPaths)
38
39
  description: 'skip deploying app frontend',
39
40
  }),
40
41
  };
41
- static summary = 'Push local files to your Swell app.';
42
- static delayOrientation = true;
43
42
  static orientation = {
44
43
  env: 'test',
45
44
  };
46
- resolvePushPaths(file) {
47
- // Join the appPath and file to get the absolute filePath
48
- const filePath = path.resolve(process.cwd(), file);
45
+ static summary = 'Push local files to your Swell app.';
46
+ resolvePushPaths(file, appPathFlag) {
47
+ // If --app-path was explicitly provided, resolve FILE relative to appPath
48
+ // Otherwise, resolve relative to cwd for better UX (tab completion)
49
+ const baseDir = appPathFlag ? this.appPath : process.cwd();
50
+ const filePath = path.resolve(baseDir, file);
49
51
  // Check if filePath is outside of appPath
50
52
  const relativePath = path.relative(this.appPath, filePath);
51
53
  if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
@@ -71,7 +73,7 @@ ${Object.values(ConfigPaths)
71
73
  return;
72
74
  }
73
75
  if (file) {
74
- const { filePath, relativePath } = this.resolvePushPaths(file);
76
+ const { filePath, relativePath } = this.resolvePushPaths(file, flags['app-path']);
75
77
  // Push individual files if not referring to the whole app path
76
78
  if (filePath !== this.appPath) {
77
79
  if (!filePathExists(filePath)) {
@@ -6,10 +6,10 @@ export default class AppRelease extends PushAppCommand {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: any;
9
- static summary: string;
10
9
  static orientation: {
11
10
  env: string;
12
11
  };
12
+ static summary: string;
13
13
  run(): Promise<void>;
14
14
  validateReleaseDetails(): void;
15
15
  private validateCreateVersion;
@@ -56,10 +56,10 @@ The app must be connected to a partner account with access to publish.`;
56
56
  description: 'skip confirmation prompt',
57
57
  }),
58
58
  };
59
- static summary = 'Release a new version of your an app for publishing to the App Store.';
60
59
  static orientation = {
61
60
  env: 'test',
62
61
  };
62
+ static summary = 'Release a new version of your an app for publishing to the App Store.';
63
63
  async run() {
64
64
  const { args, flags } = await this.parse(AppRelease);
65
65
  const { amend, message, 'not-supported': notSupported, 'release-notes': releaseNotesFile, yes, } = flags;
@@ -11,10 +11,10 @@ export default class AppVersion extends PushAppCommand {
11
11
  'no-git-tag': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
12
12
  yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
13
13
  };
14
- static summary: string;
15
14
  static orientation: {
16
15
  env: string;
17
16
  };
17
+ static summary: string;
18
18
  run(): Promise<void>;
19
19
  private createRemoteVersion;
20
20
  private gitCommit;
@@ -67,10 +67,10 @@ behavior by using the --no-git-tag option.`;
67
67
  description: 'skip confirmation prompt',
68
68
  }),
69
69
  };
70
- static summary = 'Increment the version of your Swell app.';
71
70
  static orientation = {
72
71
  env: 'test',
73
72
  };
73
+ static summary = 'Increment the version of your Swell app.';
74
74
  async run() {
75
75
  const { args, flags } = await this.parse(AppVersion);
76
76
  const { amend, 'no-git-tag': noGitTag, yes } = flags;
@@ -96,7 +96,9 @@ behavior by using the --no-git-tag option.`;
96
96
  const latestVersion = await this.getVersion();
97
97
  const remoteVersion = latestVersion?.version;
98
98
  // Suggest the next version if not provided
99
- if (!nextVersion && semver.gt(localVersion, remoteVersion)) {
99
+ if (!nextVersion &&
100
+ remoteVersion &&
101
+ semver.gt(localVersion, remoteVersion)) {
100
102
  nextVersion = localVersion;
101
103
  }
102
104
  // If the user didn't specify a version, show the latest one
@@ -187,7 +189,7 @@ behavior by using the --no-git-tag option.`;
187
189
  // increment the version
188
190
  version = semver.inc(localVersion, version);
189
191
  }
190
- if (semver.lt(version, localVersion)) {
192
+ if (semver.lte(version, localVersion)) {
191
193
  this.error(`Version ${version} must be greater than the current version ${localVersion}.`);
192
194
  }
193
195
  return version;
@@ -1,6 +1,5 @@
1
1
  import { CreateAppCommand } from '../../create-app-command.js';
2
2
  export default class CreateApp extends CreateAppCommand {
3
- appType: string;
4
3
  static args: {
5
4
  id: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
5
  };
@@ -10,11 +9,18 @@ export default class CreateApp extends CreateAppCommand {
10
9
  description: string;
11
10
  }[];
12
11
  static flags: any;
13
- createSwellConfig({ allowOverwrite, inputId, inputStorefrontApp, inputType, inputYes, nestedPath, }: {
12
+ appType: string;
13
+ createSwellConfig({ allowOverwrite, inputId, inputStorefrontApp, inputType, inputName, inputVersion, inputDescription, inputFrontend, inputIntegrationType, inputIntegrationId, inputYes, nestedPath, }: {
14
14
  allowOverwrite?: boolean;
15
+ inputDescription?: string;
16
+ inputFrontend?: string;
15
17
  inputId?: string;
18
+ inputIntegrationId?: string;
19
+ inputIntegrationType?: string;
20
+ inputName?: string;
16
21
  inputStorefrontApp?: string;
17
22
  inputType?: string;
23
+ inputVersion?: string;
18
24
  inputYes?: boolean;
19
25
  nestedPath?: boolean;
20
26
  }): Promise<{
@@ -22,6 +28,7 @@ export default class CreateApp extends CreateAppCommand {
22
28
  config: any;
23
29
  installedStorefrontApp: any;
24
30
  swellConfigJson: {
31
+ extensions?: any[] | undefined;
25
32
  theme: {
26
33
  storefront: {
27
34
  app: string;
@@ -33,6 +40,7 @@ export default class CreateApp extends CreateAppCommand {
33
40
  type: string;
34
41
  version: string;
35
42
  } | {
43
+ extensions?: any[] | undefined;
36
44
  permissions: never[];
37
45
  description: string;
38
46
  id: string;
@@ -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: 'Create app following command prompts.',
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 my_app -y',
25
- description: 'Create app and accept all default values.',
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: 'Create app with yarn package manager instead of npm by default.',
48
+ description: 'Package manager: use Yarn instead of npm.',
30
49
  },
31
50
  {
32
51
  command: 'swell create app -p none',
33
- description: 'Create app without installing TypeScript bindings, etc.',
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
- async createSwellConfig({ allowOverwrite = true, inputId = '', inputStorefrontApp, inputType, inputYes, nestedPath = true, }) {
50
- let confirmYes = inputYes;
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
- (confirmYes
53
- ? 'admin'
54
- : await select({
55
- choices: [
56
- { name: 'Admin', value: 'admin' },
57
- { name: 'Integration', value: 'integration' },
58
- { name: 'Storefront', value: 'storefront' },
59
- { name: 'Theme', value: 'theme' },
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 = confirmYes
66
- ? toAppName(inputId)
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 = confirmYes
83
- ? '1.0.0'
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 = confirmYes
89
- ? ''
90
- : await input({
91
- default: 'Something special',
92
- message: `Describe your ${typeLabel}`,
93
- });
94
- let storefrontInstalledApps;
95
- let storefrontAppId;
96
- let installedStorefrontApp;
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
- return;
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 && id);
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: flags.type,
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;