@swell/cli 2.9.7 → 2.9.9

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.
@@ -14,6 +14,7 @@ export declare abstract class AppCommand extends SwellCommand {
14
14
  static baseFlags: {
15
15
  'app-path': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
16
16
  };
17
+ static requiresSwellConfig: boolean;
17
18
  appPath: string;
18
19
  swellConfig: LocalApp;
19
20
  init(): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import * as path from 'node:path';
3
- import { default as swellConfig } from './lib/app-config.js';
3
+ import { default as swellConfig, swellConfigFileExists, } from './lib/app-config.js';
4
+ import style from './lib/style.js';
4
5
  import { SwellCommand } from './swell-command.js';
5
6
  /**
6
7
  * A base class for Swell CLI commands that do not require an app to be saved
@@ -20,6 +21,8 @@ export class AppCommand extends SwellCommand {
20
21
  description: 'Path to your app directory',
21
22
  }),
22
23
  };
24
+ // set to false for commands that may run before an app directory exists (pull)
25
+ static requiresSwellConfig = true;
23
26
  // the local path to the app directory
24
27
  appPath = '';
25
28
  // local app configuration, what developers define
@@ -35,8 +38,17 @@ export class AppCommand extends SwellCommand {
35
38
  // ensure the base flags are available
36
39
  klass.flags = { ...klass.flags, ...AppCommand.baseFlags };
37
40
  const { flags } = await this.parse(klass);
41
+ const targetDir = flags['app-path']
42
+ ? path.resolve(flags['app-path'])
43
+ : process.cwd();
44
+ // swell.json is only ever looked up in this exact directory
45
+ // Searching parent directories (the previous behavior) can silently pick up an unrelated app's config
46
+ if (klass.requiresSwellConfig &&
47
+ !(await swellConfigFileExists(path.join(targetDir, 'swell.json')))) {
48
+ this.error(`No swell.json found in ${style.path(targetDir)}. Run this command from your app's root directory, or pass --app-path.`);
49
+ }
38
50
  // if the user passed an app path, use it: reload the app config
39
- this.swellConfig = await swellConfig(flags['app-path']);
51
+ this.swellConfig = await swellConfig(targetDir);
40
52
  this.appPath = path.resolve(this.swellConfig.swDirPath);
41
53
  }
42
54
  }
@@ -4,6 +4,7 @@ export default class AppPull extends PushAppCommand {
4
4
  appId: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
5
5
  targetPath: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
6
  };
7
+ static requiresSwellConfig: boolean;
7
8
  static description: string;
8
9
  static examples: string[];
9
10
  static flags: {
@@ -11,6 +11,7 @@ export default class AppPull extends PushAppCommand {
11
11
  description: 'path to download app files into',
12
12
  }),
13
13
  };
14
+ static requiresSwellConfig = false;
14
15
  static description = `Pull all app files, a specific file, or a specific configuration
15
16
  type from an app in your store's test environment to your local machine.
16
17
 
@@ -17,7 +17,8 @@ type to an app in your store's test environment.
17
17
  If the app does not exist, it will be created and its global ID saved to a .swellrc file.
18
18
 
19
19
  - If no file is specified, all configuration files will be pushed to the store.
20
- This includes the app icon (assets/icon.png) and swell.json.
20
+ This includes the app icon (assets/icon.png), cover image (assets/image.png),
21
+ and swell.json.
21
22
  - If a file is specified, only that file will be pushed to the store.
22
23
  - If a directory is specified, only files in that directory will be pushed.
23
24
 
@@ -11,6 +11,5 @@ export default class AppRelease extends PushAppCommand {
11
11
  };
12
12
  static summary: string;
13
13
  run(): Promise<void>;
14
- validateReleaseDetails(): void;
15
14
  private validateCreateVersion;
16
15
  }
@@ -53,7 +53,7 @@ The app must be connected to a partner account with access to publish.`;
53
53
  }),
54
54
  yes: Flags.boolean({
55
55
  char: 'y',
56
- description: 'skip confirmation prompt',
56
+ description: 'skip confirmation prompts, including unstaging another staged version',
57
57
  }),
58
58
  };
59
59
  static orientation = {
@@ -82,6 +82,20 @@ The app must be connected to a partner account with access to publish.`;
82
82
  if (appVersion.released && !amend) {
83
83
  throw new Error(`Version ${appVersion.version} has already been released. Use --amend to update version details.`);
84
84
  }
85
+ const stagedVersion = this.app.staged_props?.version;
86
+ const hasOtherStagedVersion = !amend && stagedVersion && stagedVersion !== appVersion.version;
87
+ if (hasOtherStagedVersion) {
88
+ this.log(style.funcWarn(`Version ${stagedVersion} is currently staged. Releasing version ${appVersion.version} will unstage it.`));
89
+ if (!yes) {
90
+ const continueUnstage = await confirm({
91
+ default: false,
92
+ message: `Continue?`,
93
+ });
94
+ if (!continueUnstage) {
95
+ return;
96
+ }
97
+ }
98
+ }
85
99
  const releaseData = {
86
100
  description: message || appVersion.description,
87
101
  release_notes: releaseNotesFile ? releaseNotes : appVersion.release_notes,
@@ -108,22 +122,6 @@ The app must be connected to a partner account with access to publish.`;
108
122
  }, spinner);
109
123
  spinner.succeed(`${style.appConfigValue(this.app.name)} release ${appVersion.version} ${amend ? 'updated' : 'released'}.`);
110
124
  }
111
- validateReleaseDetails() {
112
- if (!this.app.description) {
113
- throw new Error('App must have a description');
114
- }
115
- const requiresPreviewImages = this.app.type === 'storefront' &&
116
- (!this.app.kind || this.app.kind === 'shop') &&
117
- this.app.storefront?.theme.provider !== 'app';
118
- if (requiresPreviewImages) {
119
- if (!this.app.preview_image) {
120
- throw new Error('Storefront app must have a preview image to release');
121
- }
122
- }
123
- else if (this.app.type !== 'theme' && !this.app.logo_icon) {
124
- throw new Error('App must have a logo icon to release');
125
- }
126
- }
127
125
  async validateCreateVersion(inputVersion, flags) {
128
126
  let version = inputVersion;
129
127
  if (!version) {
@@ -132,7 +130,7 @@ The app must be connected to a partner account with access to publish.`;
132
130
  throw new Error('No version specified');
133
131
  }
134
132
  }
135
- this.validateReleaseDetails();
133
+ // Mandatory app details are validated server-side when the version is released.
136
134
  let appVersion = await this.getVersion(version);
137
135
  if (!appVersion) {
138
136
  if (flags.amend) {
@@ -209,12 +209,11 @@ export class CreateAppCommand extends SwellCommand {
209
209
  await fs.writeFile(configFilePath, content, 'utf8');
210
210
  }
211
211
  async createAppConfigFolders(swellConfig) {
212
+ const configPath = path.dirname(swellConfig.path);
212
213
  for (const type of getAllConfigPaths(swellConfig.get('type'))) {
213
214
  // Create a config folder
214
215
  // eslint-disable-next-line no-await-in-loop
215
- await execAsync(`mkdir -p ${type}`, {
216
- cwd: path.dirname(swellConfig.path),
217
- });
216
+ await fs.mkdir(path.join(configPath, type), { recursive: true });
218
217
  }
219
218
  }
220
219
  async createFrontendApp(swellConfig, flags, directCreate, kind) {
@@ -300,8 +299,8 @@ export class CreateAppCommand extends SwellCommand {
300
299
  }
301
300
  spinner.start(`Creating ${projectType?.name} frontend app (this may take a while)...`);
302
301
  try {
303
- await execAsync(`mkdir -p frontend`, {
304
- cwd: configPath,
302
+ await fs.mkdir(path.join(configPath, 'frontend'), {
303
+ recursive: true,
305
304
  });
306
305
  // SWELL_LOCAL_TEMPLATE_PATH overrides the scaffold for local dev — copies
307
306
  // the directory at the path into frontend/ instead of running the install
@@ -417,16 +416,14 @@ export class CreateAppCommand extends SwellCommand {
417
416
  this.log();
418
417
  spinner.start(`Creating theme template...`);
419
418
  try {
420
- await execAsync(`mkdir -p theme`, {
421
- cwd: configPath,
422
- });
419
+ await fs.mkdir(path.join(configPath, 'theme'), { recursive: true });
423
420
  for (const themeConfig of defaultThemeConfigs.results) {
424
421
  const filePath = themeConfig.file_path.replace(/^frontend\/theme-template\//, '');
425
422
  const fileContent = themeConfig.file_data;
426
423
  const isJson = filePath.endsWith('.json');
427
424
  // eslint-disable-next-line no-await-in-loop
428
- await execAsync(`mkdir -p ${path.dirname(filePath)}`, {
429
- cwd: configPath,
425
+ await fs.mkdir(path.join(configPath, path.dirname(filePath)), {
426
+ recursive: true,
430
427
  });
431
428
  // eslint-disable-next-line no-await-in-loop
432
429
  await (isJson
@@ -12,6 +12,9 @@ const schema = {
12
12
  integrations: { type: 'array' },
13
13
  name: { type: 'string' },
14
14
  permissions: { type: 'array' },
15
+ price: { type: 'number', minimum: 0 },
16
+ price_external: { type: 'boolean' },
17
+ price_interval: { type: 'string', enum: ['once', 'monthly'] },
15
18
  storefront: { type: 'object' },
16
19
  theme: { type: 'object' },
17
20
  type: { type: 'string' },
@@ -2,7 +2,7 @@
2
2
  /// <reference types="node" resolution-mode="require"/>
3
3
  import { AppConfig } from './app-config.js';
4
4
  export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
5
- export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, } from './paths.js';
5
+ export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, toPosixPath, } from './paths.js';
6
6
  export declare const PUSH_CONCURRENCY = 3;
7
7
  /**
8
8
  * An app as defined by the Swell API. This is different than an app
@@ -16,9 +16,12 @@ export interface App {
16
16
  [key: string]: string;
17
17
  };
18
18
  configs?: AppConfig[];
19
+ cover_image?: unknown;
19
20
  date_created?: string;
20
21
  date_updated?: string;
22
+ demo_url?: string;
21
23
  description?: string;
24
+ documentation_url?: string;
22
25
  extensions?: any[];
23
26
  features?: string[];
24
27
  frontend?: {
@@ -29,6 +32,7 @@ export interface App {
29
32
  service?: string;
30
33
  url?: string;
31
34
  };
35
+ full_description?: string;
32
36
  highlights?: Array<{
33
37
  id: string;
34
38
  image_src?: unknown;
@@ -45,10 +49,19 @@ export interface App {
45
49
  owned?: boolean;
46
50
  preview_image?: unknown;
47
51
  preview_mobile_image?: unknown;
52
+ price?: number;
53
+ price_external?: boolean;
54
+ price_interval?: 'once' | 'monthly';
55
+ preview_video_url?: string;
48
56
  private_id?: string;
49
57
  public_id?: string;
50
58
  published?: boolean;
51
59
  purchase_options?: string[];
60
+ repository_url?: string;
61
+ staged_props?: {
62
+ [key: string]: any;
63
+ version?: string;
64
+ };
52
65
  storefront?: {
53
66
  [key: string]: any;
54
67
  };
@@ -60,6 +73,8 @@ export interface App {
60
73
  preview_image?: unknown;
61
74
  preview_mobile_image?: unknown;
62
75
  }>;
76
+ support_email?: string;
77
+ support_url?: string;
63
78
  theme?: {
64
79
  [key: string]: any;
65
80
  };
@@ -74,6 +89,9 @@ export declare enum SwellJsonFields {
74
89
  KIND = "kind",
75
90
  NAME = "name",
76
91
  PERMISSIONS = "permissions",
92
+ PRICE = "price",
93
+ PRICE_EXTERNAL = "price_external",
94
+ PRICE_INTERVAL = "price_interval",
77
95
  STOREFRONT = "storefront",
78
96
  THEME = "theme",
79
97
  TYPE = "type",
@@ -5,7 +5,7 @@ import * as path from 'node:path';
5
5
  import { detectPackageManager, transformCommand } from '../package-manager.js';
6
6
  import { AppConfig } from './app-config.js';
7
7
  export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
8
- export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, } from './paths.js';
8
+ export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, toPosixPath, } from './paths.js';
9
9
  export const PUSH_CONCURRENCY = 3;
10
10
  export var SwellJsonFields;
11
11
  (function (SwellJsonFields) {
@@ -15,6 +15,9 @@ export var SwellJsonFields;
15
15
  SwellJsonFields["KIND"] = "kind";
16
16
  SwellJsonFields["NAME"] = "name";
17
17
  SwellJsonFields["PERMISSIONS"] = "permissions";
18
+ SwellJsonFields["PRICE"] = "price";
19
+ SwellJsonFields["PRICE_EXTERNAL"] = "price_external";
20
+ SwellJsonFields["PRICE_INTERVAL"] = "price_interval";
18
21
  SwellJsonFields["STOREFRONT"] = "storefront";
19
22
  SwellJsonFields["THEME"] = "theme";
20
23
  SwellJsonFields["TYPE"] = "type";
@@ -1,5 +1,6 @@
1
1
  import { type GlobbyFilterFunction } from 'globby';
2
2
  import { ConfigType } from './index.js';
3
+ export declare function toPosixPath(inputPath: string): string;
3
4
  export declare function globAllFilesByPath(appPath: string, dirPath?: string): Promise<string[]>;
4
5
  export declare function getGlobIgnorePathsChecker(appPath: string): Promise<GlobbyFilterFunction>;
5
6
  export declare function allConfigDirsPaths(appPath: string): Generator<{
@@ -27,11 +27,15 @@ function globOptions(appPath, options) {
27
27
  ],
28
28
  };
29
29
  }
30
+ // use forward slashes to support Windows
31
+ export function toPosixPath(inputPath) {
32
+ return inputPath.split(path.sep).join('/');
33
+ }
30
34
  function globFilesSync(pattern, appPath, dirPath = '.', options = {}) {
31
- return globbySync(`${dirPath}${path.sep}${pattern}`, globOptions(appPath, options));
35
+ return globbySync(`${toPosixPath(dirPath)}/${pattern}`, globOptions(appPath, options));
32
36
  }
33
37
  function globFiles(pattern, appPath, dirPath = '.', options = {}) {
34
- return globby(`${dirPath}${path.sep}${pattern}`, globOptions(appPath, options));
38
+ return globby(`${toPosixPath(dirPath)}/${pattern}`, globOptions(appPath, options));
35
39
  }
36
40
  export function globAllFilesByPath(appPath, dirPath) {
37
41
  return globFiles('**', appPath, dirPath);
@@ -26,7 +26,7 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
26
26
  createAppStorefront(hasOtherStorefronts?: boolean, nonInteractive?: boolean): Promise<any>;
27
27
  deployAppFrontend(force?: boolean, log?: boolean): Promise<void>;
28
28
  deployFrontend(projectType: FrontendProjectType): Promise<string>;
29
- ensureAppExists(file?: string, shouldCreate?: boolean): Promise<boolean>;
29
+ ensureAppExists(file?: string, shouldCreate?: boolean, force?: boolean): Promise<boolean>;
30
30
  ensureLoggedIn(): Promise<void>;
31
31
  exec(command: string, cwd?: string, onOutput?: (string: string) => any | false): Promise<void>;
32
32
  execFrontend(command: string, onOutput?: (string: string) => any | false): Promise<void>;
@@ -6,7 +6,7 @@ import * as path from 'node:path';
6
6
  import Stream from 'node:stream';
7
7
  import ora from 'ora';
8
8
  import { default as swellConfig } from './lib/app-config.js';
9
- import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, CUSTOM_FRAMEWORK_SLUG, appConfigFromFile, filePathExistsAsync, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, getProjectCommands, globAllFilesByPath, hashString, isPathDirectory, } from './lib/apps/index.js';
9
+ import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, CUSTOM_FRAMEWORK_SLUG, appConfigFromFile, filePathExistsAsync, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, getProjectCommands, globAllFilesByPath, hashString, isPathDirectory, toPosixPath, } from './lib/apps/index.js';
10
10
  import { getGlobIgnorePathsChecker } from './lib/apps/paths.js';
11
11
  import { slugFromApp } from './lib/apps/slug.js';
12
12
  import { default as localConfig } from './lib/config.js';
@@ -32,14 +32,12 @@ export class PushAppCommand extends RemoteAppCommand {
32
32
  if (!filename) {
33
33
  return;
34
34
  }
35
- if (this.watchingIgnoreFilter && this.watchingIgnoreFilter(filename)) {
35
+ const configFile = toPosixPath(filename);
36
+ if (this.watchingIgnoreFilter && this.watchingIgnoreFilter(configFile)) {
36
37
  return;
37
38
  }
38
- const configFile = filename;
39
- const pathParsed = path.parse(configFile);
40
- const fileDirs = pathParsed.dir.split(path.sep);
41
39
  // only the first directory is relevant for us
42
- const configDir = fileDirs[0];
40
+ const configDir = configFile.split('/')[0];
43
41
  // we only want to watch files in the root of the app directory
44
42
  // and ignore build files etc
45
43
  // TODO: renaming a folder doesn't seem to work
@@ -284,7 +282,7 @@ export class PushAppCommand extends RemoteAppCommand {
284
282
  this.error(`Error deploying frontend: ${error.message}`);
285
283
  }
286
284
  }
287
- async ensureAppExists(file, shouldCreate = true) {
285
+ async ensureAppExists(file, shouldCreate = true, force = false) {
288
286
  const klass = this.ctor;
289
287
  const currentStore = localConfig.getDefaultStore();
290
288
  await this.ensureLoggedIn();
@@ -313,14 +311,14 @@ export class PushAppCommand extends RemoteAppCommand {
313
311
  // If app exists but owned by another client, create a new instance
314
312
  if (app?.client_id && app.client_id !== currentStore) {
315
313
  // Create a new app
316
- this.app = await this.getCreateUpdateApp();
314
+ this.app = await this.getCreateUpdateApp(undefined, force);
317
315
  if (!this.app) {
318
316
  return false;
319
317
  }
320
318
  }
321
319
  else if (!file) {
322
320
  // Update existing app if not targeting a file
323
- this.app = await this.getCreateUpdateApp(app);
321
+ this.app = await this.getCreateUpdateApp(app, force);
324
322
  }
325
323
  return true;
326
324
  }
@@ -433,7 +431,7 @@ export class PushAppCommand extends RemoteAppCommand {
433
431
  async getFrontendDeploymentHash() {
434
432
  const localConfigs = await this.getLocalAppConfigs();
435
433
  let frontendHashes = localConfigs
436
- .filter((config) => config.filePath?.startsWith(`frontend${path.sep}`))
434
+ .filter((config) => config.filePath?.startsWith('frontend/'))
437
435
  .map((config) => config.hash)
438
436
  .sort()
439
437
  .join('|');
@@ -675,16 +673,18 @@ export class PushAppCommand extends RemoteAppCommand {
675
673
  this.log(`View your storefront at ${style.link(this.storefrontFrontendUrl(currentStore, storefront, branchId))}.`);
676
674
  }
677
675
  async pushFile(relativePath) {
678
- const basePath = relativePath.split(path.sep)[0];
676
+ const relativePathPosix = toPosixPath(relativePath);
677
+ const basePath = relativePathPosix.split('/')[0];
679
678
  const configType = getConfigTypeFromPath(basePath) || ConfigType.FILE;
680
- const config = await appConfigFromFile(relativePath, configType, this.appPath);
679
+ const config = await appConfigFromFile(relativePathPosix, configType, this.appPath);
681
680
  await this.pushRemoteFile(config);
682
681
  }
683
682
  async pushFilePath(relativePath, force) {
684
683
  const topDir = relativePath.split(path.sep)[0];
685
684
  const configType = getConfigTypeFromPath(topDir) || ConfigType.FILE;
686
685
  const configTypeKey = getConfigTypeKeyFromValue(configType) || ConfigType.FILE;
687
- const exAppConfigs = (this.app?.configs || []).filter((config) => config.filePath?.startsWith(`${relativePath}/`));
686
+ const relativePathPosix = toPosixPath(relativePath);
687
+ const exAppConfigs = (this.app?.configs || []).filter((config) => config.filePath?.startsWith(`${relativePathPosix}/`));
688
688
  const promises = [];
689
689
  for (const { configFile } of allConfigFilesInDir(this.appPath, relativePath, configTypeKey)) {
690
690
  promises.push(appConfigFromFile(configFile, configType, this.appPath));
@@ -24,7 +24,7 @@ export declare abstract class RemoteAppCommand extends AppCommand {
24
24
  protected getApp(id?: string): Promise<App>;
25
25
  protected getAppWithConfig(id?: string): Promise<App>;
26
26
  getConfigData(configId: string): Promise<any>;
27
- protected getCreateUpdateApp(updateApp?: App | null): Promise<App>;
27
+ protected getCreateUpdateApp(updateApp?: App | null, force?: boolean): Promise<App>;
28
28
  protected getInstalledApp(): Promise<any>;
29
29
  protected getLocalAppConfigs(): Promise<AppConfig[]>;
30
30
  protected getVersion(version?: null | string | undefined): Promise<AppVersion>;
@@ -172,7 +172,7 @@ export class RemoteAppCommand extends AppCommand {
172
172
  adminPath: `/apps/${this.app.id}/configs/${configId}/data`,
173
173
  }, { query: { ...(themeId ? { theme_id: themeId } : undefined) } });
174
174
  }
175
- async getCreateUpdateApp(updateApp) {
175
+ async getCreateUpdateApp(updateApp, force = false) {
176
176
  const spinner = ora();
177
177
  let sourceApp;
178
178
  try {
@@ -195,6 +195,9 @@ export class RemoteAppCommand extends AppCommand {
195
195
  ? { private_id: this.swellConfig.store.id }
196
196
  : undefined),
197
197
  ...(sourceApp?.id ? { source_id: sourceApp.id } : undefined),
198
+ // Overwrite name/description/logo_icon on the server
199
+ // instead of only filling them in when empty
200
+ ...(force ? { $force_meta: true } : undefined),
198
201
  };
199
202
  const appLabel = this.app.type === 'theme' ? 'theme' : 'app';
200
203
  let app = updateApp || {};
@@ -708,6 +708,7 @@
708
708
  "pluginType": "core",
709
709
  "strict": true,
710
710
  "enableJsonFlag": false,
711
+ "requiresSwellConfig": true,
711
712
  "delayOrientation": false,
712
713
  "orientation": {
713
714
  "env": "test"
@@ -851,6 +852,7 @@
851
852
  "strict": true,
852
853
  "summary": "Show information about your Swell app.",
853
854
  "enableJsonFlag": false,
855
+ "requiresSwellConfig": true,
854
856
  "delayOrientation": false,
855
857
  "isESM": true,
856
858
  "relativePath": [
@@ -1101,6 +1103,7 @@
1101
1103
  "strict": true,
1102
1104
  "summary": "Install an existing app in another store environment.",
1103
1105
  "enableJsonFlag": false,
1106
+ "requiresSwellConfig": true,
1104
1107
  "delayOrientation": false,
1105
1108
  "orientation": {
1106
1109
  "env": "test"
@@ -1157,6 +1160,7 @@
1157
1160
  "pluginType": "core",
1158
1161
  "strict": true,
1159
1162
  "summary": "Pull app files from Swell to your local machine.",
1163
+ "requiresSwellConfig": false,
1160
1164
  "orientation": {
1161
1165
  "env": "test"
1162
1166
  },
@@ -1176,7 +1180,7 @@
1176
1180
  "name": "file"
1177
1181
  }
1178
1182
  },
1179
- "description": "Push all app files, a specific file, or a specific configuration\ntype to an app in your store's test environment.\n\nIf the app does not exist, it will be created and its global ID saved to a .swellrc file.\n\n- If no file is specified, all configuration files will be pushed to the store.\n This includes the app icon (assets/icon.png) and swell.json.\n- If a file is specified, only that file will be pushed to the store.\n- If a directory is specified, only files in that directory will be pushed.\n\nApp file directories:\nassets/\ncontent/\nfrontend/\ncomponents/\nfunctions/\nmodels/\nnotifications/\nsettings/\ntheme/\nwebhooks/",
1183
+ "description": "Push all app files, a specific file, or a specific configuration\ntype to an app in your store's test environment.\n\nIf the app does not exist, it will be created and its global ID saved to a .swellrc file.\n\n- If no file is specified, all configuration files will be pushed to the store.\n This includes the app icon (assets/icon.png), cover image (assets/image.png),\n and swell.json.\n- If a file is specified, only that file will be pushed to the store.\n- If a directory is specified, only files in that directory will be pushed.\n\nApp file directories:\nassets/\ncontent/\nfrontend/\ncomponents/\nfunctions/\nmodels/\nnotifications/\nsettings/\ntheme/\nwebhooks/",
1180
1184
  "examples": [
1181
1185
  "swell app push",
1182
1186
  "swell app push content",
@@ -1281,7 +1285,7 @@
1281
1285
  },
1282
1286
  "yes": {
1283
1287
  "char": "y",
1284
- "description": "skip confirmation prompt",
1288
+ "description": "skip confirmation prompts, including unstaging another staged version",
1285
1289
  "name": "yes",
1286
1290
  "allowNo": false,
1287
1291
  "type": "boolean"
@@ -2298,6 +2302,7 @@
2298
2302
  "strict": true,
2299
2303
  "summary": "Create tests scaffolding for your Swell app.",
2300
2304
  "enableJsonFlag": false,
2305
+ "requiresSwellConfig": true,
2301
2306
  "helpMeta": {
2302
2307
  "usageDirect": "[-y]"
2303
2308
  },
@@ -3326,6 +3331,7 @@
3326
3331
  "pluginName": "@swell/cli",
3327
3332
  "pluginType": "core",
3328
3333
  "summary": "Pull theme files from Swell to your local machine.",
3334
+ "requiresSwellConfig": false,
3329
3335
  "orientation": {
3330
3336
  "env": "test"
3331
3337
  },
@@ -3588,5 +3594,5 @@
3588
3594
  ]
3589
3595
  }
3590
3596
  },
3591
- "version": "2.9.7"
3597
+ "version": "2.9.9"
3592
3598
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swell/cli",
3
- "version": "2.9.7",
3
+ "version": "2.9.9",
4
4
  "type": "module",
5
5
  "description": "Swell's command line interface/utility",
6
6
  "keywords": [