@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
@@ -1,16 +1,34 @@
1
+ import { hash as blake3hash } from 'blake3-wasm';
1
2
  import { pluralize, titleize } from 'inflection';
2
3
  import * as fs from 'node:fs';
3
4
  import * as path from 'node:path';
4
- import { appConfigFileExists } from '../app-config.js';
5
+ import { toAppId } from '../create/index.js';
5
6
  import { AppConfig } from './app-config.js';
7
+ const PUSH_CONCURRENCY = 3;
8
+ var SwellJsonFields;
9
+ (function (SwellJsonFields) {
10
+ SwellJsonFields["DESCRIPTION"] = "description";
11
+ SwellJsonFields["EXTENSIONS"] = "extensions";
12
+ SwellJsonFields["ID"] = "id";
13
+ SwellJsonFields["KIND"] = "kind";
14
+ SwellJsonFields["NAME"] = "name";
15
+ SwellJsonFields["PERMISSIONS"] = "permissions";
16
+ SwellJsonFields["STOREFRONT"] = "storefront";
17
+ SwellJsonFields["THEME"] = "theme";
18
+ SwellJsonFields["TYPE"] = "type";
19
+ SwellJsonFields["VERSION"] = "version";
20
+ })(SwellJsonFields || (SwellJsonFields = {}));
6
21
  var ConfigType;
7
22
  (function (ConfigType) {
8
23
  ConfigType["ASSET"] = "asset";
9
24
  ConfigType["CONTENT"] = "content";
25
+ ConfigType["FILE"] = "file";
26
+ ConfigType["FRONTEND"] = "frontend";
10
27
  ConfigType["FUNCTION"] = "function";
11
28
  ConfigType["MODEL"] = "model";
12
29
  ConfigType["NOTIFICATION"] = "notification";
13
30
  ConfigType["SETTING"] = "setting";
31
+ ConfigType["THEME"] = "theme";
14
32
  ConfigType["WEBHOOK"] = "webhook";
15
33
  })(ConfigType || (ConfigType = {}));
16
34
  // Models and content should come first in order
@@ -22,14 +40,44 @@ const ConfigTypeBatchOrder = [
22
40
  ConfigType.NOTIFICATION,
23
41
  ConfigType.SETTING,
24
42
  ConfigType.WEBHOOK,
43
+ ConfigType.THEME,
44
+ ConfigType.FRONTEND,
45
+ ConfigType.FILE,
25
46
  ];
26
- const ConfigPaths = {
27
- ...Object.fromEntries(Object.keys(ConfigType).map((type) => [
47
+ // Configs available for different app types
48
+ const StandardConfigTypes = [
49
+ 'MODEL',
50
+ 'CONTENT',
51
+ 'ASSET',
52
+ 'FUNCTION',
53
+ 'FRONTEND',
54
+ 'NOTIFICATION',
55
+ 'SETTING',
56
+ 'WEBHOOK',
57
+ 'THEME',
58
+ ];
59
+ const StorefrontConfigTypes = ['FRONTEND', 'ASSET', 'SETTING'];
60
+ const ThemeConfigTypes = ['THEME', 'ASSET'];
61
+ const getConfigPaths = (types) => ({
62
+ ...Object.fromEntries(Object.keys(ConfigType)
63
+ .filter((type) => !types || types.find((inType) => inType === type))
64
+ .map((type) => [
28
65
  type,
29
66
  // a bit of a hack to make the path singular for content configs
30
67
  // need to refactor this to a function if we add more exceptions
31
- type === 'CONTENT' ? 'content' : pluralize(type.toLowerCase()),
68
+ type === 'CONTENT' || type === 'FRONTEND' || type === 'THEME'
69
+ ? type.toLowerCase()
70
+ : pluralize(type.toLowerCase()),
32
71
  ])),
72
+ });
73
+ const ConfigPaths = {
74
+ ...getConfigPaths(StandardConfigTypes),
75
+ };
76
+ const StorefrontConfigPaths = {
77
+ ...getConfigPaths(StorefrontConfigTypes),
78
+ };
79
+ const ThemeConfigPaths = {
80
+ ...getConfigPaths(ThemeConfigTypes),
33
81
  };
34
82
  var ConfigInputFields;
35
83
  (function (ConfigInputFields) {
@@ -39,17 +87,39 @@ var ConfigInputFields;
39
87
  ConfigInputFields["VALUES"] = "values";
40
88
  ConfigInputFields["VERSION"] = "version";
41
89
  })(ConfigInputFields || (ConfigInputFields = {}));
42
- function hasAllowedFileExtension(file, configType) {
43
- let allowedFileExtensions = new Set([
44
- '.js',
45
- '.json',
46
- '.tpl',
47
- '.ts',
48
- ]);
49
- if (configType === ConfigType.NOTIFICATION) {
50
- allowedFileExtensions = new Set(['.json']);
90
+ const FrontendProjectTypes = [
91
+ {
92
+ buildCommand: 'npx @cloudflare/next-on-pages',
93
+ configFileName: 'next.config',
94
+ deployPath: '.vercel/output/static',
95
+ devCommand: 'npx next dev --port ${PORT}',
96
+ installCommand: 'npx create-next-app@latest frontend --typescript --use-npm --src-dir --app --eslint --import-alias "@/*" --tailwind',
97
+ name: 'Next.js',
98
+ slug: 'nextjs',
99
+ },
100
+ {
101
+ buildCommand: 'npx astro build',
102
+ configFileName: 'astro.config',
103
+ deployPath: 'dist',
104
+ devCommand: 'npx astro dev --port ${PORT}',
105
+ installCommand: 'npm create astro@latest frontend -- --install --no-git --yes --skip-houston --typescript strict',
106
+ name: 'Astro',
107
+ slug: 'astro',
108
+ },
109
+ ];
110
+ const frontendProjectConfigExtensions = ['.mjs', '.js'];
111
+ function getAppSlugId(app) {
112
+ return toAppId(app.private_id) || app.public_id || app.id;
113
+ }
114
+ function getFrontendProjectType(appPath) {
115
+ for (const projectType of FrontendProjectTypes) {
116
+ for (const extension of frontendProjectConfigExtensions) {
117
+ const configFile = path.join(appPath, 'frontend', `${projectType.configFileName}${extension}`);
118
+ if (filePathExists(configFile)) {
119
+ return projectType;
120
+ }
121
+ }
51
122
  }
52
- return allowedFileExtensions.has(path.parse(file).ext);
53
123
  }
54
124
  function getConfigTypeFromPath(path) {
55
125
  for (const type in ConfigPaths) {
@@ -63,7 +133,7 @@ function getConfigTypeFromPath(path) {
63
133
  function getConfigTypeKeyFromValue(value) {
64
134
  return Object.keys(ConfigType).find((key) => ConfigType[key] === value);
65
135
  }
66
- function fileExists(filePath) {
136
+ function filePathExists(filePath) {
67
137
  try {
68
138
  // eslint-disable-next-line no-bitwise
69
139
  fs.accessSync(filePath, fs.constants.R_OK | fs.constants.W_OK);
@@ -76,13 +146,22 @@ function fileExists(filePath) {
76
146
  return false;
77
147
  }
78
148
  }
79
- function writeFile(file, data) {
149
+ async function writeFile(file, data) {
150
+ // Convert from base64 if needed
151
+ let contents;
152
+ try {
153
+ const isBase64 = Buffer.from(data, 'base64').toString('base64') === data;
154
+ contents = isBase64 ? Buffer.from(data, 'base64') : data;
155
+ }
156
+ catch {
157
+ contents = data;
158
+ }
80
159
  return new Promise((resolve, _reject) => {
81
160
  fs.mkdir(path.dirname(file), { recursive: true }, (error) => {
82
161
  if (error) {
83
162
  throw error;
84
163
  }
85
- fs.writeFile(file, data, (error) => {
164
+ fs.writeFile(file, contents || '', (error) => {
86
165
  if (error) {
87
166
  throw error;
88
167
  }
@@ -91,49 +170,73 @@ function writeFile(file, data) {
91
170
  });
92
171
  });
93
172
  }
173
+ function deleteFile(file) {
174
+ return new Promise((resolve, _reject) => {
175
+ fs.unlink(file, (error) => {
176
+ if (error) {
177
+ throw error;
178
+ }
179
+ resolve();
180
+ });
181
+ });
182
+ }
94
183
  async function writeJsonFile(file, data = {}) {
95
184
  return writeFile(file, `${JSON.stringify(data, null, 2)}\n`);
96
185
  }
97
- function appLogoIcon(appPath) {
186
+ // Hash method adapted from wrangler-sdk
187
+ function hashFile(filePath, fileContents) {
188
+ const contents = fileContents ?? fs.readFileSync(filePath);
189
+ const base64Contents = contents.toString('base64');
190
+ const extension = path.extname(filePath).slice(1);
191
+ return hashString(base64Contents + extension);
192
+ }
193
+ function hashString(contents) {
194
+ return blake3hash(contents).toString('hex').slice(0, 32);
195
+ }
196
+ function appAssetImage(appPath, fileName) {
98
197
  const assetsDirPath = path.join(appPath, ConfigPaths['ASSET']);
99
- if (!appConfigFileExists(assetsDirPath)) {
198
+ if (!filePathExists(assetsDirPath)) {
100
199
  return;
101
200
  }
102
201
  for (const configFile of fs.readdirSync(assetsDirPath)) {
103
- if (isAppLogoIcon(configFile)) {
202
+ if (path.parse(configFile).name === fileName) {
104
203
  return path.join(assetsDirPath, configFile);
105
204
  }
106
205
  }
107
206
  }
108
- function isAppLogoIcon(file) {
109
- return path.parse(file).name === 'icon';
110
- }
111
- function appConfigFromFile(configFile, configType) {
112
- const { name } = path.parse(configFile);
113
- const appConfig = AppConfig.create({
114
- localPath: configFile,
207
+ function appConfigFromFile(filePath, configType, appPath) {
208
+ const { name } = path.parse(filePath);
209
+ // Nested files are considered 'file' type
210
+ const type = (filePath.split(path.sep).length > 2 ? 'file' : configType || 'file');
211
+ const config = AppConfig.create({
115
212
  name,
116
- type: configType,
117
- values: {},
213
+ filePath,
214
+ appPath,
215
+ type,
216
+ file_path: filePath,
118
217
  });
119
- return appConfig;
218
+ // Do not allow files larger than 10MB
219
+ if (config.mbSize && config.mbSize > 10) {
220
+ throw new Error(`App file size too large: ${config.filePath} (${config.mbSize} MB)`);
221
+ }
222
+ return config;
120
223
  }
121
- function findAppConfig(app, file, configType) {
122
- const { name } = path.parse(file);
224
+ function findAppConfig(app, filePath, configType) {
225
+ const { name } = path.parse(filePath);
123
226
  const config = app?.configs?.find((c) => c && c.type === configType && c.name === name);
124
227
  return config;
125
228
  }
126
229
  /**
127
230
  * Get files to push in batches prioritized by dependencies
128
231
  *
129
- * @param appConfigs - List of app configs
232
+ * @param configs - List of app configs
130
233
  * @returns {Array} Batches of files to push
131
234
  */
132
- function batchAppConfigsByType(appConfigs) {
133
- const modelsWithExtends = appConfigs.filter((item) => item.type === 'model' && item.values.extends);
134
- const modelsWithoutExtends = appConfigs.filter((item) => item.type === 'model' && !item.values.extends);
135
- const contentTypesWithCollection = appConfigs.filter((item) => item.type === 'content' && item.values.collection);
136
- const contentTypesWithoutCollection = appConfigs.filter((item) => item.type === 'content' && !item.values.collection);
235
+ function batchAppFilesByType(configs) {
236
+ const modelsWithExtends = configs.filter((config) => config.type === 'model' && config.values.extends);
237
+ const modelsWithoutExtends = configs.filter((config) => config.type === 'model' && !config.values.extends);
238
+ const contentTypesWithCollection = configs.filter((config) => config.type === 'content' && config.values.collection);
239
+ const contentTypesWithoutCollection = configs.filter((config) => config.type === 'content' && !config.values.collection);
137
240
  const prioritized = [
138
241
  ...modelsWithExtends,
139
242
  ...modelsWithoutExtends,
@@ -142,26 +245,28 @@ function batchAppConfigsByType(appConfigs) {
142
245
  ];
143
246
  const sorted = [
144
247
  ...prioritized,
145
- ...appConfigs.filter((item) => !prioritized.includes(item)),
248
+ ...configs.filter((config) => !prioritized.includes(config)),
146
249
  ];
147
250
  const batches = [];
148
251
  // eslint-disable-next-line guard-for-in
149
- for (const key in ConfigTypeBatchOrder) {
150
- const configType = ConfigTypeBatchOrder[key];
151
- const configsByType = sorted.filter((item) => item.type === configType);
152
- if (configsByType.length === 0) {
153
- continue;
154
- }
252
+ for (const type of Object.keys(ConfigTypeBatchOrder)) {
253
+ const configsByType = sorted.filter((config) => config.type === type);
254
+ const label = type === ConfigType.FILE ? 'App files' : titleize(type);
255
+ const concurrency = type === ConfigType.NOTIFICATION || type === ConfigType.ASSET
256
+ ? 1
257
+ : PUSH_CONCURRENCY;
155
258
  batches.push({
156
259
  configs: configsByType,
157
- label: titleize(configType),
158
- type: configType,
260
+ type,
261
+ label,
262
+ concurrency,
159
263
  });
160
264
  }
265
+ // Add base files if config types are not specified
161
266
  return batches;
162
267
  }
163
- export { ConfigInputFields, ConfigPaths, ConfigType,
268
+ export { PUSH_CONCURRENCY, ConfigInputFields, ConfigPaths, ConfigType, FrontendProjectTypes, StorefrontConfigPaths, StorefrontConfigTypes, SwellJsonFields, ThemeConfigPaths, ThemeConfigTypes, appAssetImage, appConfigFromFile, batchAppFilesByType, deleteFile, filePathExists, findAppConfig,
164
269
  // functions
165
- appConfigFromFile, appLogoIcon, batchAppConfigsByType, fileExists, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, hasAllowedFileExtension, isAppLogoIcon, writeFile, writeJsonFile, };
270
+ getAppSlugId, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, hashFile, hashString, writeFile, writeJsonFile, };
166
271
  export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
167
- export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, configFilePath, } from './paths.js';
272
+ export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, } from './paths.js';
@@ -1,18 +1,25 @@
1
- import { AppConfig, ConfigType } from './index.js';
1
+ import { ConfigType } from './index.js';
2
+ declare function globAllFilesByPath(appPath: string, dirPath?: string): string[];
2
3
  declare function allConfigDirsPaths(appPath: string): Generator<{
3
- configDirPath: string;
4
+ configDir: string;
4
5
  configType: ConfigType;
5
6
  }>;
6
7
  declare function allConfigFilesPaths(appPath: string): Generator<{
7
- configDirPath: string;
8
+ configDir: string;
8
9
  configFile: string;
9
10
  configType: ConfigType;
10
11
  }, void, unknown>;
11
12
  declare function allConfigFilesPathsByType(appPath: string, configTypeKey: string): Generator<{
12
- configDirPath: string;
13
+ configDir: string;
13
14
  configFile: string;
14
15
  configType: ConfigType;
15
16
  }, void, unknown>;
16
- declare function configFilePath(appPath: string, config: AppConfig): string;
17
- declare function getAllConfigPaths(): Array<string>;
18
- export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, configFilePath, };
17
+ declare function allConfigFilesInDir(appPath: string, dirPath: string, configType: ConfigType): Generator<{
18
+ configDir: string;
19
+ configFile: string;
20
+ configType: ConfigType;
21
+ }, void, unknown>;
22
+ declare function allBaseFilesInDir(appPath: string): Generator<string, void, unknown>;
23
+ declare function getAllConfigPaths(appType: string): Array<string>;
24
+ declare function isPathDirectory(path: string): boolean;
25
+ export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, };
@@ -1,49 +1,84 @@
1
+ import { globbySync } from 'globby';
1
2
  import * as fs from 'node:fs';
2
3
  import * as path from 'node:path';
3
- import { appConfigFileExists } from '../app-config.js';
4
- import { ConfigPaths, ConfigType, hasAllowedFileExtension, } from './index.js';
5
- function* allConfigDirsPaths(appPath) {
4
+ import { ConfigPaths, ConfigType, StorefrontConfigPaths, ThemeConfigPaths, filePathExists, } from './index.js';
5
+ function globFiles(pattern, appPath, dirPath = '.', options = {}) {
6
+ const filePaths = globbySync(`${dirPath}${path.sep}${pattern}`, {
7
+ cwd: appPath,
8
+ dot: true,
9
+ gitignore: true,
10
+ ...options,
11
+ expandDirectories: true,
12
+ // Explicitly ignore node_modules and lock files just in case
13
+ ignore: [
14
+ '**/.swellrc',
15
+ '**/.git/**',
16
+ '**/node_modules/**',
17
+ '**/package-lock.json',
18
+ '**/yarn.lock',
19
+ ...(options?.ignore || []),
20
+ ],
21
+ });
22
+ return filePaths;
23
+ }
24
+ function globAllFilesByPath(appPath, dirPath) {
25
+ return globFiles('**', appPath, dirPath);
26
+ }
27
+ function getConfirDirsPaths(appPath) {
28
+ const configDirsPaths = [];
6
29
  for (const type in ConfigType) {
7
30
  if (Object.prototype.hasOwnProperty.call(ConfigType, type)) {
8
31
  const configType = ConfigType[type];
9
32
  const configPath = ConfigPaths[type];
10
- const configDirPath = path.join(appPath, configPath);
11
- if (appConfigFileExists(configDirPath)) {
12
- yield { configDirPath, configType };
33
+ if (!configPath) {
34
+ continue;
35
+ }
36
+ const configDir = path.join(appPath, configPath);
37
+ if (filePathExists(configDir)) {
38
+ configDirsPaths.push({ configDir: configPath, configType });
13
39
  }
14
40
  }
15
41
  }
42
+ return configDirsPaths;
43
+ }
44
+ function* allConfigDirsPaths(appPath) {
45
+ for (const { configDir, configType } of getConfirDirsPaths(appPath)) {
46
+ yield { configDir, configType };
47
+ }
16
48
  }
17
49
  function* allConfigFilesPaths(appPath) {
18
- for (const { configDirPath, configType } of allConfigDirsPaths(appPath)) {
19
- yield* allConfigFilesInDir(configDirPath, configType);
50
+ for (const { configDir, configType } of allConfigDirsPaths(appPath)) {
51
+ yield* allConfigFilesInDir(appPath, configDir, configType);
20
52
  }
21
53
  }
22
54
  function* allConfigFilesPathsByType(appPath, configTypeKey) {
23
55
  const configPath = ConfigPaths[configTypeKey];
24
- const configDirPath = path.join(appPath, configPath);
25
- yield* allConfigFilesInDir(configDirPath, ConfigType[configTypeKey]);
26
- }
27
- function* allConfigFilesInDir(configDirPath, configType) {
28
- for (const file of fs.readdirSync(configDirPath)) {
29
- const configFile = path.join(configDirPath, file);
30
- // ignore sub-directories, e.g. functions/lib
31
- // ignore files that don't have the allowed extensions
32
- if (fs.lstatSync(configFile).isDirectory() ||
33
- !hasAllowedFileExtension(configFile, configType)) {
34
- continue;
35
- }
36
- yield { configDirPath, configFile, configType };
56
+ yield* allConfigFilesInDir(appPath, configPath, ConfigType[configTypeKey]);
57
+ }
58
+ function* allConfigFilesInDir(appPath, dirPath, configType) {
59
+ const filePaths = globFiles('**', appPath, dirPath);
60
+ for (const configFile of filePaths) {
61
+ yield { configDir: dirPath, configFile, configType };
37
62
  }
38
63
  }
39
- function configFilePath(appPath, config) {
40
- const configPath = ConfigPaths[config.type.toLocaleUpperCase()];
41
- const fileExt = config.file?.ext || '.json';
42
- return path.join(appPath, configPath, [config.name, fileExt].join(''));
64
+ function* allBaseFilesInDir(appPath) {
65
+ // Only get base files excluding config directories
66
+ const configPaths = getConfirDirsPaths(appPath);
67
+ const baseFiles = globFiles('**', appPath, '.', {
68
+ ignore: configPaths.map((config) => `${config.configDir}/**`),
69
+ });
70
+ for (const path of baseFiles) {
71
+ yield path;
72
+ }
43
73
  }
44
- function getAllConfigPaths() {
74
+ function getAllConfigPaths(appType) {
45
75
  const configPaths = [];
46
- for (const type in ConfigPaths) {
76
+ const ConfigPathsByType = appType === 'storefront'
77
+ ? StorefrontConfigPaths
78
+ : appType === 'theme'
79
+ ? ThemeConfigPaths
80
+ : ConfigPaths;
81
+ for (const type in ConfigPathsByType) {
47
82
  if (Object.prototype.hasOwnProperty.call(ConfigPaths, type)) {
48
83
  const configPath = ConfigPaths[type];
49
84
  configPaths.push(configPath);
@@ -51,4 +86,14 @@ function getAllConfigPaths() {
51
86
  }
52
87
  return configPaths;
53
88
  }
54
- export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, configFilePath, };
89
+ function isPathDirectory(path) {
90
+ try {
91
+ const stat = fs.statSync(path);
92
+ return stat.isDirectory();
93
+ }
94
+ catch {
95
+ // Handle error (e.g., file not found)
96
+ return false;
97
+ }
98
+ }
99
+ export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, };
@@ -24,15 +24,14 @@ export async function bundleFunction(filePath) {
24
24
  .join('\n');
25
25
  // Remove comments with to get a unique hash in the API
26
26
  // Because esbuild puts the file name as a comment
27
- code = code.replace(/^[\n]?\/\/.*/gm, '');
27
+ code = code.replaceAll(/^\n?\/\/.*/gm, '');
28
28
  // eslint-disable-next-line no-new-func
29
29
  const evalFn = new Function(`${code}\nreturn { ...moduleExports }`);
30
30
  const { config, ...moduleExports } = evalFn();
31
- code += getSwellFunctionWrapper();
31
+ code = await minifyCode(code + getSwellFunctionWrapper());
32
32
  return { code, config, dependencies, moduleExports };
33
33
  }
34
34
  catch (error) {
35
- console.log(error);
36
35
  throw new Error(`Unable to compile function ${filePath}: ${error.message}`);
37
36
  }
38
37
  }
@@ -41,3 +40,16 @@ function getSwellFunctionWrapper() {
41
40
  const __dirname = path.dirname(__filename);
42
41
  return fs.readFileSync(path.join(__dirname, './swell-function-wrapper.js'), 'utf8');
43
42
  }
43
+ async function minifyCode(code) {
44
+ try {
45
+ const result = await esbuild.transform(code, {
46
+ minifyIdentifiers: false,
47
+ minifySyntax: false,
48
+ minifyWhitespace: true,
49
+ });
50
+ return result.code;
51
+ }
52
+ catch (error) {
53
+ throw new Error('Compilation error:', error.message);
54
+ }
55
+ }
@@ -1,9 +1,14 @@
1
1
  declare const env: any;
2
+ declare function setEnv(name: string, reviewBranch?: string): {
3
+ [key: string]: string;
4
+ };
2
5
  declare function ensureLoggedIn(storeId: string): Promise<any>;
3
6
  declare function get(key: string): any;
4
7
  declare function getAll(): any;
5
- declare function getDefaultStore(): any;
8
+ declare function getDefaultStore(): string;
6
9
  declare function setDefaultStore(storeId: string): void;
10
+ declare function getDefaultStorefront(appPath: string): string;
11
+ declare function setDefaultStorefront(appPath: string, storefrontId: string): void;
7
12
  declare function getSessionId(storeId: string): any;
8
13
  /**
9
14
  * Set store data for later access.
@@ -28,10 +33,12 @@ declare const config: {
28
33
  get: typeof get;
29
34
  getAll: typeof getAll;
30
35
  getDefaultStore: typeof getDefaultStore;
36
+ getDefaultStorefront: typeof getDefaultStorefront;
31
37
  getSessionId: typeof getSessionId;
32
38
  getUser: typeof getUser;
33
39
  setDefaultStore: typeof setDefaultStore;
40
+ setDefaultStorefront: typeof setDefaultStorefront;
34
41
  setStore: typeof setStore;
35
42
  setUser: typeof setUser;
36
43
  };
37
- export { config as default, env };
44
+ export { config as default, env, setEnv };
@@ -2,6 +2,10 @@ import Conf from 'conf';
2
2
  import Configstore from 'configstore';
3
3
  import { homedir } from 'node:os';
4
4
  import path from 'node:path';
5
+ import localEnv from '../env/local.js';
6
+ import prodEnv from '../env/production.js';
7
+ import reviewEnv from '../env/review.js';
8
+ import stagingEnv from '../env/staging.js';
5
9
  import style from './style.js';
6
10
  const configPath = process.env.NODE_ENV === 'test'
7
11
  ? path.resolve(process.cwd(), 'test', '.swell', 'config.json')
@@ -28,6 +32,36 @@ const env = new Conf({
28
32
  projectName: 'swell',
29
33
  projectSuffix: '',
30
34
  });
35
+ function setEnv(name, reviewBranch) {
36
+ let newEnv = {};
37
+ if (name.startsWith('prod')) {
38
+ newEnv = { ...prodEnv };
39
+ }
40
+ else if (name.startsWith('stag')) {
41
+ newEnv = { ...stagingEnv };
42
+ }
43
+ else if (name.startsWith('local')) {
44
+ newEnv = { ...localEnv };
45
+ }
46
+ else if (name.startsWith('rev') || name.startsWith('ra')) {
47
+ if (reviewBranch) {
48
+ const reviewAppEnv = { ...reviewEnv };
49
+ const reviewDomain = reviewBranch.replaceAll('/', '-');
50
+ for (const key of Object.keys(reviewAppEnv)) {
51
+ reviewAppEnv[key] = reviewAppEnv[key].replace('${REVIEW_DOMAIN}', reviewDomain);
52
+ }
53
+ newEnv = reviewAppEnv;
54
+ }
55
+ else {
56
+ throw new Error('Review environment requires a branch name.');
57
+ }
58
+ }
59
+ else {
60
+ throw new Error(`Invalid environment '${name}'. Should be one of 'prod', 'staging', 'review', or 'local'.`);
61
+ }
62
+ env.set(newEnv);
63
+ return newEnv;
64
+ }
31
65
  /**
32
66
  * Get specified attributes for given object.
33
67
  *
@@ -68,6 +102,18 @@ function getDefaultStore() {
68
102
  function setDefaultStore(storeId) {
69
103
  return configStore.set('defaultStore', storeId);
70
104
  }
105
+ function getDefaultStorefront(appPath) {
106
+ const storeId = getDefaultStore();
107
+ const defaultStorefronts = configStore.get('defaultStorefronts') || {};
108
+ return defaultStorefronts[storeId]?.[appPath];
109
+ }
110
+ function setDefaultStorefront(appPath, storefrontId) {
111
+ const storeId = getDefaultStore();
112
+ const defaultStorefronts = configStore.get('defaultStorefronts') || {};
113
+ defaultStorefronts[storeId] = defaultStorefronts[storeId] || {};
114
+ defaultStorefronts[storeId][appPath] = storefrontId;
115
+ return configStore.set('defaultStorefronts', defaultStorefronts);
116
+ }
71
117
  function getSessionId(storeId) {
72
118
  if (!configStore.has('stores'))
73
119
  return;
@@ -122,10 +168,12 @@ const config = {
122
168
  get,
123
169
  getAll,
124
170
  getDefaultStore,
171
+ getDefaultStorefront,
125
172
  getSessionId,
126
173
  getUser,
127
174
  setDefaultStore,
175
+ setDefaultStorefront,
128
176
  setStore,
129
177
  setUser,
130
178
  };
131
- export { config as default, env };
179
+ export { config as default, env, setEnv };
@@ -1,5 +1,10 @@
1
1
  export declare const API_BASE_URL: any;
2
2
  export declare const ADMIN_API_BASE_URL: any;
3
3
  export declare const LOGIN_HOST: any;
4
+ export declare const APP_PAGES_HOST: any;
5
+ export declare const STOREFRONT_FRONTEND_HOST: any;
4
6
  export declare function getAdminApiBaseUrl(storeId: string): any;
5
7
  export declare function getLoginHost(storeId?: string): any;
8
+ export declare function getAppFrontendHost(storeId: string, installedAppId: string): any;
9
+ export declare function getStorefrontFrontendHost(storefrontSlug: string, branchId?: string): any;
10
+ export declare function getLocalFrontendHost(storeId: string, sessionId: string): any;
@@ -1,17 +1,35 @@
1
+ import defaultEnv from '../env/production.js';
1
2
  import { env } from './config.js';
2
3
  // note: env values stored in ~/.swell/env.json
3
4
  // url for backend api calls
4
- export const API_BASE_URL = env.get('API_BASE_URL') || 'https://api.swell.store';
5
+ export const API_BASE_URL = env.get('API_BASE_URL') || defaultEnv.API_BASE_URL;
5
6
  // url for admin api calls
6
- export const ADMIN_API_BASE_URL =
7
- // eslint-disable-next-line no-template-curly-in-string
8
- env.get('ADMIN_API_BASE_URL') || 'https://${STORE_ID}.swell.store/admin/api';
7
+ export const ADMIN_API_BASE_URL = env.get('ADMIN_API_BASE_URL') || defaultEnv.ADMIN_API_BASE_URL;
9
8
  // url host for admin login
10
- export const LOGIN_HOST = env.get('LOGIN_HOST') || 'https://${STORE_ID}.swell.store';
9
+ export const LOGIN_HOST = env.get('LOGIN_HOST') || defaultEnv.LOGIN_HOST;
10
+ // url host for app frontend
11
+ export const APP_PAGES_HOST = env.get('APP_PAGES_HOST') || defaultEnv.APP_PAGES_HOST;
12
+ // url host for storefront app frontend
13
+ export const STOREFRONT_FRONTEND_HOST = env.get('STOREFRONT_FRONTEND_HOST') ||
14
+ env.get('STOREFRONT_PAGES_HOST') || // Deprecated
15
+ defaultEnv.STOREFRONT_FRONTEND_HOST;
11
16
  // helpers to get constants with replacements
12
17
  export function getAdminApiBaseUrl(storeId) {
13
18
  return ADMIN_API_BASE_URL.replace('${STORE_ID}', storeId);
14
19
  }
15
20
  export function getLoginHost(storeId) {
16
- return LOGIN_HOST.replace('${STORE_ID}', storeId || 'app');
21
+ return LOGIN_HOST.replace('${STORE_ID}', storeId || 'login');
22
+ }
23
+ export function getAppFrontendHost(storeId, installedAppId) {
24
+ return APP_PAGES_HOST.replace('${STORE_ID}', storeId)
25
+ .replace('${APP_ID}', installedAppId)
26
+ .replace('${ENV}', 'app');
27
+ }
28
+ export function getStorefrontFrontendHost(storefrontSlug, branchId) {
29
+ return STOREFRONT_FRONTEND_HOST.replace('${STOREFRONT_SLUG}', storefrontSlug).replace('${BRANCH_PREFIX}', branchId ? `${branchId}.` : '');
30
+ }
31
+ export function getLocalFrontendHost(storeId, sessionId) {
32
+ return APP_PAGES_HOST.replace('${STORE_ID}', storeId)
33
+ .replace('${APP_ID}', sessionId)
34
+ .replace('${ENV}', 'local');
17
35
  }