@swell/cli 2.0.1-alpha.4 → 2.0.1-alpha.6
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.
- package/dist/commands/app/_pull.js +2 -2
- package/dist/commands/app/install.js +3 -1
- package/dist/commands/app/push.d.ts +3 -0
- package/dist/commands/app/push.js +18 -6
- package/dist/lib/apps/app-config.js +4 -1
- package/dist/lib/bundle.js +3 -0
- package/dist/lib/create/index.d.ts +1 -1
- package/dist/lib/create/index.js +5 -3
- package/dist/remote-app-command.js +21 -8
- package/oclif.manifest.json +7 -2
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Args } from '@oclif/core';
|
|
2
2
|
import { dasherize, pluralize, underscore } from 'inflection';
|
|
3
|
-
import snakeCase from 'lodash/snakeCase.js';
|
|
4
3
|
import * as path from 'node:path';
|
|
5
4
|
import { newConfig } from '../../lib/app-config.js';
|
|
6
5
|
import { ConfigPaths, writeJsonFile, } from '../../lib/apps/index.js';
|
|
6
|
+
import { toAppId } from '../../lib/create/index.js';
|
|
7
7
|
import style from '../../lib/style.js';
|
|
8
8
|
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
9
9
|
export default class AppPull extends RemoteAppCommand {
|
|
@@ -25,7 +25,7 @@ export default class AppPull extends RemoteAppCommand {
|
|
|
25
25
|
const config = newConfig();
|
|
26
26
|
const appConfigJson = {
|
|
27
27
|
description: this.app.description || '',
|
|
28
|
-
id:
|
|
28
|
+
id: toAppId(this.app.name),
|
|
29
29
|
name: this.app.name,
|
|
30
30
|
version: this.app.version,
|
|
31
31
|
};
|
|
@@ -93,7 +93,8 @@ Without a store specified, install the app in the Live environment of the curren
|
|
|
93
93
|
else {
|
|
94
94
|
extraMessage = ` ${style.appConfigValue(this.app.name)} v${versionToInstall} on ${style.storeId(storeToInstall)} [${envToInstall ? envToInstall : 'live'}]`;
|
|
95
95
|
}
|
|
96
|
-
const shouldContinue =
|
|
96
|
+
const shouldContinue = existingVersion ||
|
|
97
|
+
(await this.confirmRemoveDevelopmentApp(storeToInstall));
|
|
97
98
|
if (!shouldContinue) {
|
|
98
99
|
return;
|
|
99
100
|
}
|
|
@@ -148,6 +149,7 @@ Without a store specified, install the app in the Live environment of the curren
|
|
|
148
149
|
query: { source_id: this.app.id },
|
|
149
150
|
});
|
|
150
151
|
if (existingSourcedApps.results[0]?.id) {
|
|
152
|
+
console.log(existingSourcedApps.results[0]);
|
|
151
153
|
const continueInstall = await confirm({
|
|
152
154
|
message: `${style.storeId(storeToInstall)} has a development version of ${style.appConfigValue(this.app.name)}. Do you want to replace it with an installed instance?`,
|
|
153
155
|
default: false,
|
|
@@ -2,6 +2,9 @@ import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
|
2
2
|
export default class AppPush extends RemoteAppCommand {
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
file: import("@oclif/core/lib/interfaces/parser.js").Arg<string | undefined, Record<string, unknown>>;
|
|
7
|
+
};
|
|
5
8
|
static flags: {
|
|
6
9
|
file: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
10
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { confirm } from '@inquirer/prompts';
|
|
2
|
-
import { Flags } from '@oclif/core';
|
|
2
|
+
import { Flags, Args } from '@oclif/core';
|
|
3
3
|
import * as path from 'node:path';
|
|
4
4
|
import ora from 'ora';
|
|
5
5
|
import { appConfigFileExists } from '../../lib/app-config.js';
|
|
@@ -26,6 +26,13 @@ ${Object.values(ConfigPaths)
|
|
|
26
26
|
'swell app push -f content',
|
|
27
27
|
'swell app push -f models/products.json',
|
|
28
28
|
];
|
|
29
|
+
static args = {
|
|
30
|
+
file: Args.string({
|
|
31
|
+
char: 'f',
|
|
32
|
+
description: 'relative path to a configuration file or directory to push',
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
// Deprecating the flags in favor of args
|
|
29
36
|
static flags = {
|
|
30
37
|
file: Flags.string({
|
|
31
38
|
char: 'f',
|
|
@@ -37,20 +44,25 @@ ${Object.values(ConfigPaths)
|
|
|
37
44
|
};
|
|
38
45
|
static summary = 'Push configuration files to your Swell app.';
|
|
39
46
|
async run() {
|
|
40
|
-
const { flags } = await this.parse(AppPush);
|
|
41
|
-
const { file } =
|
|
47
|
+
const { args, flags } = await this.parse(AppPush);
|
|
48
|
+
const { file: fileArg } = args;
|
|
49
|
+
const { file: fileFlag } = flags;
|
|
50
|
+
const file = fileArg || fileFlag;
|
|
42
51
|
const currentStore = swellConfig.getDefaultStore();
|
|
43
52
|
this.api.setStoreEnv(currentStore, 'test');
|
|
44
|
-
this.app = await this.getAppWithConfig(
|
|
53
|
+
this.app = await this.getAppWithConfig('', false);
|
|
45
54
|
const confirmExistingApp = await this.confirmRemoveInstalledApp(currentStore);
|
|
46
55
|
if (confirmExistingApp === false) {
|
|
47
56
|
return;
|
|
48
57
|
}
|
|
49
58
|
// If app exists but owned by another client, offer to create a new instance
|
|
50
|
-
if (this.app.client_id !== currentStore) {
|
|
59
|
+
if (this.app.client_id && this.app.client_id !== currentStore) {
|
|
51
60
|
if (!confirmExistingApp?.id) {
|
|
61
|
+
const otherAppName = this.app.name || this.appConfig.get('name');
|
|
52
62
|
const confirmPushNew = await confirm({
|
|
53
|
-
message: `${style.appConfigValue(
|
|
63
|
+
message: `${style.appConfigValue(otherAppName)} has a dev version in another ${this.app.client_id
|
|
64
|
+
? `store ${style.dim(`(${this.app.client_id})`)}`
|
|
65
|
+
: 'environment'}. Do you want to create a new development instance in ${style.storeId(currentStore)}?`,
|
|
54
66
|
default: false,
|
|
55
67
|
});
|
|
56
68
|
if (!confirmPushNew) {
|
|
@@ -68,9 +68,12 @@ class AppConfig {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
async postData() {
|
|
71
|
-
if (!this.localPath
|
|
71
|
+
if (!this.localPath) {
|
|
72
72
|
throw new Error('Missing local config path');
|
|
73
73
|
}
|
|
74
|
+
if (!this.fileData) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
74
77
|
if (this.localPath.endsWith('.json')) {
|
|
75
78
|
try {
|
|
76
79
|
this.values = JSON.parse(this.fileData);
|
package/dist/lib/bundle.js
CHANGED
|
@@ -22,6 +22,9 @@ export async function bundleFunction(filePath) {
|
|
|
22
22
|
? line.replace('module.exports', 'var moduleExports')
|
|
23
23
|
: line)
|
|
24
24
|
.join('\n');
|
|
25
|
+
// Remove comments with to get a unique hash in the API
|
|
26
|
+
// Because esbuild puts the file name as a comment
|
|
27
|
+
code = code.replace(/^[\n]?\/\/.*/gm, '');
|
|
25
28
|
// eslint-disable-next-line no-new-func
|
|
26
29
|
const evalFn = new Function(`${code}\nreturn { ...moduleExports }`);
|
|
27
30
|
const { config, ...moduleExports } = evalFn();
|
|
@@ -5,7 +5,7 @@ interface CreateFileParams {
|
|
|
5
5
|
}
|
|
6
6
|
declare const toFileName: (value: string) => string;
|
|
7
7
|
declare const toAppName: (value: string) => string;
|
|
8
|
-
declare const toAppId: (value: string) => string;
|
|
8
|
+
declare const toAppId: (value: string | undefined) => string;
|
|
9
9
|
declare const toFunctionFileName: (value: string) => string;
|
|
10
10
|
declare const toCollectionName: (value: string) => string;
|
|
11
11
|
declare const toCollectionLabel: (value: string) => string;
|
package/dist/lib/create/index.js
CHANGED
|
@@ -4,9 +4,11 @@ const toFileName = (value) => dasherize(underscore(value.split('.')[0].replace('
|
|
|
4
4
|
// Example: 'my-things' => 'My Things'
|
|
5
5
|
const toAppName = (value) => pluralize(titleize(value).replace('-', ' '));
|
|
6
6
|
// Example: 'My App' => 'my_app'
|
|
7
|
-
const toAppId = (value) => value
|
|
8
|
-
.replace(/[^a-
|
|
9
|
-
.replace(
|
|
7
|
+
const toAppId = (value) => String(value || '')
|
|
8
|
+
.replace(/[^a-zA-Z0-9\-_\s:\.\/]/g, '')
|
|
9
|
+
.replace(/[_\s-:\.\/]+/g, '_')
|
|
10
|
+
.replace(/^[_]+|[_]+$/g, '')
|
|
11
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
10
12
|
.toLowerCase();
|
|
11
13
|
// Example: 'My Thing' => 'my-thing'
|
|
12
14
|
// And: 'myThing' => 'myThing'
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { inflect } from 'inflection';
|
|
2
|
-
import snakeCase from 'lodash/snakeCase.js';
|
|
3
2
|
import * as fs from 'node:fs';
|
|
4
3
|
import * as path from 'node:path';
|
|
5
4
|
import ora from 'ora';
|
|
6
5
|
import { AppCommand } from './app-command.js';
|
|
7
6
|
import { appConfigFileExists, readRcFile, writeRcFile, } from './lib/app-config.js';
|
|
8
7
|
import { FunctionProcessingError, IgnoringFileError, allConfigFilesPaths, appConfigFromFile, appLogoIcon, batchAppConfigsByType, configFilePath, isAppLogoIcon, } from './lib/apps/index.js';
|
|
8
|
+
import { toAppId } from './lib/create/index.js';
|
|
9
9
|
import { default as swellConfig } from './lib/config.js';
|
|
10
10
|
import { getLoginHost } from './lib/constants.js';
|
|
11
11
|
import style from './lib/style.js';
|
|
@@ -64,7 +64,7 @@ export class RemoteAppCommand extends AppCommand {
|
|
|
64
64
|
this.error(`App ${style.appConfigName('id')} is not defined in swell.json.`);
|
|
65
65
|
}
|
|
66
66
|
if (privateId !== appConfigId) {
|
|
67
|
-
this.error(`App ${style.appConfigName('id')} must be
|
|
67
|
+
this.error(`App ${style.appConfigName('id')} must be declared in snake_case: ${style.appConfigValue(appConfigId)}.`);
|
|
68
68
|
}
|
|
69
69
|
appId = privateId;
|
|
70
70
|
}
|
|
@@ -97,7 +97,7 @@ export class RemoteAppCommand extends AppCommand {
|
|
|
97
97
|
: errorJson.error.message;
|
|
98
98
|
this.error(errorMessage);
|
|
99
99
|
}
|
|
100
|
-
if (swellId && swellId !== id) {
|
|
100
|
+
if (swellId && swellId !== id && swellId !== this.app?.id) {
|
|
101
101
|
app = await this.getAppWithConfig(swellId);
|
|
102
102
|
}
|
|
103
103
|
}
|
|
@@ -113,7 +113,9 @@ export class RemoteAppCommand extends AppCommand {
|
|
|
113
113
|
// when posting to the API
|
|
114
114
|
id: undefined,
|
|
115
115
|
logo_icon: null,
|
|
116
|
-
|
|
116
|
+
...(this.appConfig.store.id
|
|
117
|
+
? { private_id: this.appConfig.store.id }
|
|
118
|
+
: undefined),
|
|
117
119
|
...(remoteApp?.id ? { source_id: remoteApp.id } : undefined),
|
|
118
120
|
};
|
|
119
121
|
const logoIcon = appLogoIcon(this.appPath);
|
|
@@ -126,9 +128,15 @@ export class RemoteAppCommand extends AppCommand {
|
|
|
126
128
|
let appCreated = false;
|
|
127
129
|
if (!app.id) {
|
|
128
130
|
// May already have a dev instance
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
let existingDevApp;
|
|
132
|
+
try {
|
|
133
|
+
existingDevApp = await this.api.get({
|
|
134
|
+
adminPath: `/apps/${this.appConfig.store.id}`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
// noop
|
|
139
|
+
}
|
|
132
140
|
spinner.start(`Creating app...`);
|
|
133
141
|
if (existingDevApp) {
|
|
134
142
|
app = await this.handleRequestErrors(async () => this.api.put({ adminPath: `/apps/${existingDevApp.id}` }, { body }), () => spinner.fail('Error creating app'));
|
|
@@ -220,6 +228,10 @@ export class RemoteAppCommand extends AppCommand {
|
|
|
220
228
|
spinner.start(`Pushing ${pathDesc}`);
|
|
221
229
|
try {
|
|
222
230
|
postData = await appConfig.postData();
|
|
231
|
+
if (!postData) {
|
|
232
|
+
spinner.stop();
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
223
235
|
}
|
|
224
236
|
catch (error) {
|
|
225
237
|
if (error instanceof IgnoringFileError) {
|
|
@@ -327,8 +339,9 @@ export class RemoteAppCommand extends AppCommand {
|
|
|
327
339
|
}
|
|
328
340
|
}
|
|
329
341
|
if (this.appConfig.get('id')) {
|
|
342
|
+
// use the same formula as the backend api
|
|
330
343
|
// eslint-disable-next-line camelcase
|
|
331
|
-
remoteApp.private_id =
|
|
344
|
+
remoteApp.private_id = toAppId(this.appConfig.get('id'));
|
|
332
345
|
}
|
|
333
346
|
return remoteApp;
|
|
334
347
|
}
|
package/oclif.manifest.json
CHANGED
|
@@ -452,7 +452,12 @@
|
|
|
452
452
|
},
|
|
453
453
|
"app:push": {
|
|
454
454
|
"aliases": [],
|
|
455
|
-
"args": {
|
|
455
|
+
"args": {
|
|
456
|
+
"file": {
|
|
457
|
+
"description": "relative path to a configuration file or directory to push",
|
|
458
|
+
"name": "file"
|
|
459
|
+
}
|
|
460
|
+
},
|
|
456
461
|
"description": "Push all app configurations, a specific file, or a specific configuration\ntype to an app in your store's test environment. If the app does not exist yet,\nit will be created and its global ID saved to a .swellrc file.\n\n- If no file is specified, all configuration files will be pushed to the store.\n This includes the app icon (assets/icon.png) and swell.json.\n- If a file is specified, only that file will be pushed to the store.\n- If a directory is specified, only files in that directory will be pushed.\n\nApp configuration directories:\nassets/\ncontent/\nfunctions/\nmodels/\nnotifications/\nsettings/\nwebhooks/",
|
|
457
462
|
"examples": [
|
|
458
463
|
"swell app push",
|
|
@@ -1081,5 +1086,5 @@
|
|
|
1081
1086
|
]
|
|
1082
1087
|
}
|
|
1083
1088
|
},
|
|
1084
|
-
"version": "2.0.1-alpha.
|
|
1089
|
+
"version": "2.0.1-alpha.6"
|
|
1085
1090
|
}
|