@swell/cli 2.0.1-alpha.13 → 2.0.1-alpha.17

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 (91) hide show
  1. package/dist/app-command.d.ts +1 -1
  2. package/dist/app-command.js +8 -8
  3. package/dist/commands/app/dev.d.ts +18 -0
  4. package/dist/commands/app/dev.js +31 -0
  5. package/dist/commands/app/frontend/deploy.d.ts +17 -0
  6. package/dist/commands/app/frontend/deploy.js +44 -0
  7. package/dist/commands/app/frontend/dev.d.ts +21 -0
  8. package/dist/commands/app/frontend/dev.js +111 -0
  9. package/dist/commands/app/info.js +15 -8
  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 +37 -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 +71 -143
  18. package/dist/commands/app/release.d.ts +13 -0
  19. package/dist/commands/app/release.js +150 -0
  20. package/dist/commands/app/version.d.ts +7 -6
  21. package/dist/commands/app/version.js +64 -43
  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 +150 -144
  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 +41 -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/login.d.ts +1 -1
  37. package/dist/commands/login.js +12 -9
  38. package/dist/commands/logs.js +2 -1
  39. package/dist/commands/theme/dev.d.ts +23 -0
  40. package/dist/commands/theme/dev.js +167 -0
  41. package/dist/commands/theme/init.d.ts +9 -0
  42. package/dist/commands/theme/init.js +15 -0
  43. package/dist/commands/theme/pull.d.ts +20 -0
  44. package/dist/commands/theme/pull.js +65 -0
  45. package/dist/commands/theme/push.d.ts +19 -0
  46. package/dist/commands/theme/push.js +92 -0
  47. package/dist/create-app-command.d.ts +20 -0
  48. package/dist/create-app-command.js +254 -0
  49. package/dist/create-collection-command.d.ts +9 -9
  50. package/dist/create-collection-command.js +23 -23
  51. package/dist/{create-command.d.ts → create-config-command.d.ts} +1 -1
  52. package/dist/{create-command.js → create-config-command.js} +9 -7
  53. package/dist/lib/api.d.ts +6 -4
  54. package/dist/lib/api.js +58 -29
  55. package/dist/lib/app-config.d.ts +13 -5
  56. package/dist/lib/app-config.js +60 -23
  57. package/dist/lib/apps/app-config.d.ts +22 -8
  58. package/dist/lib/apps/app-config.js +202 -68
  59. package/dist/lib/apps/index.d.ts +93 -13
  60. package/dist/lib/apps/index.js +150 -50
  61. package/dist/lib/apps/paths.d.ts +14 -7
  62. package/dist/lib/apps/paths.js +73 -28
  63. package/dist/lib/bundle.js +4 -5
  64. package/dist/lib/config.d.ts +5 -1
  65. package/dist/lib/config.js +14 -0
  66. package/dist/lib/constants.d.ts +5 -0
  67. package/dist/lib/constants.js +22 -3
  68. package/dist/lib/create/content.d.ts +2 -2
  69. package/dist/lib/create/content.js +1 -1
  70. package/dist/lib/create/function.d.ts +2 -2
  71. package/dist/lib/create/function.js +2 -2
  72. package/dist/lib/create/index.d.ts +1 -1
  73. package/dist/lib/create/index.js +11 -7
  74. package/dist/lib/create/model.d.ts +2 -2
  75. package/dist/lib/create/notification.d.ts +2 -2
  76. package/dist/lib/info.js +2 -2
  77. package/dist/lib/sessions.js +2 -2
  78. package/dist/lib/stores.d.ts +5 -2
  79. package/dist/lib/stores.js +9 -6
  80. package/dist/lib/style.d.ts +2 -0
  81. package/dist/lib/style.js +2 -0
  82. package/dist/lib/swell-function-wrapper.d.ts +2 -2
  83. package/dist/lib/swell-function-wrapper.js +2 -2
  84. package/dist/push-app-command.d.ts +73 -0
  85. package/dist/push-app-command.js +765 -0
  86. package/dist/remote-app-command.d.ts +35 -9
  87. package/dist/remote-app-command.js +436 -164
  88. package/oclif.manifest.json +841 -125
  89. package/package.json +13 -1
  90. package/dist/commands/app/_pull.d.ts +0 -8
  91. package/dist/commands/app/_pull.js +0 -45
@@ -1,8 +1,22 @@
1
- import { pluralize, titleize } from 'inflection';
2
1
  import * as fs from 'node:fs';
3
2
  import * as path from 'node:path';
4
- import { appConfigFileExists } from '../app-config.js';
3
+ import { pluralize, titleize } from 'inflection';
4
+ import { hash as blake3hash } from 'blake3-wasm';
5
5
  import { AppConfig } from './app-config.js';
6
+ import { toAppId } from '../create/index.js';
7
+ var SwellJsonFields;
8
+ (function (SwellJsonFields) {
9
+ SwellJsonFields["ID"] = "id";
10
+ SwellJsonFields["NAME"] = "name";
11
+ SwellJsonFields["TYPE"] = "type";
12
+ SwellJsonFields["KIND"] = "kind";
13
+ SwellJsonFields["VERSION"] = "version";
14
+ SwellJsonFields["DESCRIPTION"] = "description";
15
+ SwellJsonFields["STOREFRONT"] = "storefront";
16
+ SwellJsonFields["THEME"] = "theme";
17
+ SwellJsonFields["PERMISSIONS"] = "permissions";
18
+ SwellJsonFields["EXTENSIONS"] = "extensions";
19
+ })(SwellJsonFields || (SwellJsonFields = {}));
6
20
  var ConfigType;
7
21
  (function (ConfigType) {
8
22
  ConfigType["ASSET"] = "asset";
@@ -12,6 +26,9 @@ var ConfigType;
12
26
  ConfigType["NOTIFICATION"] = "notification";
13
27
  ConfigType["SETTING"] = "setting";
14
28
  ConfigType["WEBHOOK"] = "webhook";
29
+ ConfigType["FRONTEND"] = "frontend";
30
+ ConfigType["THEME"] = "theme";
31
+ ConfigType["FILE"] = "file";
15
32
  })(ConfigType || (ConfigType = {}));
16
33
  // Models and content should come first in order
17
34
  const ConfigTypeBatchOrder = [
@@ -22,14 +39,46 @@ const ConfigTypeBatchOrder = [
22
39
  ConfigType.NOTIFICATION,
23
40
  ConfigType.SETTING,
24
41
  ConfigType.WEBHOOK,
42
+ ConfigType.THEME,
43
+ ConfigType.FRONTEND,
44
+ ConfigType.FILE,
25
45
  ];
46
+ // Configs available for different app types
47
+ const StandardConfigTypes = [
48
+ 'MODEL',
49
+ 'CONTENT',
50
+ 'ASSET',
51
+ 'FUNCTION',
52
+ 'FRONTEND',
53
+ 'NOTIFICATION',
54
+ 'SETTING',
55
+ 'WEBHOOK',
56
+ 'THEME',
57
+ ];
58
+ const StorefrontConfigTypes = ['FRONTEND', 'ASSET', 'SETTING'];
59
+ const ThemeConfigTypes = ['THEME', 'ASSET'];
60
+ const getConfigPaths = (types) => {
61
+ return {
62
+ ...Object.fromEntries(Object.keys(ConfigType)
63
+ .filter((type) => !types || types.find((inType) => inType === type))
64
+ .map((type) => [
65
+ type,
66
+ // a bit of a hack to make the path singular for content configs
67
+ // need to refactor this to a function if we add more exceptions
68
+ type === 'CONTENT' || type === 'FRONTEND' || type === 'THEME'
69
+ ? type.toLowerCase()
70
+ : pluralize(type.toLowerCase()),
71
+ ])),
72
+ };
73
+ };
26
74
  const ConfigPaths = {
27
- ...Object.fromEntries(Object.keys(ConfigType).map((type) => [
28
- type,
29
- // a bit of a hack to make the path singular for content configs
30
- // need to refactor this to a function if we add more exceptions
31
- type === 'CONTENT' ? 'content' : pluralize(type.toLowerCase()),
32
- ])),
75
+ ...getConfigPaths(StandardConfigTypes),
76
+ };
77
+ const StorefrontConfigPaths = {
78
+ ...getConfigPaths(StorefrontConfigTypes),
79
+ };
80
+ const ThemeConfigPaths = {
81
+ ...getConfigPaths(ThemeConfigTypes),
33
82
  };
34
83
  var ConfigInputFields;
35
84
  (function (ConfigInputFields) {
@@ -39,17 +88,39 @@ var ConfigInputFields;
39
88
  ConfigInputFields["VALUES"] = "values";
40
89
  ConfigInputFields["VERSION"] = "version";
41
90
  })(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']);
91
+ const FrontendProjectTypes = [
92
+ {
93
+ name: 'Next.js',
94
+ slug: 'nextjs',
95
+ configFileName: 'next.config',
96
+ deployPath: '.vercel/output/static',
97
+ installCommand: 'npx create-next-app@latest frontend --typescript --use-npm --src-dir --app --eslint --import-alias "@/*" --tailwind',
98
+ buildCommand: 'npx @cloudflare/next-on-pages',
99
+ devCommand: 'npx next dev --port ${PORT}',
100
+ },
101
+ {
102
+ name: 'Astro',
103
+ slug: 'astro',
104
+ configFileName: 'astro.config',
105
+ deployPath: 'dist',
106
+ installCommand: 'npm create astro@latest frontend -- --install --no-git --yes --skip-houston --typescript strict',
107
+ buildCommand: 'npx astro build',
108
+ devCommand: 'npx astro dev --port ${PORT}',
109
+ },
110
+ ];
111
+ const frontendProjectConfigExtensions = ['.mjs', '.js'];
112
+ function getAppSlugId(app) {
113
+ return toAppId(app.private_id) || app.public_id || app.id;
114
+ }
115
+ function getFrontendProjectType(appPath) {
116
+ for (const projectType of FrontendProjectTypes) {
117
+ for (const extension of frontendProjectConfigExtensions) {
118
+ const configFile = path.join(appPath, 'frontend', `${projectType.configFileName}${extension}`);
119
+ if (filePathExists(configFile)) {
120
+ return projectType;
121
+ }
122
+ }
51
123
  }
52
- return allowedFileExtensions.has(path.parse(file).ext);
53
124
  }
54
125
  function getConfigTypeFromPath(path) {
55
126
  for (const type in ConfigPaths) {
@@ -63,7 +134,7 @@ function getConfigTypeFromPath(path) {
63
134
  function getConfigTypeKeyFromValue(value) {
64
135
  return Object.keys(ConfigType).find((key) => ConfigType[key] === value);
65
136
  }
66
- function fileExists(filePath) {
137
+ function filePathExists(filePath) {
67
138
  try {
68
139
  // eslint-disable-next-line no-bitwise
69
140
  fs.accessSync(filePath, fs.constants.R_OK | fs.constants.W_OK);
@@ -76,13 +147,22 @@ function fileExists(filePath) {
76
147
  return false;
77
148
  }
78
149
  }
79
- function writeFile(file, data) {
150
+ async function writeFile(file, data) {
151
+ // Convert from base64 if needed
152
+ let contents;
153
+ try {
154
+ const isBase64 = Buffer.from(data, 'base64').toString('base64') === data;
155
+ contents = isBase64 ? Buffer.from(data, 'base64') : data;
156
+ }
157
+ catch {
158
+ contents = data;
159
+ }
80
160
  return new Promise((resolve, _reject) => {
81
161
  fs.mkdir(path.dirname(file), { recursive: true }, (error) => {
82
162
  if (error) {
83
163
  throw error;
84
164
  }
85
- fs.writeFile(file, data, (error) => {
165
+ fs.writeFile(file, contents || '', (error) => {
86
166
  if (error) {
87
167
  throw error;
88
168
  }
@@ -91,35 +171,57 @@ function writeFile(file, data) {
91
171
  });
92
172
  });
93
173
  }
174
+ function deleteFile(file) {
175
+ return new Promise((resolve, _reject) => {
176
+ fs.unlink(file, (error) => {
177
+ if (error) {
178
+ throw error;
179
+ }
180
+ resolve();
181
+ });
182
+ });
183
+ }
94
184
  async function writeJsonFile(file, data = {}) {
95
185
  return writeFile(file, `${JSON.stringify(data, null, 2)}\n`);
96
186
  }
97
- function appLogoIcon(appPath) {
187
+ // Hash method adapted from wrangler-sdk
188
+ function hashFile(filePath, fileContents) {
189
+ const contents = fileContents ?? fs.readFileSync(filePath);
190
+ const base64Contents = contents.toString('base64');
191
+ const extension = path.extname(filePath).substring(1);
192
+ return hashString(base64Contents + extension);
193
+ }
194
+ function hashString(contents) {
195
+ return blake3hash(contents).toString('hex').slice(0, 32);
196
+ }
197
+ function appAssetImage(appPath, fileName) {
98
198
  const assetsDirPath = path.join(appPath, ConfigPaths['ASSET']);
99
- if (!appConfigFileExists(assetsDirPath)) {
199
+ if (!filePathExists(assetsDirPath)) {
100
200
  return;
101
201
  }
102
202
  for (const configFile of fs.readdirSync(assetsDirPath)) {
103
- if (isAppLogoIcon(configFile)) {
203
+ if (path.parse(configFile).name === fileName) {
104
204
  return path.join(assetsDirPath, configFile);
105
205
  }
106
206
  }
107
207
  }
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,
208
+ function appConfigFromFile(filePath, configType, appPath) {
209
+ const { name } = path.parse(filePath);
210
+ const config = AppConfig.create({
115
211
  name,
116
- type: configType,
117
- values: {},
212
+ filePath,
213
+ appPath,
214
+ type: configType || 'file',
215
+ file_path: filePath,
118
216
  });
119
- return appConfig;
217
+ // Do not allow files larger than 10MB
218
+ if (config.mbSize && config.mbSize > 10) {
219
+ throw new Error(`App file size too large: ${config.filePath} (${config.mbSize} MB)`);
220
+ }
221
+ return config;
120
222
  }
121
- function findAppConfig(app, file, configType) {
122
- const { name } = path.parse(file);
223
+ function findAppConfig(app, filePath, configType) {
224
+ const { name } = path.parse(filePath);
123
225
  const config = app?.configs?.find((c) => c && c.type === configType && c.name === name);
124
226
  return config;
125
227
  }
@@ -129,11 +231,11 @@ function findAppConfig(app, file, configType) {
129
231
  * @param appConfigs - List of app configs
130
232
  * @returns {Array} Batches of files to push
131
233
  */
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);
234
+ function batchAppFilesByType(configs) {
235
+ const modelsWithExtends = configs.filter((config) => config.type === 'model' && config.values.extends);
236
+ const modelsWithoutExtends = configs.filter((config) => config.type === 'model' && !config.values.extends);
237
+ const contentTypesWithCollection = configs.filter((config) => config.type === 'content' && config.values.collection);
238
+ const contentTypesWithoutCollection = configs.filter((config) => config.type === 'content' && !config.values.collection);
137
239
  const prioritized = [
138
240
  ...modelsWithExtends,
139
241
  ...modelsWithoutExtends,
@@ -142,26 +244,24 @@ function batchAppConfigsByType(appConfigs) {
142
244
  ];
143
245
  const sorted = [
144
246
  ...prioritized,
145
- ...appConfigs.filter((item) => !prioritized.includes(item)),
247
+ ...configs.filter((config) => !prioritized.includes(config)),
146
248
  ];
147
249
  const batches = [];
148
250
  // eslint-disable-next-line guard-for-in
149
251
  for (const key in ConfigTypeBatchOrder) {
150
252
  const configType = ConfigTypeBatchOrder[key];
151
- const configsByType = sorted.filter((item) => item.type === configType);
152
- if (configsByType.length === 0) {
153
- continue;
154
- }
253
+ const configsByType = sorted.filter((config) => config.type === configType);
155
254
  batches.push({
156
255
  configs: configsByType,
157
- label: titleize(configType),
256
+ label: configType === ConfigType.FILE ? 'App files' : titleize(configType),
158
257
  type: configType,
159
258
  });
160
259
  }
260
+ // Add base files if config types are not specified
161
261
  return batches;
162
262
  }
163
- export { ConfigInputFields, ConfigPaths, ConfigType,
263
+ export { SwellJsonFields, ConfigInputFields, ConfigPaths, StorefrontConfigPaths, StorefrontConfigTypes, ThemeConfigTypes, ThemeConfigPaths, ConfigType, FrontendProjectTypes,
164
264
  // functions
165
- appConfigFromFile, appLogoIcon, batchAppConfigsByType, fileExists, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, hasAllowedFileExtension, isAppLogoIcon, writeFile, writeJsonFile, };
265
+ getAppSlugId, appConfigFromFile, appAssetImage, batchAppFilesByType, filePathExists, findAppConfig, getConfigTypeFromPath, getConfigTypeKeyFromValue, writeFile, writeJsonFile, deleteFile, hashFile, hashString, getFrontendProjectType, };
166
266
  export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
167
- export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, configFilePath, } from './paths.js';
267
+ export { globAllFilesByPath, allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, allConfigFilesInDir, allBaseFilesInDir, isPathDirectory, getAllConfigPaths, } 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 { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, allConfigFilesInDir, allBaseFilesInDir, globAllFilesByPath, isPathDirectory, getAllConfigPaths, };
@@ -1,49 +1,84 @@
1
1
  import * as fs from 'node:fs';
2
2
  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) {
3
+ import { globbySync } from 'globby';
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
+ // Explicitly ignore node_modules and lock files just in case
12
+ ignore: [
13
+ '**/.swellrc',
14
+ '**/.git/**',
15
+ '**/node_modules/**',
16
+ '**/package-lock.json',
17
+ '**/yarn.lock',
18
+ ...(options?.ignore || []),
19
+ ],
20
+ expandDirectories: true,
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 (error) {
95
+ // Handle error (e.g., file not found)
96
+ return false;
97
+ }
98
+ }
99
+ export { allConfigDirsPaths, allConfigFilesPaths, allConfigFilesPathsByType, allConfigFilesInDir, allBaseFilesInDir, globAllFilesByPath, isPathDirectory, getAllConfigPaths, };
@@ -24,7 +24,7 @@ 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();
@@ -32,7 +32,6 @@ export async function bundleFunction(filePath) {
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
  }
@@ -44,13 +43,13 @@ function getSwellFunctionWrapper() {
44
43
  async function minifyCode(code) {
45
44
  try {
46
45
  const result = await esbuild.transform(code, {
47
- minifyWhitespace: true,
48
46
  minifyIdentifiers: false,
49
47
  minifySyntax: false,
48
+ minifyWhitespace: true,
50
49
  });
51
50
  return result.code;
52
51
  }
53
- catch (err) {
54
- throw new Error('Compilation error:', err.message);
52
+ catch (error) {
53
+ throw new Error('Compilation error:', error.message);
55
54
  }
56
55
  }
@@ -2,8 +2,10 @@ declare const env: any;
2
2
  declare function ensureLoggedIn(storeId: string): Promise<any>;
3
3
  declare function get(key: string): any;
4
4
  declare function getAll(): any;
5
- declare function getDefaultStore(): any;
5
+ declare function getDefaultStore(): string;
6
6
  declare function setDefaultStore(storeId: string): void;
7
+ declare function getDefaultStorefront(appPath: string): string;
8
+ declare function setDefaultStorefront(appPath: string, storefrontId: string): void;
7
9
  declare function getSessionId(storeId: string): any;
8
10
  /**
9
11
  * Set store data for later access.
@@ -28,9 +30,11 @@ declare const config: {
28
30
  get: typeof get;
29
31
  getAll: typeof getAll;
30
32
  getDefaultStore: typeof getDefaultStore;
33
+ getDefaultStorefront: typeof getDefaultStorefront;
31
34
  getSessionId: typeof getSessionId;
32
35
  getUser: typeof getUser;
33
36
  setDefaultStore: typeof setDefaultStore;
37
+ setDefaultStorefront: typeof setDefaultStorefront;
34
38
  setStore: typeof setStore;
35
39
  setUser: typeof setUser;
36
40
  };
@@ -68,6 +68,18 @@ function getDefaultStore() {
68
68
  function setDefaultStore(storeId) {
69
69
  return configStore.set('defaultStore', storeId);
70
70
  }
71
+ function getDefaultStorefront(appPath) {
72
+ const storeId = getDefaultStore();
73
+ const defaultStorefronts = configStore.get('defaultStorefronts') || {};
74
+ return defaultStorefronts[storeId]?.[appPath];
75
+ }
76
+ function setDefaultStorefront(appPath, storefrontId) {
77
+ const storeId = getDefaultStore();
78
+ const defaultStorefronts = configStore.get('defaultStorefronts') || {};
79
+ defaultStorefronts[storeId] = defaultStorefronts[storeId] || {};
80
+ defaultStorefronts[storeId][appPath] = storefrontId;
81
+ return configStore.set('defaultStorefronts', defaultStorefronts);
82
+ }
71
83
  function getSessionId(storeId) {
72
84
  if (!configStore.has('stores'))
73
85
  return;
@@ -122,9 +134,11 @@ const config = {
122
134
  get,
123
135
  getAll,
124
136
  getDefaultStore,
137
+ getDefaultStorefront,
125
138
  getSessionId,
126
139
  getUser,
127
140
  setDefaultStore,
141
+ setDefaultStorefront,
128
142
  setStore,
129
143
  setUser,
130
144
  };
@@ -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,13 +1,19 @@
1
+ /* eslint-disable no-template-curly-in-string */
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
5
  export const API_BASE_URL = env.get('API_BASE_URL') || 'https://api.swell.store';
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') || 'https://${STORE_ID}.swell.store/admin/api';
9
8
  // url host for admin login
10
9
  export const LOGIN_HOST = env.get('LOGIN_HOST') || 'https://${STORE_ID}.swell.store';
10
+ // url host for app frontend
11
+ export const APP_PAGES_HOST = env.get('APP_PAGES_HOST') ||
12
+ 'https://${STORE_ID}--${APP_ID}--${ENV}.swell.store';
13
+ // url host for storefront app frontend
14
+ export const STOREFRONT_FRONTEND_HOST = env.get('STOREFRONT_FRONTEND_HOST') ||
15
+ env.get('STOREFRONT_PAGES_HOST') ||
16
+ 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.swell.store';
11
17
  // helpers to get constants with replacements
12
18
  export function getAdminApiBaseUrl(storeId) {
13
19
  return ADMIN_API_BASE_URL.replace('${STORE_ID}', storeId);
@@ -15,3 +21,16 @@ export function getAdminApiBaseUrl(storeId) {
15
21
  export function getLoginHost(storeId) {
16
22
  return LOGIN_HOST.replace('${STORE_ID}', storeId || 'login');
17
23
  }
24
+ export function getAppFrontendHost(storeId, installedAppId) {
25
+ return APP_PAGES_HOST.replace('${STORE_ID}', storeId)
26
+ .replace('${APP_ID}', installedAppId)
27
+ .replace('${ENV}', 'app');
28
+ }
29
+ export function getStorefrontFrontendHost(storefrontSlug, branchId) {
30
+ return STOREFRONT_FRONTEND_HOST.replace('${STOREFRONT_SLUG}', storefrontSlug).replace('${BRANCH_PREFIX}', branchId ? `${branchId}.` : '');
31
+ }
32
+ export function getLocalFrontendHost(storeId, sessionId) {
33
+ return APP_PAGES_HOST.replace('${STORE_ID}', storeId)
34
+ .replace('${APP_ID}', sessionId)
35
+ .replace('${ENV}', 'local');
36
+ }
@@ -1,8 +1,8 @@
1
1
  interface CreateContentFileBody {
2
2
  collection: string;
3
- label?: string;
4
3
  description?: string;
5
4
  fields?: ContentFieldProps[];
5
+ label?: string;
6
6
  views?: ContentViewProps[];
7
7
  }
8
8
  interface ContentViewProps {
@@ -12,7 +12,7 @@ interface ContentViewProps {
12
12
  tabs: [];
13
13
  }
14
14
  type ContentView = 'edit' | 'list' | 'new';
15
- type ContentType = 'asset' | 'basic_html' | 'blocks' | 'boolean' | 'category_lookup' | 'checkbox' | 'checkboxes' | 'collection' | 'color' | 'currency' | 'customer_lookup' | 'date' | 'datetime' | 'document' | 'dropdown' | 'email' | 'field_group' | 'generic_lookup' | 'icon' | 'image' | 'long_text' | 'lookup' | 'markdown' | 'number' | 'percent' | 'phone' | 'product_lookup' | 'radio' | 'rich_html' | 'select' | 'short_text' | 'slider' | 'slug' | 'tags' | 'text' | 'textarea' | 'time' | 'toggle' | 'url' | 'variant_lookup' | 'video';
15
+ type ContentType = 'asset' | 'basic_html' | 'boolean' | 'category_lookup' | 'checkbox' | 'checkboxes' | 'collection' | 'color' | 'color_scheme' | 'color_scheme_group' | 'currency' | 'customer_lookup' | 'date' | 'datetime' | 'document' | 'dropdown' | 'email' | 'field_group' | 'font_family' | 'generic_lookup' | 'html' | 'icon' | 'image' | 'long_text' | 'lookup' | 'markdown' | 'number' | 'percent' | 'phone' | 'product_lookup' | 'radio' | 'rich_html' | 'rich_text' | 'select' | 'short_text' | 'slider' | 'slug' | 'tags' | 'text' | 'textarea' | 'time' | 'toggle' | 'url' | 'variant_lookup' | 'video';
16
16
  interface ContentFieldProps {
17
17
  id: string;
18
18
  label: string;
@@ -8,8 +8,8 @@ const parseViews = (views) => {
8
8
  viewResponse.push({
9
9
  id: viewId,
10
10
  ...(viewId === 'list' ? { nav: {} } : {}),
11
- tabs: [],
12
11
  fields: [],
12
+ tabs: [],
13
13
  });
14
14
  }
15
15
  return viewResponse;
@@ -7,11 +7,11 @@ interface CreateFunctionArgs {
7
7
  interface WriteFunctionFileParams {
8
8
  description: string;
9
9
  events: string;
10
- route: string;
11
10
  functionName: string;
12
11
  language: FunctionLanguage;
12
+ route: string;
13
13
  schedule: string;
14
14
  trigger: FunctionType;
15
15
  }
16
- declare const getFunctionTemplate: ({ description, events, route, schedule, language, trigger, }: WriteFunctionFileParams) => string;
16
+ declare const getFunctionTemplate: ({ description, events, language, route, schedule, trigger, }: WriteFunctionFileParams) => string;
17
17
  export { CreateFunctionArgs, FunctionLanguage, FunctionType, WriteFunctionFileParams, getFunctionTemplate, };
@@ -1,9 +1,9 @@
1
- const getFunctionTemplate = ({ description, events, route, schedule, language, trigger, }) => {
1
+ const getFunctionTemplate = ({ description, events, language, route, schedule, trigger, }) => {
2
2
  let typeAppend = ``;
3
3
  if (trigger === 'model') {
4
4
  const eventsList = events
5
5
  .split(/\s*,\s*/g)
6
- .filter((e) => e)
6
+ .filter(Boolean)
7
7
  .map((eventName) => `'${eventName}'`)
8
8
  .join(', ');
9
9
  typeAppend = `,