@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
@@ -5,10 +5,9 @@ import fs from 'node:fs';
5
5
  import ora from 'ora';
6
6
  import { default as localConfig } from '../../lib/config.js';
7
7
  import style from '../../lib/style.js';
8
- import { PushAppCommand } from '../../push-app-command.js';
9
8
  import { ThemeSync } from '../../lib/theme-sync.js';
9
+ import { PushAppCommand } from '../../push-app-command.js';
10
10
  export default class AppThemeDev extends PushAppCommand {
11
- appType = 'theme';
12
11
  static examples = [
13
12
  'swell theme dev',
14
13
  'swell theme dev --local',
@@ -43,6 +42,7 @@ export default class AppThemeDev extends PushAppCommand {
43
42
  }),
44
43
  };
45
44
  static summary = `Run a theme in dev mode from your local machine.`;
45
+ appType = 'theme';
46
46
  logAppLocalUrl(storeId, sessionId) {
47
47
  this.log(`View it at ${style.link(this.localFrontendUrl(storeId, sessionId))}\n`);
48
48
  }
@@ -1,9 +1,9 @@
1
1
  import InitApp from '../app/init.js';
2
2
  export default class InitTheme extends InitApp {
3
- appType: string;
4
3
  static description: string;
5
4
  static examples: {
6
5
  command: string;
7
6
  description: string;
8
7
  }[];
8
+ appType: string;
9
9
  }
@@ -1,6 +1,5 @@
1
1
  import InitApp from '../app/init.js';
2
2
  export default class InitTheme extends InitApp {
3
- appType = 'theme';
4
3
  static description = 'Initialize theme swell.json in the current directory.';
5
4
  static examples = [
6
5
  {
@@ -12,4 +11,5 @@ export default class InitTheme extends InitApp {
12
11
  description: 'Initialize theme and accept all default values.',
13
12
  },
14
13
  ];
14
+ appType = 'theme';
15
15
  }
@@ -1,10 +1,10 @@
1
1
  import AppPull from '../app/pull.js';
2
2
  export default class AppThemePull extends AppPull {
3
- appType: string;
4
3
  static args: {
5
4
  appId: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
6
5
  targetPath: import("@oclif/core/lib/interfaces/parser.js").Arg<string, Record<string, unknown>>;
7
6
  };
7
+ static delayOrientation: boolean;
8
8
  static description: string;
9
9
  static examples: string[];
10
10
  static flags: {
@@ -15,6 +15,6 @@ export default class AppThemePull extends AppPull {
15
15
  'sync-app': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
16
16
  };
17
17
  static summary: string;
18
- static delayOrientation: boolean;
18
+ appType: string;
19
19
  run(): Promise<void>;
20
20
  }
@@ -3,7 +3,6 @@ import { ThemeConfigPaths } from '../../lib/apps/index.js';
3
3
  import style from '../../lib/style.js';
4
4
  import AppPull from '../app/pull.js';
5
5
  export default class AppThemePull extends AppPull {
6
- appType = 'theme';
7
6
  static args = {
8
7
  appId: Args.string({
9
8
  default: '',
@@ -14,6 +13,7 @@ export default class AppThemePull extends AppPull {
14
13
  description: 'path to download theme files into',
15
14
  }),
16
15
  };
16
+ static delayOrientation = true;
17
17
  static description = `Pull all theme files, a specific file, or a specific configuration
18
18
  type from a theme in your store's test environment to your local machine.
19
19
 
@@ -53,7 +53,7 @@ ${Object.values(ThemeConfigPaths)
53
53
  }),
54
54
  };
55
55
  static summary = 'Pull theme files from Swell to your local machine.';
56
- static delayOrientation = true;
56
+ appType = 'theme';
57
57
  async run() {
58
58
  const { args, flags } = await this.parse(AppThemePull);
59
59
  const { force } = flags;
@@ -1,6 +1,5 @@
1
1
  import AppPush from '../app/push.js';
2
2
  export default class AppThemePush extends AppPush {
3
- appType: string;
4
3
  static args: {
5
4
  file: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
6
5
  };
@@ -14,5 +13,6 @@ export default class AppThemePush extends AppPush {
14
13
  'sync-app': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
15
14
  };
16
15
  static summary: string;
16
+ appType: string;
17
17
  run(): Promise<void>;
18
18
  }
@@ -4,7 +4,6 @@ import { default as localConfig } from '../../lib/config.js';
4
4
  import style from '../../lib/style.js';
5
5
  import AppPush from '../app/push.js';
6
6
  export default class AppThemePush extends AppPush {
7
- appType = 'theme';
8
7
  static args = {
9
8
  file: Args.string({
10
9
  char: 'f',
@@ -56,6 +55,7 @@ ${Object.values(ThemeConfigPaths)
56
55
  }),
57
56
  };
58
57
  static summary = 'Push local files to your Swell theme.';
58
+ appType = 'theme';
59
59
  async run() {
60
60
  const { args, flags } = await this.parse(AppThemePush);
61
61
  const { file } = args;
@@ -3,14 +3,15 @@ export declare abstract class CreateAppCommand extends SwellCommand {
3
3
  static baseFlags: {
4
4
  frontend: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
5
5
  'storefront-app': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
+ 'integration-type': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ 'integration-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
6
8
  yes: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
7
9
  };
8
10
  private devApi;
9
11
  constructor(argv: string[], config: any);
10
- addAllowedHostsToAstro(configPath: string): Promise<void>;
11
12
  addAllowedHostsToAngular(configPath: string): Promise<void>;
13
+ addAllowedHostsToAstro(configPath: string): Promise<void>;
12
14
  addAllowedHostsToNuxt(configPath: string): Promise<void>;
13
- private addFrontendAllowedHosts;
14
15
  createAppConfigFolders(swellConfig: any): Promise<void>;
15
16
  createFrontendApp(swellConfig: any, flags: any, directCreate?: boolean, kind?: string): Promise<boolean>;
16
17
  createStorefrontApp(swellConfig: any, flags: any, directCreate?: boolean): Promise<boolean>;
@@ -19,6 +20,15 @@ export declare abstract class CreateAppCommand extends SwellCommand {
19
20
  execWithStdio(cwd: string, command: string, onOutput?: (string: string) => any | false): Promise<void>;
20
21
  findPackageManager(pkg?: string): Promise<string | undefined>;
21
22
  getInstalledStorefrontApps(): Promise<any[] | boolean>;
23
+ getProjectType(frameworkType: string): {
24
+ installCommand: string;
25
+ buildCommand?: string | undefined;
26
+ devCommand: string;
27
+ mainPackage: string;
28
+ name: string;
29
+ slug: string;
30
+ };
22
31
  setupPackage(name: string, config: any, pkg: string): Promise<void>;
23
32
  tryPackageSetup(name: string, config: any, pkg: string): Promise<void>;
33
+ private addFrontendAllowedHosts;
24
34
  }
@@ -2,28 +2,35 @@ import { confirm, select } from '@inquirer/prompts';
2
2
  import { Flags } from '@oclif/core';
3
3
  import { $ } from 'execa';
4
4
  import { exec } from 'node:child_process';
5
+ import fs from 'node:fs/promises';
5
6
  import path from 'node:path';
6
7
  import Stream from 'node:stream';
7
8
  import { promisify } from 'node:util';
8
9
  import ora from 'ora';
9
10
  import Api from './lib/api.js';
10
- import { FrontendProjectTypes, getAllConfigPaths, writeFile, writeJsonFile, } from './lib/apps/index.js';
11
+ import { FrontendProjectTypes, getFrontendProjectValidValues, getAllConfigPaths, getFrontendProjectSlugs, writeFile, writeJsonFile, } from './lib/apps/index.js';
11
12
  import style from './lib/style.js';
12
13
  import { SwellCommand } from './swell-command.js';
13
- import fs from 'node:fs/promises';
14
14
  const execAsync = promisify(exec);
15
15
  export class CreateAppCommand extends SwellCommand {
16
16
  static baseFlags = {
17
17
  frontend: Flags.string({
18
- description: `create a starter framework for a hosted frontend`,
19
- options: FrontendProjectTypes.map(({ slug }) => slug),
18
+ description: `create a starter framework for a hosted frontend (default in -y: none)`,
19
+ options: getFrontendProjectSlugs(true, false),
20
20
  }),
21
21
  'storefront-app': Flags.string({
22
22
  description: `id of an installed storefront app to create a theme for, if applicable`,
23
23
  }),
24
+ 'integration-type': Flags.string({
25
+ description: `integration type for integration app, if applicable`,
26
+ options: ['generic', 'payment', 'shipping', 'tax'],
27
+ }),
28
+ 'integration-id': Flags.string({
29
+ description: `unique identifier for the integration service (required for payment/shipping/tax; use 'card' for credit cards)`,
30
+ }),
24
31
  yes: Flags.boolean({
25
32
  char: 'y',
26
- description: `accept all default values`,
33
+ description: `accept all default values; non-interactive, never prompts; errors if required values are missing`,
27
34
  }),
28
35
  };
29
36
  devApi;
@@ -31,48 +38,11 @@ export class CreateAppCommand extends SwellCommand {
31
38
  super(argv, config);
32
39
  this.devApi = new Api(undefined, 'test');
33
40
  }
34
- async addAllowedHostsToAstro(configPath) {
35
- const astroConfigPath = path.join(configPath, 'frontend', 'astro.config.mjs');
36
- let content;
37
- try {
38
- content = await fs.readFile(astroConfigPath, 'utf-8');
39
- }
40
- catch {
41
- return;
42
- }
43
- if (content.includes('allowedHosts')) {
44
- return;
45
- }
46
- const lines = content.split('\n');
47
- let insertIndex = -1;
48
- for (let i = 0; i < lines.length; i++) {
49
- if (lines[i].includes('defineConfig({')) {
50
- insertIndex = i + 1;
51
- break;
52
- }
53
- }
54
- if (insertIndex === -1) {
55
- return;
56
- }
57
- // Detect indentation from next line or use default
58
- const nextLine = lines[insertIndex];
59
- const indentMatch = nextLine?.match(/^(\s+)/);
60
- const baseIndent = indentMatch ? indentMatch[1] : ' ';
61
- const viteConfig = [
62
- `${baseIndent}vite: {`,
63
- `${baseIndent} server: {`,
64
- `${baseIndent} allowedHosts: ['.ngrok.app', '.loca.lt'],`,
65
- `${baseIndent} },`,
66
- `${baseIndent}},`,
67
- ];
68
- lines.splice(insertIndex, 0, ...viteConfig);
69
- await fs.writeFile(astroConfigPath, lines.join('\n'), 'utf-8');
70
- }
71
41
  async addAllowedHostsToAngular(configPath) {
72
42
  const angularJsonPath = path.join(configPath, 'frontend', 'angular.json');
73
43
  let content;
74
44
  try {
75
- content = await fs.readFile(angularJsonPath, 'utf-8');
45
+ content = await fs.readFile(angularJsonPath, 'utf8');
76
46
  }
77
47
  catch {
78
48
  return;
@@ -84,7 +54,7 @@ export class CreateAppCommand extends SwellCommand {
84
54
  catch {
85
55
  return;
86
56
  }
87
- const projects = config.projects;
57
+ const { projects } = config;
88
58
  if (!projects || typeof projects !== 'object') {
89
59
  return;
90
60
  }
@@ -105,13 +75,13 @@ export class CreateAppCommand extends SwellCommand {
105
75
  return;
106
76
  }
107
77
  project.architect.serve.options.allowedHosts = ['.ngrok.app', '.loca.lt'];
108
- await fs.writeFile(angularJsonPath, JSON.stringify(config, null, 2), 'utf-8');
78
+ await fs.writeFile(angularJsonPath, JSON.stringify(config, null, 2), 'utf8');
109
79
  }
110
- async addAllowedHostsToNuxt(configPath) {
111
- const nuxtConfigPath = path.join(configPath, 'frontend', 'nuxt.config.ts');
80
+ async addAllowedHostsToAstro(configPath) {
81
+ const astroConfigPath = path.join(configPath, 'frontend', 'astro.config.mjs');
112
82
  let content;
113
83
  try {
114
- content = await fs.readFile(nuxtConfigPath, 'utf-8');
84
+ content = await fs.readFile(astroConfigPath, 'utf8');
115
85
  }
116
86
  catch {
117
87
  return;
@@ -121,8 +91,8 @@ export class CreateAppCommand extends SwellCommand {
121
91
  }
122
92
  const lines = content.split('\n');
123
93
  let insertIndex = -1;
124
- for (let i = 0; i < lines.length; i++) {
125
- if (lines[i].includes('defineNuxtConfig({')) {
94
+ for (const [i, line] of lines.entries()) {
95
+ if (line.includes('defineConfig({')) {
126
96
  insertIndex = i + 1;
127
97
  break;
128
98
  }
@@ -130,6 +100,7 @@ export class CreateAppCommand extends SwellCommand {
130
100
  if (insertIndex === -1) {
131
101
  return;
132
102
  }
103
+ // Detect indentation from next line or use default
133
104
  const nextLine = lines[insertIndex];
134
105
  const indentMatch = nextLine?.match(/^(\s+)/);
135
106
  const baseIndent = indentMatch ? indentMatch[1] : ' ';
@@ -141,23 +112,43 @@ export class CreateAppCommand extends SwellCommand {
141
112
  `${baseIndent}},`,
142
113
  ];
143
114
  lines.splice(insertIndex, 0, ...viteConfig);
144
- await fs.writeFile(nuxtConfigPath, lines.join('\n'), 'utf-8');
115
+ await fs.writeFile(astroConfigPath, lines.join('\n'), 'utf8');
145
116
  }
146
- async addFrontendAllowedHosts(projectType, configPath) {
147
- // Configure allowedHosts for frameworks using Vite dev server
148
- // This allows tunneling services (ngrok, localtunnel) to proxy requests to the local dev environment
149
- switch (projectType.slug) {
150
- case 'astro':
151
- await this.addAllowedHostsToAstro(configPath);
152
- break;
153
- case 'angular':
154
- await this.addAllowedHostsToAngular(configPath);
155
- break;
156
- case 'nuxt':
157
- await this.addAllowedHostsToNuxt(configPath);
117
+ async addAllowedHostsToNuxt(configPath) {
118
+ const nuxtConfigPath = path.join(configPath, 'frontend', 'nuxt.config.ts');
119
+ let content;
120
+ try {
121
+ content = await fs.readFile(nuxtConfigPath, 'utf8');
122
+ }
123
+ catch {
124
+ return;
125
+ }
126
+ if (content.includes('allowedHosts')) {
127
+ return;
128
+ }
129
+ const lines = content.split('\n');
130
+ let insertIndex = -1;
131
+ for (const [i, line] of lines.entries()) {
132
+ if (line.includes('defineNuxtConfig({')) {
133
+ insertIndex = i + 1;
158
134
  break;
159
- // Other frameworks don't require allowedHosts configuration
135
+ }
136
+ }
137
+ if (insertIndex === -1) {
138
+ return;
160
139
  }
140
+ const nextLine = lines[insertIndex];
141
+ const indentMatch = nextLine?.match(/^(\s+)/);
142
+ const baseIndent = indentMatch ? indentMatch[1] : ' ';
143
+ const viteConfig = [
144
+ `${baseIndent}vite: {`,
145
+ `${baseIndent} server: {`,
146
+ `${baseIndent} allowedHosts: ['.ngrok.app', '.loca.lt'],`,
147
+ `${baseIndent} },`,
148
+ `${baseIndent}},`,
149
+ ];
150
+ lines.splice(insertIndex, 0, ...viteConfig);
151
+ await fs.writeFile(nuxtConfigPath, lines.join('\n'), 'utf8');
161
152
  }
162
153
  async createAppConfigFolders(swellConfig) {
163
154
  for (const type of getAllConfigPaths(swellConfig.get('type'))) {
@@ -171,13 +162,11 @@ export class CreateAppCommand extends SwellCommand {
171
162
  async createFrontendApp(swellConfig, flags, directCreate, kind) {
172
163
  const spinner = ora();
173
164
  const configPath = path.dirname(swellConfig.path);
174
- const frameworkType = flags.frontend ||
165
+ // use 'none' by default for non-interactive mode
166
+ const inputFrontend = flags.yes ? flags.frontend || 'none' : flags.frontend;
167
+ const frameworkType = inputFrontend ||
175
168
  (await select({
176
169
  choices: [
177
- ...FrontendProjectTypes.map(({ name, slug }) => ({
178
- name,
179
- value: slug,
180
- })),
181
170
  ...(directCreate
182
171
  ? []
183
172
  : [
@@ -186,19 +175,17 @@ export class CreateAppCommand extends SwellCommand {
186
175
  value: null,
187
176
  },
188
177
  ]),
178
+ ...FrontendProjectTypes.map(({ name, slug }) => ({
179
+ name,
180
+ value: slug,
181
+ })),
189
182
  ],
190
183
  message: directCreate
191
184
  ? `Choose framework for your hosted${kind ? ` ${kind} ` : ' '}app frontend`
192
185
  : `Install framework for a hosted${kind ? ` ${kind} ` : ' '}app frontend?`,
193
186
  }));
194
- if (frameworkType) {
195
- const projectType = FrontendProjectTypes.find((type) => type.slug === frameworkType);
196
- if (!projectType) {
197
- this.error(`Could not find project type: ${frameworkType}`);
198
- }
199
- if (!projectType.installCommand) {
200
- this.error(`Project type ${projectType.name} cannot be installed (legacy type)`);
201
- }
187
+ if (frameworkType && frameworkType !== 'none') {
188
+ const projectType = this.getProjectType(frameworkType);
202
189
  this.log();
203
190
  spinner.start(`Creating ${projectType?.name} frontend app (this may take a while)...`);
204
191
  try {
@@ -223,11 +210,11 @@ export class CreateAppCommand extends SwellCommand {
223
210
  // Ensure frontend package.json has correct name for workspace
224
211
  const frontendPkgPath = path.join(configPath, 'frontend', 'package.json');
225
212
  try {
226
- const pkgContent = await fs.readFile(frontendPkgPath, 'utf-8');
213
+ const pkgContent = await fs.readFile(frontendPkgPath, 'utf8');
227
214
  const pkg = JSON.parse(pkgContent);
228
215
  if (pkg.name !== 'frontend') {
229
216
  pkg.name = 'frontend';
230
- await fs.writeFile(frontendPkgPath, JSON.stringify(pkg, null, 2), 'utf-8');
217
+ await fs.writeFile(frontendPkgPath, JSON.stringify(pkg, null, 2), 'utf8');
231
218
  }
232
219
  }
233
220
  catch {
@@ -242,7 +229,7 @@ export class CreateAppCommand extends SwellCommand {
242
229
  await execAsync('npm install', { cwd: configPath });
243
230
  spinner.succeed('Workspace initialized');
244
231
  }
245
- catch (error) {
232
+ catch {
246
233
  spinner.warn('Failed to initialize workspace');
247
234
  }
248
235
  spinner.succeed(`${projectType?.name} initialized app in ${configPath}/frontend/`);
@@ -364,6 +351,23 @@ export class CreateAppCommand extends SwellCommand {
364
351
  spinner.stop();
365
352
  return storefrontInstalledApps;
366
353
  }
354
+ getProjectType(frameworkType) {
355
+ const projectType = FrontendProjectTypes.find((type) => type.slug === frameworkType);
356
+ if (!projectType) {
357
+ this.error(`Could not find project type: ${frameworkType}\n\nValid values: ${getFrontendProjectValidValues(true, false)}\n\nExample: swell create app reviews --type admin --frontend astro -y`, {
358
+ exit: 1,
359
+ });
360
+ }
361
+ if (!projectType.installCommand) {
362
+ this.error(`Project type ${projectType.name} cannot be installed (legacy type)\n\nValid values: ${getFrontendProjectValidValues(true, false)}\n\nExample: swell create app reviews --type admin --frontend astro -y`, {
363
+ exit: 1,
364
+ });
365
+ }
366
+ return {
367
+ ...projectType,
368
+ installCommand: projectType.installCommand || '',
369
+ };
370
+ }
367
371
  async setupPackage(name, config, pkg) {
368
372
  const execAsync = promisify(exec);
369
373
  const configPath = path.dirname(config.path);
@@ -410,4 +414,23 @@ export class CreateAppCommand extends SwellCommand {
410
414
  this.log(`There was an error setting up the package manager: ${error.message}`);
411
415
  }
412
416
  }
417
+ async addFrontendAllowedHosts(projectType, configPath) {
418
+ // Configure allowedHosts for frameworks using Vite dev server
419
+ // This allows tunneling services (ngrok, localtunnel) to proxy requests to the local dev environment
420
+ switch (projectType.slug) {
421
+ case 'astro': {
422
+ await this.addAllowedHostsToAstro(configPath);
423
+ break;
424
+ }
425
+ case 'angular': {
426
+ await this.addAllowedHostsToAngular(configPath);
427
+ break;
428
+ }
429
+ case 'nuxt': {
430
+ await this.addAllowedHostsToNuxt(configPath);
431
+ break;
432
+ }
433
+ // Other frameworks don't require allowedHosts configuration
434
+ }
435
+ }
413
436
  }
@@ -41,7 +41,10 @@ export class CreateCollectionCommand extends CreateConfigCommand {
41
41
  }),
42
42
  };
43
43
  async promptCollection() {
44
- const { args, flags } = await this.parse(CreateCollectionCommand);
44
+ // Parse using the concrete command class so child-command flags
45
+ // (e.g., --events, --fields) are recognized during interactive flows.
46
+ const klass = super.ctor;
47
+ const { args, flags } = await this.parse(klass);
45
48
  let { description, label } = flags;
46
49
  let { collection } = args;
47
50
  let isStandardCollection = false;
@@ -35,6 +35,10 @@ export class CreateConfigCommand extends AppCommand {
35
35
  }
36
36
  }
37
37
  }
38
+ else if (filePathExists(filePath) && !overwrite) {
39
+ // Non-interactive mode: never prompt; fail fast on potential overwrite
40
+ this.error(`A ${this.createType} file already exists at ${filePath}.\n\nUse --overwrite to replace the existing file in non-interactive mode.`, { exit: 1 });
41
+ }
38
42
  typeof fileBody === 'string'
39
43
  ? await writeFile(filePath, fileBody)
40
44
  : await writeJsonFile(filePath, fileBody);
@@ -5,5 +5,6 @@ declare namespace _default {
5
5
  let APP_PAGES_HOST: string;
6
6
  let STOREFRONT_FRONTEND_HOST: string;
7
7
  let LOCAL_PROXY_PROVIDER: string;
8
+ let SCHEMAS_BASE_URL: string;
8
9
  }
9
10
  export default _default;
package/dist/env/local.js CHANGED
@@ -5,4 +5,5 @@ export default {
5
5
  APP_PAGES_HOST: 'http://${STORE_ID}--${APP_ID}--${ENV}.swell.test:4001',
6
6
  STOREFRONT_FRONTEND_HOST: 'http://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.test:4001',
7
7
  LOCAL_PROXY_PROVIDER: '',
8
+ SCHEMAS_BASE_URL: 'https://pub-3672563ccb924eeba4d45be1296a0aee.r2.dev',
8
9
  };
@@ -5,5 +5,6 @@ declare namespace _default {
5
5
  let APP_PAGES_HOST: string;
6
6
  let STOREFRONT_FRONTEND_HOST: string;
7
7
  let LOCAL_PROXY_PROVIDER: string;
8
+ let SCHEMAS_BASE_URL: string;
8
9
  }
9
10
  export default _default;
@@ -5,4 +5,5 @@ export default {
5
5
  APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.swell.store',
6
6
  STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.store',
7
7
  LOCAL_PROXY_PROVIDER: '',
8
+ SCHEMAS_BASE_URL: 'https://json.swell.store',
8
9
  };
@@ -5,5 +5,6 @@ declare namespace _default {
5
5
  let APP_PAGES_HOST: string;
6
6
  let STOREFRONT_FRONTEND_HOST: string;
7
7
  let LOCAL_PROXY_PROVIDER: string;
8
+ let SCHEMAS_BASE_URL: string;
8
9
  }
9
10
  export default _default;
@@ -5,4 +5,5 @@ export default {
5
5
  APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.${REVIEW_DOMAIN}.rav2.swell.store',
6
6
  STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.${REVIEW_DOMAIN}.rav2.swell.store',
7
7
  LOCAL_PROXY_PROVIDER: '',
8
+ SCHEMAS_BASE_URL: 'https://pub-3672563ccb924eeba4d45be1296a0aee.r2.dev',
8
9
  };
@@ -5,5 +5,6 @@ declare namespace _default {
5
5
  let APP_PAGES_HOST: string;
6
6
  let STOREFRONT_FRONTEND_HOST: string;
7
7
  let LOCAL_PROXY_PROVIDER: string;
8
+ let SCHEMAS_BASE_URL: string;
8
9
  }
9
10
  export default _default;
@@ -5,4 +5,5 @@ export default {
5
5
  APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.staging.swell.store',
6
6
  STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.staging.swell.store',
7
7
  LOCAL_PROXY_PROVIDER: '',
8
+ SCHEMAS_BASE_URL: 'https://pub-3672563ccb924eeba4d45be1296a0aee.r2.dev',
8
9
  };
package/dist/lib/api.d.ts CHANGED
@@ -1,16 +1,27 @@
1
+ export declare enum HttpMethod {
2
+ GET = "get",
3
+ POST = "post",
4
+ PUT = "put",
5
+ DELETE = "delete"
6
+ }
1
7
  export default class Api {
2
8
  envId: string | undefined;
3
9
  secretKey: string | undefined;
4
10
  storeId: string | undefined;
5
11
  constructor(storeId?: string, envId?: string, secretKey?: string);
6
12
  api(pathOpts: Api.Paths, options: Api.RequestOptions): Promise<any>;
7
- delete(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
8
13
  get(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
9
- isAdmin(): boolean;
14
+ getAll(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<{
15
+ count: number;
16
+ results: any[];
17
+ }>;
10
18
  post(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
11
19
  put(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
12
- setStoreEnv(storeId: string, envId?: string): Promise<void>;
20
+ delete(pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
13
21
  isTestEnvEnabled(): Promise<boolean>;
14
- truncatePath(path: string | undefined): string | undefined;
15
- waitForAsyncResponse(response: any, pathOpts: Api.Paths, options?: Api.RequestOptions): Promise<any>;
22
+ setEnv(envId: string): Promise<void>;
23
+ setStoreEnv(storeId: string, envId?: string): Promise<void>;
24
+ private isAdmin;
25
+ private truncatePath;
26
+ private waitForAsyncResponse;
16
27
  }