@swell/cli 2.0.1-alpha.9 → 2.0.2

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 (102) hide show
  1. package/dist/app-command.d.ts +1 -1
  2. package/dist/app-command.js +9 -9
  3. package/dist/commands/app/dev.d.ts +16 -0
  4. package/dist/commands/app/dev.js +28 -0
  5. package/dist/commands/app/frontend/deploy.d.ts +15 -0
  6. package/dist/commands/app/frontend/deploy.js +41 -0
  7. package/dist/commands/app/frontend/dev.d.ts +19 -0
  8. package/dist/commands/app/frontend/dev.js +108 -0
  9. package/dist/commands/app/info.js +14 -9
  10. package/dist/commands/app/init.d.ts +12 -0
  11. package/dist/commands/app/init.js +34 -0
  12. package/dist/commands/app/install.d.ts +4 -4
  13. package/dist/commands/app/install.js +36 -42
  14. package/dist/commands/app/pull.d.ts +15 -0
  15. package/dist/commands/app/pull.js +50 -0
  16. package/dist/commands/app/push.d.ts +11 -14
  17. package/dist/commands/app/push.js +64 -138
  18. package/dist/commands/app/release.d.ts +13 -0
  19. package/dist/commands/app/release.js +148 -0
  20. package/dist/commands/app/version.d.ts +5 -4
  21. package/dist/commands/app/version.js +63 -42
  22. package/dist/commands/app/watch.d.ts +2 -4
  23. package/dist/commands/app/watch.js +3 -83
  24. package/dist/commands/create/app.d.ts +35 -11
  25. package/dist/commands/create/app.js +143 -137
  26. package/dist/commands/create/content.d.ts +2 -2
  27. package/dist/commands/create/content.js +10 -10
  28. package/dist/commands/create/frontend.d.ts +12 -0
  29. package/dist/commands/create/frontend.js +38 -0
  30. package/dist/commands/create/function.d.ts +3 -3
  31. package/dist/commands/create/function.js +22 -22
  32. package/dist/commands/create/model.d.ts +2 -2
  33. package/dist/commands/create/model.js +11 -11
  34. package/dist/commands/create/notification.d.ts +4 -4
  35. package/dist/commands/create/notification.js +38 -40
  36. package/dist/commands/env.d.ts +12 -0
  37. package/dist/commands/env.js +31 -0
  38. package/dist/commands/login.d.ts +1 -1
  39. package/dist/commands/login.js +18 -24
  40. package/dist/commands/logs.js +0 -1
  41. package/dist/commands/theme/dev.d.ts +21 -0
  42. package/dist/commands/theme/dev.js +164 -0
  43. package/dist/commands/theme/init.d.ts +9 -0
  44. package/dist/commands/theme/init.js +15 -0
  45. package/dist/commands/theme/pull.d.ts +20 -0
  46. package/dist/commands/theme/pull.js +65 -0
  47. package/dist/commands/theme/push.d.ts +19 -0
  48. package/dist/commands/theme/push.js +89 -0
  49. package/dist/create-app-command.d.ts +20 -0
  50. package/dist/create-app-command.js +253 -0
  51. package/dist/create-collection-command.d.ts +9 -9
  52. package/dist/create-collection-command.js +23 -23
  53. package/dist/{create-command.d.ts → create-config-command.d.ts} +1 -1
  54. package/dist/{create-command.js → create-config-command.js} +7 -7
  55. package/dist/env/local.d.ts +8 -0
  56. package/dist/env/local.js +7 -0
  57. package/dist/env/production.d.ts +8 -0
  58. package/dist/env/production.js +7 -0
  59. package/dist/env/review.d.ts +8 -0
  60. package/dist/env/review.js +7 -0
  61. package/dist/env/staging.d.ts +8 -0
  62. package/dist/env/staging.js +7 -0
  63. package/dist/lib/api.d.ts +4 -2
  64. package/dist/lib/api.js +50 -21
  65. package/dist/lib/app-config.d.ts +13 -5
  66. package/dist/lib/app-config.js +62 -24
  67. package/dist/lib/apps/app-config.d.ts +17 -3
  68. package/dist/lib/apps/app-config.js +194 -66
  69. package/dist/lib/apps/index.d.ts +93 -12
  70. package/dist/lib/apps/index.js +155 -50
  71. package/dist/lib/apps/paths.d.ts +14 -7
  72. package/dist/lib/apps/paths.js +73 -28
  73. package/dist/lib/bundle.js +15 -3
  74. package/dist/lib/config.d.ts +9 -2
  75. package/dist/lib/config.js +49 -1
  76. package/dist/lib/constants.d.ts +5 -0
  77. package/dist/lib/constants.js +24 -6
  78. package/dist/lib/create/content.d.ts +2 -2
  79. package/dist/lib/create/content.js +1 -1
  80. package/dist/lib/create/function.d.ts +2 -2
  81. package/dist/lib/create/function.js +2 -2
  82. package/dist/lib/create/index.d.ts +1 -1
  83. package/dist/lib/create/index.js +11 -7
  84. package/dist/lib/create/model.d.ts +2 -2
  85. package/dist/lib/create/notification.d.ts +2 -2
  86. package/dist/lib/info.js +2 -2
  87. package/dist/lib/logs/index.js +1 -1
  88. package/dist/lib/sessions.js +2 -2
  89. package/dist/lib/stores.d.ts +9 -4
  90. package/dist/lib/stores.js +13 -8
  91. package/dist/lib/style.d.ts +2 -0
  92. package/dist/lib/style.js +2 -0
  93. package/dist/lib/swell-function-wrapper.d.ts +40 -1
  94. package/dist/lib/swell-function-wrapper.js +107 -22
  95. package/dist/push-app-command.d.ts +73 -0
  96. package/dist/push-app-command.js +743 -0
  97. package/dist/remote-app-command.d.ts +37 -11
  98. package/dist/remote-app-command.js +432 -169
  99. package/oclif.manifest.json +872 -123
  100. package/package.json +14 -1
  101. package/dist/commands/app/_pull.d.ts +0 -8
  102. package/dist/commands/app/_pull.js +0 -45
@@ -14,8 +14,8 @@ 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
- appConfig: any;
18
17
  appPath: string;
18
+ swellConfig: any;
19
19
  constructor(argv: string[], config: Config);
20
20
  init(): Promise<void>;
21
21
  protected initBaseFlags(): Promise<void>;
@@ -1,5 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
- import { default as appConfig } from './lib/app-config.js';
2
+ import * as path from 'node:path';
3
+ import { default as swellConfig } from './lib/app-config.js';
3
4
  import { SwellCommand } from './swell-command.js';
4
5
  /**
5
6
  * A base class for Swell CLI commands that do not require an app to be saved
@@ -16,17 +17,17 @@ export class AppCommand extends SwellCommand {
16
17
  // needs to be in a ES2021 format: https://github.com/oclif/oclif/issues/1100
17
18
  static baseFlags = {
18
19
  'app-path': Flags.string({
19
- description: 'path to the app directory',
20
+ description: 'path to your app directory',
20
21
  }),
21
22
  };
22
- // local app configuration, what developers define
23
- appConfig;
24
23
  // the local path to the app directory
25
24
  appPath = '';
25
+ // local app configuration, what developers define
26
+ swellConfig;
26
27
  constructor(argv, config) {
27
28
  super(argv, config);
28
- this.appConfig = appConfig();
29
- this.appPath = this.appConfig.swDirPath;
29
+ this.swellConfig = swellConfig();
30
+ this.appPath = path.resolve(this.swellConfig.swDirPath);
30
31
  }
31
32
  async init() {
32
33
  await super.init();
@@ -38,8 +39,7 @@ export class AppCommand extends SwellCommand {
38
39
  klass.flags = { ...klass.flags, ...AppCommand.baseFlags };
39
40
  const { flags } = await this.parse(klass);
40
41
  // if the user passed an app path, use it: reload the app config
41
- this.appConfig = appConfig(flags['app-path']);
42
- this.appPath = this.appConfig.swDirPath;
43
- this.debug(`config path loaded: ${this.appConfig.path}`);
42
+ this.swellConfig = swellConfig(flags['app-path']);
43
+ this.appPath = path.resolve(this.swellConfig.swDirPath);
44
44
  }
45
45
  }
@@ -0,0 +1,16 @@
1
+ import { PushAppCommand } from '../../push-app-command.js';
2
+ export default class AppDev extends PushAppCommand {
3
+ static delayOrientation: boolean;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'storefront-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ 'storefront-select': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
8
+ 'no-push': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
9
+ port: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
+ };
11
+ static orientation: {
12
+ env: string;
13
+ };
14
+ static summary: string;
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,28 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { PushAppCommand } from '../../push-app-command.js';
3
+ import AppFrontendDev from './frontend/dev.js';
4
+ export default class AppDev extends PushAppCommand {
5
+ static delayOrientation = true;
6
+ static examples = [
7
+ 'swell app dev',
8
+ 'swell app dev --storefront-id <id>',
9
+ 'swell app dev --port 3000',
10
+ ];
11
+ static flags = {
12
+ ...AppFrontendDev.flags,
13
+ 'storefront-id': Flags.string({
14
+ description: 'for storefront apps, identify a storefront to preview and push theme files to',
15
+ }),
16
+ 'storefront-select': Flags.boolean({
17
+ default: false,
18
+ description: 'for storefront apps, prompt to select a storefront to preview',
19
+ }),
20
+ };
21
+ static orientation = {
22
+ env: 'test',
23
+ };
24
+ static summary = `Run an app in dev mode from your local machine.`;
25
+ async run() {
26
+ await this.config.runCommand('app:frontend:dev', this.argv);
27
+ }
28
+ }
@@ -0,0 +1,15 @@
1
+ import { PushAppCommand } from '../../../push-app-command.js';
2
+ export default class AppFrontendDeploy extends PushAppCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
+ 'storefront-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ 'storefront-select': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
9
+ };
10
+ static orientation: {
11
+ env: string;
12
+ };
13
+ static summary: string;
14
+ run(): Promise<void>;
15
+ }
@@ -0,0 +1,41 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { PushAppCommand } from '../../../push-app-command.js';
3
+ export default class AppFrontendDeploy extends PushAppCommand {
4
+ static description = `Creates a new build using the relevant command depending on your frontend framework.
5
+ Once the build succeeds, this command will invoke Cloudflare Wrangler to upload
6
+ build assets using your own Cloudflare account. Once complete, Swell will update
7
+ the app's deployment URL and push the updated frontend files.`;
8
+ static examples = [
9
+ 'swell app frontend deploy',
10
+ 'swell app frontend deploy --force',
11
+ 'swell app frontend deploy --storefront-select',
12
+ 'swell app frontend deploy --storefront-id <id>',
13
+ ];
14
+ static flags = {
15
+ force: Flags.boolean({
16
+ description: 'force push and build all frontend files',
17
+ }),
18
+ 'storefront-id': Flags.string({
19
+ description: 'identify a storefront to preview deployment with',
20
+ }),
21
+ 'storefront-select': Flags.boolean({
22
+ default: false,
23
+ description: 'prompt to select a storefront to preview deployment with',
24
+ }),
25
+ };
26
+ static orientation = {
27
+ env: 'test',
28
+ };
29
+ static summary = `Push frontend files to your Swell app.`;
30
+ async run() {
31
+ const { flags } = await this.parse(AppFrontendDeploy);
32
+ const { force } = flags;
33
+ if (!(await this.ensureAppExists())) {
34
+ return;
35
+ }
36
+ if (this.app.type === 'storefront') {
37
+ await this.getStorefrontToPush(flags);
38
+ }
39
+ await this.deployAppFrontend(force);
40
+ }
41
+ }
@@ -0,0 +1,19 @@
1
+ import { FrontendProjectType } from '../../../lib/apps/index.js';
2
+ import { PushAppCommand } from '../../../push-app-command.js';
3
+ export default class AppFrontendDev extends PushAppCommand {
4
+ static examples: string[];
5
+ static flags: {
6
+ 'no-push': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
+ port: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ 'storefront-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
+ 'storefront-select': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
+ };
11
+ static orientation: {
12
+ env: string;
13
+ };
14
+ static summary: string;
15
+ logAppLocalUrl(storeId: string, sessionId: string): void;
16
+ run(): Promise<void>;
17
+ startDevServer(projectType: FrontendProjectType, port?: number): Promise<void>;
18
+ updateLocalProxy(proxyUrl: string, spinner: any, storefrontId?: string): Promise<void>;
19
+ }
@@ -0,0 +1,108 @@
1
+ import { Flags } from '@oclif/core';
2
+ import getPort, { portNumbers } from 'get-port';
3
+ import ngrok from 'ngrok';
4
+ import ora from 'ora';
5
+ import { default as localConfig } from '../../../lib/config.js';
6
+ import style from '../../../lib/style.js';
7
+ import { PushAppCommand } from '../../../push-app-command.js';
8
+ const FALLBACK_PORT = 3000;
9
+ export default class AppFrontendDev extends PushAppCommand {
10
+ static examples = [
11
+ 'swell app frontend dev',
12
+ 'swell app frontend dev --no-push',
13
+ 'swell app frontend dev --port 3000',
14
+ 'swell app frontend dev --storefront-select',
15
+ 'swell app frontend dev --storefront-id <id>',
16
+ ];
17
+ static flags = {
18
+ 'no-push': Flags.boolean({
19
+ description: 'skip pushing app files initially',
20
+ }),
21
+ port: Flags.integer({
22
+ char: 'p',
23
+ description: 'override the default port to run the app locally',
24
+ }),
25
+ 'storefront-id': Flags.string({
26
+ description: 'identify a storefront to preview with and push theme files to',
27
+ }),
28
+ 'storefront-select': Flags.boolean({
29
+ default: false,
30
+ description: 'prompt to select a storefront to preview with',
31
+ }),
32
+ };
33
+ static orientation = {
34
+ env: 'test',
35
+ };
36
+ static summary = `Run a frontend app in dev mode from your local machine.`;
37
+ logAppLocalUrl(storeId, sessionId) {
38
+ const frontendAppRoute = this.app.type === 'storefront' && this.app.storefront?.external
39
+ ? `/${this.app.private_id}`
40
+ : '';
41
+ this.log(`View it at ${style.link(`${this.localFrontendUrl(storeId, sessionId)}${frontendAppRoute}`)}\n`);
42
+ }
43
+ async run() {
44
+ const { flags } = await this.parse(AppFrontendDev);
45
+ const { port } = flags;
46
+ const noPush = flags['no-push'];
47
+ if (!(await this.ensureAppExists(undefined, false))) {
48
+ return;
49
+ }
50
+ if (!noPush) {
51
+ await this.pushAppConfigs();
52
+ }
53
+ if (this.app.type === 'storefront') {
54
+ await this.getStorefrontToPush(flags);
55
+ this.logStorefrontConnected();
56
+ }
57
+ this.saveCurrentStorefront();
58
+ const projectType = this.getFrontendProjectType();
59
+ await this.startDevServer(projectType, port);
60
+ }
61
+ async startDevServer(projectType, port) {
62
+ const currentStore = localConfig.getDefaultStore();
63
+ const spinner = ora();
64
+ // Find an open port starting at 3000
65
+ const freePort = port ||
66
+ (await getPort({ port: portNumbers(3000, 3100) })) ||
67
+ FALLBACK_PORT;
68
+ this.log(`Starting ${projectType.name} dev server...\n`);
69
+ // Start ngrok proxy
70
+ const proxyUrl = await ngrok.connect({
71
+ addr: freePort,
72
+ region: 'us', // TODO: make it configurable
73
+ });
74
+ await this.updateLocalProxy(proxyUrl, spinner, this.storefront?.id);
75
+ return this.exec(projectType.devCommand.replace('${PORT}', String(freePort)), (string) => {
76
+ // Hide useless astro warning about image optimization
77
+ if (string.includes('The current configuration does not support image optimization')) {
78
+ return false;
79
+ }
80
+ // Hide useless astro warning about .dev.vars
81
+ if (string.includes('There is no `.dev.vars` file')) {
82
+ return false;
83
+ }
84
+ // Hide astro localhost URL/network as it can confuse the user
85
+ if (string.includes('┃ Local') || string.includes('┃ Network')) {
86
+ return false;
87
+ }
88
+ // Started, show app url (TODO for nextjs)
89
+ if (string.includes('watching for file changes')) {
90
+ spinner.succeed(`Started ${projectType.name} dev server on port ${freePort}.\n`);
91
+ const sessionId = localConfig.getSessionId(currentStore);
92
+ setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
93
+ this.watchForChanges();
94
+ }
95
+ });
96
+ }
97
+ async updateLocalProxy(proxyUrl, spinner, storefrontId) {
98
+ const storefront = this.app.type === 'storefront' &&
99
+ (await this.getAppStorefront({ storefront_id: storefrontId }));
100
+ await this.handleRequestErrors(async () => this.api.put({ adminPath: `/client/apps/${this.app.id}/local-proxy` }, {
101
+ body: {
102
+ proxy_url: proxyUrl,
103
+ storefront_id: storefront?.id || null,
104
+ storefront_slug: storefront?.slug || null,
105
+ },
106
+ }), () => spinner.fail('Error starting local proxy'));
107
+ }
108
+ }
@@ -13,7 +13,6 @@ const FIELDS_TO_DISPLAY = [
13
13
  'published',
14
14
  'date_created',
15
15
  'date_updated',
16
- 'installed',
17
16
  'client',
18
17
  ];
19
18
  export default class AppInfo extends RemoteAppCommand {
@@ -46,25 +45,31 @@ it will output all versions of the app instead.`;
46
45
  async showAppInfo() {
47
46
  const spinner = ora();
48
47
  spinner.start('Retrieving app info...');
49
- this.app = await this.getAppWithConfig().catch((error) => {
50
- spinner.fail(`Error fetching app info:`);
51
- throw error;
52
- });
48
+ const app = await this.getAppWithConfig().catch(() => null);
49
+ this.app = app || this.swellConfig.store;
50
+ if (!this.app) {
51
+ spinner.fail('App not found');
52
+ return;
53
+ }
53
54
  if (spinner.isSpinning) {
54
55
  spinner.stop();
55
56
  }
56
57
  // we pull versions to make sure that what is displayed is the latest
57
- const versions = await this.getVersions();
58
+ const versions = app ? await this.getVersions() : [];
58
59
  // Output the app field information, e.g.:
59
60
  // Name: My App
60
61
  const fieldDefinitions = new FieldDefinitions(this.app, versions);
61
62
  for (const field of FIELDS_TO_DISPLAY) {
62
63
  const fieldDef = fieldDefinitions.getFieldDefinition(field);
63
- this.log(style.appConfigName(`${fieldDef.label}:`), fieldDef.value);
64
+ if (fieldDef.value) {
65
+ this.log(style.appConfigName(`${fieldDef.label}:`), fieldDef.value);
66
+ }
64
67
  }
65
68
  // dashboard url is built, not part of the app object
66
69
  // we display it at the end for better readability and simplicity
67
- this.log(style.appConfigName('Dashboard:'), style.link(this.dashboardUrl({}, this.app.client_id)));
70
+ if (this.app.client_id) {
71
+ this.log(style.appConfigName('Dashboard:'), style.link(this.dashboardUrl({}, this.app.client_id)));
72
+ }
68
73
  }
69
74
  async showVersions() {
70
75
  const spinner = ora();
@@ -81,7 +86,7 @@ it will output all versions of the app instead.`;
81
86
  spinner.succeed(`${this.app.name} app versions:\n`);
82
87
  const displayData = versions.map((appVersion) => [
83
88
  style.appConfigValue(appVersion.version),
84
- appVersion.notes,
89
+ appVersion.description,
85
90
  new Date(appVersion.date_created).toUTCString(),
86
91
  ]);
87
92
  this.log(table(displayData, {
@@ -0,0 +1,12 @@
1
+ import CreateApp from '../create/app.js';
2
+ export default class InitApp extends CreateApp {
3
+ static description: string;
4
+ static examples: {
5
+ command: string;
6
+ description: string;
7
+ }[];
8
+ static flags: {
9
+ yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,34 @@
1
+ import { Flags } from '@oclif/core';
2
+ import path from 'node:path';
3
+ import CreateApp from '../create/app.js';
4
+ export default class InitApp extends CreateApp {
5
+ static description = 'Initialize app swell.json in the current directory.';
6
+ static examples = [
7
+ {
8
+ command: 'swell app init',
9
+ description: 'Initialize app following command prompts.',
10
+ },
11
+ {
12
+ command: 'swell app init -y',
13
+ description: 'Initialize app and accept all default values.',
14
+ },
15
+ ];
16
+ static flags = {
17
+ yes: Flags.boolean({
18
+ char: 'y',
19
+ description: `accept all default values`,
20
+ }),
21
+ };
22
+ async run() {
23
+ const { flags } = await this.parse(InitApp);
24
+ const { yes: confirmYes } = flags;
25
+ const id = process.cwd().split(path.sep).pop() || 'Swell App';
26
+ await this.createSwellConfig({
27
+ allowOverwrite: false,
28
+ inputId: id,
29
+ inputType: this.appType,
30
+ inputYes: confirmYes,
31
+ nestedPath: false,
32
+ });
33
+ }
34
+ }
@@ -7,11 +7,11 @@ export default class AppInstall extends RemoteAppCommand {
7
7
  store: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
8
  version: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
9
  };
10
+ static orientation: {
11
+ env: string;
12
+ };
10
13
  static summary: string;
11
14
  run(): Promise<void>;
12
- private installApp;
13
- private installConfirmationMessage;
14
- private updateApp;
15
- private getInstalledApp;
16
15
  private confirmRemoveDevelopmentApp;
16
+ private installConfirmationMessage;
17
17
  }
@@ -1,19 +1,19 @@
1
1
  import { confirm } from '@inquirer/prompts';
2
2
  import { Flags } from '@oclif/core';
3
- import * as semver from 'semver';
4
3
  import ora from 'ora';
4
+ import * as semver from 'semver';
5
5
  import config from '../../lib/config.js';
6
+ import { selectEnvironmentId, selectLoggedInStoreId, } from '../../lib/stores.js';
6
7
  import style from '../../lib/style.js';
7
- import { selectLoggedInStoreId, selectEnvironmentId, } from '../../lib/stores.js';
8
8
  import { RemoteAppCommand } from '../../remote-app-command.js';
9
9
  export default class AppInstall extends RemoteAppCommand {
10
10
  static description = `Without a version specified, install the latest version of the app.
11
11
 
12
12
  Without a store specified, install the app in the Live environment of the current store.`;
13
13
  static examples = [
14
- 'swell install',
15
- 'swell install -v 3.2.7',
16
- 'swell install -v 1.0.0 -s my-store-id',
14
+ 'swell app install',
15
+ 'swell app install -v 3.2.7',
16
+ 'swell app install -v 1.0.0 -s my-store-id',
17
17
  ];
18
18
  static flags = {
19
19
  env: Flags.string({
@@ -35,6 +35,9 @@ Without a store specified, install the app in the Live environment of the curren
35
35
  },
36
36
  }),
37
37
  };
38
+ static orientation = {
39
+ env: 'test',
40
+ };
38
41
  static summary = 'Install an existing app in another store environment.';
39
42
  async run() {
40
43
  const { flags } = await this.parse(AppInstall);
@@ -91,7 +94,7 @@ Without a store specified, install the app in the Live environment of the curren
91
94
  }
92
95
  }
93
96
  else {
94
- extraMessage = ` ${style.appConfigValue(this.app.name)} v${versionToInstall} on ${style.storeId(storeToInstall)} [${envToInstall ? envToInstall : 'live'}]`;
97
+ extraMessage = ` ${style.appConfigValue(this.app.name)} v${versionToInstall} on ${style.storeId(storeToInstall)} [${envToInstall || 'live'}]`;
95
98
  }
96
99
  const shouldContinue = existingVersion ||
97
100
  (await this.confirmRemoveDevelopmentApp(storeToInstall));
@@ -99,60 +102,34 @@ Without a store specified, install the app in the Live environment of the curren
99
102
  return;
100
103
  }
101
104
  const spinner = ora();
105
+ this.log();
102
106
  // install or update app depending on whether it's already installed
103
107
  let request;
104
108
  if (existingInstalledApp?.id) {
105
- spinner.start(`Updating${extraMessage}...`);
106
- request = async () => this.updateApp(versionToInstall, envToInstall);
109
+ spinner.start(`Updating${extraMessage}`);
110
+ request = async () => this.updateInstalledApp(versionToInstall, spinner);
107
111
  }
108
112
  else {
109
- spinner.start(`Installing${extraMessage}...`);
110
- request = async () => this.installApp(versionToInstall, envToInstall);
113
+ spinner.start(`Installing${extraMessage}`);
114
+ request = async () => this.installApp(versionToInstall, spinner);
111
115
  }
112
116
  const response = await this.handleRequestErrors(request, () => spinner.fail('Something went wrong.'));
113
117
  if (response?.errors) {
114
118
  spinner.fail(`Error installing app: ${JSON.stringify(response.errors)}`);
115
119
  }
116
- spinner.succeed(`Install complete. Manage app settings in your dashboard at ${style.link(this.dashboardUrl({ flags: { env: envToInstall } }, storeToInstall))}.`);
117
- }
118
- async installApp(version, env) {
119
- return this.api.post({ adminPath: `/client/apps` },
120
- // eslint-disable-next-line camelcase
121
- { body: { app_id: this.app.id, version } });
122
- }
123
- installConfirmationMessage(storeToInstall, envToInstall, versionToInstall, existingVersion) {
124
- const message = [];
125
- if (existingVersion && semver.gt(versionToInstall, existingVersion)) {
126
- message.push('Updating');
127
- }
128
- else if (existingVersion &&
129
- semver.lt(versionToInstall, existingVersion)) {
130
- message.push('Rolling back');
131
- }
132
120
  else {
133
- message.push('Install');
121
+ spinner.succeed(`${existingInstalledApp?.id ? 'Update' : 'Install'} complete.\n`);
122
+ this.log(`Manage app settings in your dashboard at ${style.link(this.dashboardUrl({ flags: { env: envToInstall } }, storeToInstall))}.`);
134
123
  }
135
- let versionToShow = versionToInstall;
136
- versionToShow += versionToInstall === this.app.version ? ' (latest)' : '';
137
- message.push(style.appConfigValue(this.app.name), `v${versionToShow}`, `on ${style.storeId(storeToInstall)} [${envToInstall ? envToInstall : 'live'}]?`);
138
- return message.join(' ');
139
- }
140
- async updateApp(version, env) {
141
- await this.api.put({ adminPath: `/client/apps/${this.app.id}` }, { body: { version } });
142
- }
143
- async getInstalledApp() {
144
- // check if app is already installed on store
145
- return await this.api.get({ adminPath: `/client/apps/${this.app.id}` }, {});
146
124
  }
147
125
  async confirmRemoveDevelopmentApp(storeToInstall) {
148
126
  const existingSourcedApps = await this.api.get({ adminPath: `/client/apps` }, {
149
127
  query: { source_id: this.app.id },
150
128
  });
151
129
  if (existingSourcedApps.results[0]?.id) {
152
- console.log(existingSourcedApps.results[0]);
153
130
  const continueInstall = await confirm({
154
- message: `${style.storeId(storeToInstall)} has a development version of ${style.appConfigValue(this.app.name)}. Do you want to replace it with an installed instance?`,
155
131
  default: false,
132
+ message: `${style.storeId(storeToInstall)} has a development version of ${style.appConfigValue(this.app.name)}. Do you want to replace it with an installed instance?`,
156
133
  });
157
134
  if (!continueInstall) {
158
135
  return false;
@@ -168,12 +145,29 @@ Without a store specified, install the app in the Live environment of the curren
168
145
  });
169
146
  spinner.succeed('Development app removed.');
170
147
  }
171
- catch (err) {
148
+ catch (error) {
172
149
  spinner.fail(`Error removing development app from ${style.storeId(storeToInstall)}.`);
173
- this.logError(err.message);
150
+ this.logError(error.message);
174
151
  return false;
175
152
  }
176
153
  }
177
154
  return true;
178
155
  }
156
+ installConfirmationMessage(storeToInstall, envToInstall, versionToInstall, existingVersion) {
157
+ const message = [];
158
+ if (existingVersion && semver.gt(versionToInstall, existingVersion)) {
159
+ message.push('Updating');
160
+ }
161
+ else if (existingVersion &&
162
+ semver.lt(versionToInstall, existingVersion)) {
163
+ message.push('Rolling back');
164
+ }
165
+ else {
166
+ message.push('Install');
167
+ }
168
+ let versionToShow = versionToInstall;
169
+ versionToShow += versionToInstall === this.app.version ? ' (latest)' : '';
170
+ message.push(style.appConfigValue(this.app.name), `v${versionToShow}`, `on ${style.storeId(storeToInstall)} [${envToInstall || 'live'}]?`);
171
+ return message.join(' ');
172
+ }
179
173
  }
@@ -0,0 +1,15 @@
1
+ import { PushAppCommand } from '../../push-app-command.js';
2
+ export default class AppPull extends PushAppCommand {
3
+ static args: {
4
+ appId: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
5
+ targetPath: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: {
10
+ force: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
11
+ };
12
+ static summary: string;
13
+ logPulledChanges(pulled: string[], removed: string[]): void;
14
+ run(): Promise<void>;
15
+ }
@@ -0,0 +1,50 @@
1
+ import { Args, Flags } from '@oclif/core';
2
+ import * as path from 'node:path';
3
+ import { ConfigPaths } from '../../lib/apps/index.js';
4
+ import style from '../../lib/style.js';
5
+ import { PushAppCommand } from '../../push-app-command.js';
6
+ export default class AppPull extends PushAppCommand {
7
+ static args = {
8
+ appId: Args.string({ default: '', description: 'app id to pull' }),
9
+ targetPath: Args.string({
10
+ default: '',
11
+ description: 'path to download app files into',
12
+ }),
13
+ };
14
+ static description = `Pull all app files, a specific file, or a specific configuration
15
+ type from an app in your store's test environment to your local machine.
16
+
17
+ If APPID is not specified, you will be prompted with a list of apps to choose from.
18
+
19
+ App file directories:
20
+ ${Object.values(ConfigPaths)
21
+ .map((dir) => style.path(`${dir}/`))
22
+ .join('\n')}\n${style.path(`frontend/`)}`;
23
+ static examples = [
24
+ 'swell app pull',
25
+ 'swell app pull example_app',
26
+ 'swell app pull example_app example-folder',
27
+ 'swell app pull --force',
28
+ ];
29
+ static flags = {
30
+ force: Flags.boolean({
31
+ default: false,
32
+ description: 'force pull all files',
33
+ }),
34
+ };
35
+ static summary = 'Pull app files from Swell to your local machine.';
36
+ logPulledChanges(pulled, removed) {
37
+ const typeLabel = this.appType === 'theme' ? 'theme' : 'app';
38
+ const changeCount = pulled.length + removed.length;
39
+ const pluralFiles = changeCount === 1 ? 'file' : 'files';
40
+ this.log(`Pulled ${changeCount} ${pluralFiles} from ${style.appConfigValue(this.app.name)} ${typeLabel} in ${style.path(path.resolve(this.appPath))}.`);
41
+ }
42
+ async run() {
43
+ const { args, flags } = await this.parse(AppPull);
44
+ const { force } = flags;
45
+ await this.ensureLoggedIn();
46
+ this.app = (await this.getAppToPull(args));
47
+ const { pulled, removed } = await this.pullAppConfigs(force);
48
+ this.logPulledChanges(pulled, removed);
49
+ }
50
+ }
@@ -1,20 +1,17 @@
1
- import { RemoteAppCommand } from '../../remote-app-command.js';
2
- export default class AppPush extends RemoteAppCommand {
3
- static description: string;
4
- static examples: string[];
1
+ import { PushAppCommand } from '../../push-app-command.js';
2
+ export default class AppPush extends PushAppCommand {
5
3
  static args: {
6
4
  file: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
7
5
  };
8
- static flags: {
9
- file: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
10
- };
11
- static orientation: {
12
- env: string;
13
- };
6
+ static delayOrientation: boolean;
7
+ static description: string;
8
+ static examples: string[];
9
+ static flags: any;
10
+ static orientation: any;
14
11
  static summary: string;
12
+ resolvePushPaths(file: string): {
13
+ filePath: string;
14
+ relativePath: string;
15
+ };
15
16
  run(): Promise<void>;
16
- private logDashboardUrl;
17
- private pushConfigPath;
18
- private pushFile;
19
- private confirmRemoveInstalledApp;
20
17
  }