@swell/cli 2.0.1-alpha.17 → 2.0.1-alpha.18
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/app-command.d.ts +1 -1
- package/dist/app-command.js +3 -3
- package/dist/commands/app/dev.d.ts +3 -5
- package/dist/commands/app/dev.js +3 -6
- package/dist/commands/app/frontend/deploy.d.ts +1 -3
- package/dist/commands/app/frontend/deploy.js +2 -5
- package/dist/commands/app/frontend/dev.d.ts +4 -6
- package/dist/commands/app/frontend/dev.js +12 -15
- package/dist/commands/app/info.js +1 -3
- package/dist/commands/app/init.js +2 -2
- package/dist/commands/app/install.js +0 -1
- package/dist/commands/app/pull.d.ts +3 -3
- package/dist/commands/app/pull.js +16 -16
- package/dist/commands/app/push.d.ts +4 -4
- package/dist/commands/app/push.js +28 -31
- package/dist/commands/app/release.d.ts +3 -3
- package/dist/commands/app/release.js +43 -45
- package/dist/commands/app/version.d.ts +4 -4
- package/dist/commands/app/version.js +28 -28
- package/dist/commands/create/app.d.ts +15 -15
- package/dist/commands/create/app.js +66 -66
- package/dist/commands/create/frontend.js +4 -7
- package/dist/commands/env.d.ts +12 -0
- package/dist/commands/env.js +31 -0
- package/dist/commands/login.js +1 -3
- package/dist/commands/logs.js +0 -2
- package/dist/commands/theme/dev.d.ts +6 -8
- package/dist/commands/theme/dev.js +30 -33
- package/dist/commands/theme/init.d.ts +1 -1
- package/dist/commands/theme/init.js +1 -1
- package/dist/commands/theme/pull.d.ts +7 -7
- package/dist/commands/theme/pull.js +21 -21
- package/dist/commands/theme/push.d.ts +7 -7
- package/dist/commands/theme/push.js +24 -27
- package/dist/create-app-command.d.ts +7 -7
- package/dist/create-app-command.js +146 -147
- package/dist/create-config-command.js +0 -2
- package/dist/env/local.d.ts +8 -0
- package/dist/env/local.js +7 -0
- package/dist/env/production.d.ts +8 -0
- package/dist/env/production.js +7 -0
- package/dist/env/review.d.ts +8 -0
- package/dist/env/review.js +7 -0
- package/dist/env/staging.d.ts +8 -0
- package/dist/env/staging.js +7 -0
- package/dist/lib/api.d.ts +3 -3
- package/dist/lib/api.js +35 -35
- package/dist/lib/app-config.d.ts +2 -2
- package/dist/lib/app-config.js +12 -11
- package/dist/lib/apps/app-config.d.ts +19 -19
- package/dist/lib/apps/app-config.js +59 -65
- package/dist/lib/apps/index.d.ts +56 -56
- package/dist/lib/apps/index.js +40 -42
- package/dist/lib/apps/paths.d.ts +1 -1
- package/dist/lib/apps/paths.js +4 -4
- package/dist/lib/config.d.ts +4 -1
- package/dist/lib/config.js +35 -1
- package/dist/lib/constants.js +7 -8
- package/dist/lib/create/index.js +2 -2
- package/dist/lib/stores.d.ts +4 -2
- package/dist/lib/stores.js +4 -2
- package/dist/push-app-command.d.ts +34 -34
- package/dist/push-app-command.js +391 -402
- package/dist/remote-app-command.d.ts +24 -24
- package/dist/remote-app-command.js +245 -259
- package/oclif.manifest.json +152 -119
- package/package.json +2 -1
package/dist/push-app-command.js
CHANGED
|
@@ -1,64 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as fs from 'node:fs';
|
|
3
|
-
import { confirm, select, input } from '@inquirer/prompts';
|
|
4
|
-
import Stream from 'stream';
|
|
1
|
+
import { confirm, input, select } from '@inquirer/prompts';
|
|
5
2
|
import { $ } from 'execa';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import Stream from 'node:stream';
|
|
6
6
|
import ora from 'ora';
|
|
7
|
-
import { ConfigType, FrontendProjectTypes, findAppConfig, globAllFilesByPath, allConfigFilesInDir, appConfigFromFile, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, getAppSlugId, hashString, filePathExists, isPathDirectory, } from './lib/apps/index.js';
|
|
8
|
-
import { default as localConfig } from './lib/config.js';
|
|
9
7
|
import { default as swellConfig } from './lib/app-config.js';
|
|
10
|
-
import
|
|
8
|
+
import { ConfigType, FrontendProjectTypes, allConfigFilesInDir, appConfigFromFile, filePathExists, findAppConfig, getAppSlugId, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, globAllFilesByPath, hashString, isPathDirectory, } from './lib/apps/index.js';
|
|
9
|
+
import { default as localConfig } from './lib/config.js';
|
|
11
10
|
import { toAppId } from './lib/create/index.js';
|
|
11
|
+
import style from './lib/style.js';
|
|
12
12
|
import { RemoteAppCommand } from './remote-app-command.js';
|
|
13
13
|
const PUSH_CONCURRENCY = 3;
|
|
14
14
|
export class PushAppCommand extends RemoteAppCommand {
|
|
15
15
|
frontendPath = '';
|
|
16
|
-
watchingFiles = [];
|
|
17
|
-
watchingChangeQueue = [];
|
|
18
|
-
watchingChangeQueueRunning = false;
|
|
19
16
|
logWatchChanges = true;
|
|
20
17
|
onWatchChange;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
shell: true,
|
|
25
|
-
stdin: 'inherit',
|
|
26
|
-
stderr: onOutput ? 'pipe' : 'inherit',
|
|
27
|
-
stdout: onOutput ? 'pipe' : 'inherit',
|
|
28
|
-
});
|
|
29
|
-
if (onOutput) {
|
|
30
|
-
const outStream = new Stream.Writable();
|
|
31
|
-
outStream._write = (chunk, _encoding, next) => {
|
|
32
|
-
const string = chunk.toString();
|
|
33
|
-
const out = onOutput(string);
|
|
34
|
-
if (out !== false) {
|
|
35
|
-
console.log(string);
|
|
36
|
-
}
|
|
37
|
-
next();
|
|
38
|
-
};
|
|
39
|
-
await $$ `${command}`.pipeStdout?.(outStream).pipeStderr?.(outStream);
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
await $$ `${command}`;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
setWatchingFiles() {
|
|
46
|
-
this.watchingFiles = globAllFilesByPath(this.appPath);
|
|
47
|
-
}
|
|
48
|
-
async watchForChanges({ logChanges, syncAll, onChange, } = {}) {
|
|
49
|
-
this.logWatchChanges = logChanges ?? true;
|
|
50
|
-
this.onWatchChange = onChange;
|
|
51
|
-
this.setWatchingFiles();
|
|
52
|
-
if (syncAll) {
|
|
53
|
-
await this.pushAppConfigs();
|
|
54
|
-
fs.watch(this.appPath, { recursive: true }, async (_eventType, _filename) => {
|
|
55
|
-
await this.pushAppConfigs();
|
|
56
|
-
this.setWatchingFiles();
|
|
57
|
-
});
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
fs.watch(this.appPath, { recursive: true }, this.watchListener);
|
|
61
|
-
}
|
|
18
|
+
watchingChangeQueue = [];
|
|
19
|
+
watchingChangeQueueRunning = false;
|
|
20
|
+
watchingFiles = [];
|
|
62
21
|
watchListener = async (eventType, filename) => {
|
|
63
22
|
const configFile = filename;
|
|
64
23
|
const pathParsed = path.parse(configFile);
|
|
@@ -117,51 +76,112 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
117
76
|
}
|
|
118
77
|
}
|
|
119
78
|
};
|
|
120
|
-
async
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
this.watchingChangeQueue.push({
|
|
136
|
-
action: 'push',
|
|
137
|
-
configFile,
|
|
138
|
-
appConfig: config,
|
|
139
|
-
});
|
|
79
|
+
async buildFrontend(projectType) {
|
|
80
|
+
this.log(`Building ${projectType.name} frontend...\n`);
|
|
81
|
+
// TODO: check package.json for a "build" command and run that if it exists
|
|
82
|
+
await this.exec(projectType.buildCommand);
|
|
83
|
+
}
|
|
84
|
+
async chooseAppToPull(query) {
|
|
85
|
+
const typeLabelPlural = this.appType === 'theme' ? 'themes' : 'apps';
|
|
86
|
+
const typeLabelPrefixed = this.appType === 'theme' ? 'a theme' : 'an app';
|
|
87
|
+
const typeQuery = this.appType === 'theme' ? { type: 'theme' } : { type: 'storefront' };
|
|
88
|
+
const apps = await this.api.get({
|
|
89
|
+
adminPath: `/apps`,
|
|
90
|
+
}, { query: { limit: null, ...typeQuery, ...query } });
|
|
91
|
+
if (!apps.count) {
|
|
92
|
+
const currentStore = localConfig.getDefaultStore();
|
|
93
|
+
this.error(`There are no ${typeLabelPlural} associated with your store ${style.storeId(currentStore)}`);
|
|
140
94
|
}
|
|
141
|
-
await
|
|
95
|
+
const appSlugId = (await select({
|
|
96
|
+
choices: apps.results.map((app) => ({
|
|
97
|
+
name: `${style.appConfigValue(app.name)} (${getAppSlugId(app)})`,
|
|
98
|
+
value: app.private_id,
|
|
99
|
+
})),
|
|
100
|
+
message: `Choose ${typeLabelPrefixed} to pull`,
|
|
101
|
+
}));
|
|
102
|
+
const appId = apps.results.find((app) => app.private_id === appSlugId).id;
|
|
103
|
+
const app = await this.getApp(appId);
|
|
104
|
+
this.log();
|
|
105
|
+
return app;
|
|
142
106
|
}
|
|
143
|
-
async
|
|
144
|
-
|
|
145
|
-
|
|
107
|
+
async createAppStorefront(hasOtherStorefronts = true) {
|
|
108
|
+
let appId;
|
|
109
|
+
let themeAppId;
|
|
110
|
+
if (this.app.type === 'theme') {
|
|
111
|
+
themeAppId = this.app.id;
|
|
112
|
+
const storefrontApps = await this.handleRequestErrors(async () => this.api.get({
|
|
113
|
+
adminPath: `/apps?type=storefront&kind=shop`,
|
|
114
|
+
}));
|
|
115
|
+
const compatibleStorefrontApps = storefrontApps.results.filter((app) => app.installed &&
|
|
116
|
+
app.storefront?.theme?.provider === 'app' &&
|
|
117
|
+
this.app.theme?.storefront?.app === toAppId(app.private_id));
|
|
118
|
+
if (compatibleStorefrontApps.length === 0) {
|
|
119
|
+
this.error('No compatible storefront apps found for this theme. Install a new storefront app first.');
|
|
120
|
+
}
|
|
121
|
+
const targetApp = compatibleStorefrontApps.length === 1
|
|
122
|
+
? compatibleStorefrontApps[0]
|
|
123
|
+
: await select({
|
|
124
|
+
choices: compatibleStorefrontApps.map((app) => ({
|
|
125
|
+
name: `${style.appConfigValue(app.name)} v${app.version}`,
|
|
126
|
+
value: app,
|
|
127
|
+
})),
|
|
128
|
+
message: 'Choose a storefront app',
|
|
129
|
+
});
|
|
130
|
+
appId = targetApp.id;
|
|
131
|
+
this.log(`\n${style.success('✔')} Using ${style.appConfigValue(targetApp.name)} v${targetApp.version}\n`);
|
|
146
132
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
await
|
|
150
|
-
|
|
151
|
-
.map(async ({ action, configFile, appConfig }) => {
|
|
152
|
-
let result;
|
|
153
|
-
if (action === 'remove') {
|
|
154
|
-
result = await this.removeRemoteFile(appConfig, this.logWatchChanges);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
result = await this.pushRemoteFile(appConfig, this.logWatchChanges);
|
|
158
|
-
}
|
|
159
|
-
this.onWatchChange?.(appConfig, result);
|
|
133
|
+
else if (this.app.type === 'storefront') {
|
|
134
|
+
appId = this.app.id;
|
|
135
|
+
const themeApps = await this.handleRequestErrors(async () => this.api.get({
|
|
136
|
+
adminPath: `/apps?type=theme`,
|
|
160
137
|
}));
|
|
138
|
+
const compatibleThemeApps = themeApps.results.filter((app) => app.installed &&
|
|
139
|
+
Object.keys(app.compatibilities || {}).includes(this.app.private_id));
|
|
140
|
+
if (compatibleThemeApps.length === 0) {
|
|
141
|
+
this.error('No compatible theme apps found for this storefront. Install a new theme app first.');
|
|
142
|
+
}
|
|
143
|
+
let targetApp;
|
|
144
|
+
if (compatibleThemeApps.length === 1) {
|
|
145
|
+
targetApp = compatibleThemeApps[0];
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
targetApp = await select({
|
|
149
|
+
choices: compatibleThemeApps.map((app) => ({
|
|
150
|
+
name: `${style.appConfigValue(app.name)} v${app.version}`,
|
|
151
|
+
value: app,
|
|
152
|
+
})),
|
|
153
|
+
message: 'Choose a theme app',
|
|
154
|
+
});
|
|
155
|
+
this.log();
|
|
156
|
+
}
|
|
157
|
+
themeAppId = targetApp.id;
|
|
158
|
+
this.log(`${style.success('✔')} Using ${style.appConfigValue(targetApp.name)} v${targetApp.version}\n`);
|
|
161
159
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
160
|
+
else {
|
|
161
|
+
this.error('App type not supported for storefront creation');
|
|
162
|
+
}
|
|
163
|
+
let name;
|
|
164
|
+
if (hasOtherStorefronts) {
|
|
165
|
+
name = await input({
|
|
166
|
+
default: this.app.name,
|
|
167
|
+
message: 'Name your storefront',
|
|
168
|
+
});
|
|
169
|
+
if (name === this.app.name) {
|
|
170
|
+
name = undefined; // undefined allows the api to auto increment name
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return this.handleRequestErrors(async () => this.api.post({
|
|
174
|
+
adminPath: `/storefronts`,
|
|
175
|
+
}, {
|
|
176
|
+
body: {
|
|
177
|
+
app_id: appId,
|
|
178
|
+
name,
|
|
179
|
+
theme_app_id: themeAppId,
|
|
180
|
+
},
|
|
181
|
+
onAsyncGetPath: (response) => ({
|
|
182
|
+
adminPath: `/storefronts/${response.id}`,
|
|
183
|
+
}),
|
|
184
|
+
}));
|
|
165
185
|
}
|
|
166
186
|
async deployAppFrontend(force, log) {
|
|
167
187
|
const currentStore = localConfig.getDefaultStore();
|
|
@@ -188,45 +208,14 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
188
208
|
}
|
|
189
209
|
}
|
|
190
210
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
.filter((config) => config.filePath?.startsWith(`frontend${path.sep}`))
|
|
195
|
-
.map((config) => config.hash)
|
|
196
|
-
.sort()
|
|
197
|
-
.join('|');
|
|
198
|
-
if (frontendHashes.length > 0) {
|
|
199
|
-
const packageHash = localConfigs.find((config) => config.filePath === 'package.json')?.hash;
|
|
200
|
-
frontendHashes += `|${packageHash}`;
|
|
201
|
-
}
|
|
202
|
-
return frontendHashes?.length > 0 ? hashString(frontendHashes) : undefined;
|
|
203
|
-
}
|
|
204
|
-
getFrontendProjectType(required = true) {
|
|
205
|
-
this.frontendPath = path.join(this.appPath, 'frontend');
|
|
206
|
-
if (!required) {
|
|
207
|
-
const frontendExists = filePathExists(this.frontendPath);
|
|
208
|
-
if (!frontendExists) {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
const projectType = getFrontendProjectType(this.appPath);
|
|
213
|
-
if (!projectType) {
|
|
214
|
-
this.error(`No valid frontend app found in ${this.appPath}/${this.frontendPath}. Supported frameworks include: ${FrontendProjectTypes.map((type) => type.slug).join(', ')}.`);
|
|
215
|
-
}
|
|
216
|
-
return projectType;
|
|
217
|
-
}
|
|
218
|
-
async ensureLoggedIn() {
|
|
211
|
+
async deployFrontend(projectType) {
|
|
212
|
+
// TODO: check package.json for a "deploy" command and run that if it exists
|
|
213
|
+
// Note it must return a URL somehow
|
|
219
214
|
try {
|
|
220
|
-
|
|
221
|
-
if (!user) {
|
|
222
|
-
throw new Error();
|
|
223
|
-
}
|
|
215
|
+
return this.wranglerDeployFrontend(projectType);
|
|
224
216
|
}
|
|
225
|
-
catch {
|
|
226
|
-
this.
|
|
227
|
-
await this.config.runCommand('login');
|
|
228
|
-
this.log();
|
|
229
|
-
return true;
|
|
217
|
+
catch (error) {
|
|
218
|
+
this.error(`Error deploying frontend: ${error.message}`);
|
|
230
219
|
}
|
|
231
220
|
}
|
|
232
221
|
async ensureAppExists(file, shouldCreate = true) {
|
|
@@ -237,10 +226,9 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
237
226
|
currentStore = localConfig.getDefaultStore();
|
|
238
227
|
this.api.setStoreEnv(currentStore, 'test');
|
|
239
228
|
}
|
|
240
|
-
const app = await this.getAppWithConfig('').catch((_error) =>
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
});
|
|
229
|
+
const app = await this.getAppWithConfig('').catch((_error) =>
|
|
230
|
+
// Ignore so we can continue to create it
|
|
231
|
+
null);
|
|
244
232
|
const isTheme = app?.type === 'theme';
|
|
245
233
|
if (app && !isTheme && this.appType === 'theme') {
|
|
246
234
|
this.error(`App ${style.appConfigValue(app?.name)} is not a theme.`);
|
|
@@ -279,88 +267,71 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
279
267
|
}
|
|
280
268
|
return true;
|
|
281
269
|
}
|
|
282
|
-
async
|
|
283
|
-
|
|
270
|
+
async ensureLoggedIn() {
|
|
271
|
+
try {
|
|
272
|
+
const user = await this.api.get({ adminPath: 'user' });
|
|
273
|
+
if (!user) {
|
|
274
|
+
throw new Error('Failed to log in');
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
this.log('Logging in...\n');
|
|
279
|
+
await this.config.runCommand('login');
|
|
280
|
+
this.log();
|
|
284
281
|
return true;
|
|
285
282
|
}
|
|
286
|
-
const existingInstalledApp = await this.api.get({
|
|
287
|
-
adminPath: `/client/apps/${app.private_id}`,
|
|
288
|
-
}, { query: { versioned: true } });
|
|
289
|
-
if (existingInstalledApp) {
|
|
290
|
-
const continuePush = await confirm({
|
|
291
|
-
default: false,
|
|
292
|
-
message: `${style.storeId(currentStore)} has an installed version of ${app.name ? style.appConfigValue(app.name) : 'this app'} v${existingInstalledApp.version}. Do you want to replace it with a development instance?`,
|
|
293
|
-
});
|
|
294
|
-
if (!continuePush) {
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
297
|
-
// uninstall the existing app
|
|
298
|
-
const spinner = ora();
|
|
299
|
-
spinner.start('Removing installed app...');
|
|
300
|
-
try {
|
|
301
|
-
await this.api.put({
|
|
302
|
-
adminPath: `/client/apps/${app.id}`,
|
|
303
|
-
}, {
|
|
304
|
-
body: { uninstalled: true },
|
|
305
|
-
});
|
|
306
|
-
spinner.succeed('Installed app removed.');
|
|
307
|
-
this.log();
|
|
308
|
-
}
|
|
309
|
-
catch (error) {
|
|
310
|
-
spinner.fail(`Error removing installed app from ${style.storeId(currentStore)}.`);
|
|
311
|
-
this.logError(error.message);
|
|
312
|
-
return false;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
return existingInstalledApp;
|
|
316
283
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
284
|
+
async exec(command, onOutput) {
|
|
285
|
+
const $$ = $({
|
|
286
|
+
cwd: this.frontendPath,
|
|
287
|
+
shell: true,
|
|
288
|
+
stderr: onOutput ? 'pipe' : 'inherit',
|
|
289
|
+
stdin: 'inherit',
|
|
290
|
+
stdout: onOutput ? 'pipe' : 'inherit',
|
|
291
|
+
});
|
|
292
|
+
if (onOutput) {
|
|
293
|
+
const outStream = new Stream.Writable();
|
|
294
|
+
outStream._write = (chunk, _encoding, next) => {
|
|
295
|
+
const string = chunk.toString();
|
|
296
|
+
const out = onOutput(string);
|
|
297
|
+
if (out !== false) {
|
|
298
|
+
console.log(string);
|
|
299
|
+
}
|
|
300
|
+
next();
|
|
301
|
+
};
|
|
302
|
+
await $$ `${command}`.pipeStdout?.(outStream).pipeStderr?.(outStream);
|
|
324
303
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
this.appPath = path.resolve(process.cwd(), resolvedPath);
|
|
304
|
+
else {
|
|
305
|
+
await $$ `${command}`;
|
|
328
306
|
}
|
|
329
|
-
return this.appPath;
|
|
330
307
|
}
|
|
331
|
-
async
|
|
308
|
+
async getAllAppStorefronts(params) {
|
|
332
309
|
const query = Object.entries(params || {})
|
|
333
310
|
.map(([key, value]) => `${key}=${value}`)
|
|
334
311
|
.join('&');
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
}));
|
|
339
|
-
}
|
|
340
|
-
catch {
|
|
341
|
-
// noop
|
|
342
|
-
}
|
|
312
|
+
return this.handleRequestErrors(async () => this.api.get({
|
|
313
|
+
adminPath: `/apps/${this.app.id}/storefronts?${query}`,
|
|
314
|
+
}));
|
|
343
315
|
}
|
|
344
316
|
async getAllStorefronts(params) {
|
|
345
317
|
const query = Object.entries(params || {})
|
|
346
318
|
.map(([key, value]) => `${key}=${value}`)
|
|
347
319
|
.join('&');
|
|
348
|
-
return
|
|
320
|
+
return this.handleRequestErrors(async () => this.api.get({
|
|
349
321
|
adminPath: `/storefronts?${query}`,
|
|
350
322
|
}));
|
|
351
323
|
}
|
|
352
|
-
async
|
|
324
|
+
async getAppStorefront(params) {
|
|
353
325
|
const query = Object.entries(params || {})
|
|
354
326
|
.map(([key, value]) => `${key}=${value}`)
|
|
355
327
|
.join('&');
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
localConfig.setDefaultStorefront(this.appPath, this.storefront.id);
|
|
328
|
+
try {
|
|
329
|
+
return this.handleRequestErrors(async () => this.api.get({
|
|
330
|
+
adminPath: `/apps/${this.app.id}/storefront?${query}`,
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
// noop
|
|
364
335
|
}
|
|
365
336
|
}
|
|
366
337
|
async getAppToPull(args, shouldChoose = true) {
|
|
@@ -391,67 +362,50 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
391
362
|
}
|
|
392
363
|
async getExistingApp(appId) {
|
|
393
364
|
try {
|
|
394
|
-
return
|
|
365
|
+
return this.getApp(appId);
|
|
395
366
|
}
|
|
396
|
-
catch (
|
|
397
|
-
if (
|
|
367
|
+
catch (error) {
|
|
368
|
+
if (error.status === 404) {
|
|
398
369
|
const labelPrefix = this.appType === 'theme' ? 'Theme' : 'App';
|
|
399
370
|
this.error(`${labelPrefix} '${appId}' not found.`);
|
|
400
371
|
}
|
|
401
372
|
else {
|
|
402
|
-
throw
|
|
373
|
+
throw error;
|
|
403
374
|
}
|
|
404
375
|
}
|
|
405
376
|
}
|
|
406
|
-
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
message: 'Choose a storefront',
|
|
417
|
-
choices: [
|
|
418
|
-
...storefronts.results.map((storefront) => ({
|
|
419
|
-
name: `${style.appConfigValue(storefront.name)} (${storefront.id}) ${storefront.primary ? '[primary]' : ''}`,
|
|
420
|
-
value: storefront.id,
|
|
421
|
-
})),
|
|
422
|
-
{
|
|
423
|
-
name: 'Create new storefront',
|
|
424
|
-
value: null,
|
|
425
|
-
},
|
|
426
|
-
],
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
if (!storefrontId) {
|
|
430
|
-
this.storefront = await this.createAppStorefront(storefronts.count > 0);
|
|
431
|
-
}
|
|
377
|
+
getFrontendDeploymentHash() {
|
|
378
|
+
const localConfigs = this.getLocalAppConfigs();
|
|
379
|
+
let frontendHashes = localConfigs
|
|
380
|
+
.filter((config) => config.filePath?.startsWith(`frontend${path.sep}`))
|
|
381
|
+
.map((config) => config.hash)
|
|
382
|
+
.sort()
|
|
383
|
+
.join('|');
|
|
384
|
+
if (frontendHashes.length > 0) {
|
|
385
|
+
const packageHash = localConfigs.find((config) => config.filePath === 'package.json')?.hash;
|
|
386
|
+
frontendHashes += `|${packageHash}`;
|
|
432
387
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
this.log(`Storefront ${style.appConfigValue(storefrontId)} not found.\n`);
|
|
443
|
-
localConfig.setDefaultStorefront(this.appPath, '');
|
|
444
|
-
return this.getStorefrontToPush();
|
|
388
|
+
return frontendHashes?.length > 0 ? hashString(frontendHashes) : undefined;
|
|
389
|
+
}
|
|
390
|
+
getFrontendProjectType(required = true) {
|
|
391
|
+
this.frontendPath = path.join(this.appPath, 'frontend');
|
|
392
|
+
if (!required) {
|
|
393
|
+
const frontendExists = filePathExists(this.frontendPath);
|
|
394
|
+
if (!frontendExists) {
|
|
395
|
+
return null;
|
|
445
396
|
}
|
|
446
397
|
}
|
|
447
|
-
|
|
448
|
-
|
|
398
|
+
const projectType = getFrontendProjectType(this.appPath);
|
|
399
|
+
if (!projectType) {
|
|
400
|
+
this.error(`No valid frontend app found in ${this.appPath}/${this.frontendPath}. Supported frameworks include: ${FrontendProjectTypes.map((type) => type.slug).join(', ')}.`);
|
|
401
|
+
}
|
|
402
|
+
return projectType;
|
|
449
403
|
}
|
|
450
404
|
async getStorefrontToPull(args = {}, flags = {}) {
|
|
451
|
-
const { targetPath } = args;
|
|
405
|
+
const { appId, targetPath } = args;
|
|
452
406
|
const targetApp = await this.getAppToPull(args, false);
|
|
453
407
|
const targetStorefront = flags['storefront-id'];
|
|
454
|
-
this.resolveAppPath(
|
|
408
|
+
this.resolveAppPath(appId, targetPath);
|
|
455
409
|
// Only use default if path exists
|
|
456
410
|
let currentStorefront;
|
|
457
411
|
if (isPathDirectory(this.appPath)) {
|
|
@@ -466,13 +420,11 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
466
420
|
: { type: this.appType });
|
|
467
421
|
if (storefronts.count > 0) {
|
|
468
422
|
storefrontId = await select({
|
|
423
|
+
choices: storefronts.results.map((storefront) => ({
|
|
424
|
+
name: `${style.appConfigValue(storefront.name)} (${storefront.id}) ${storefront.primary ? '[primary]' : ''}`,
|
|
425
|
+
value: storefront.id,
|
|
426
|
+
})),
|
|
469
427
|
message: 'Choose a storefront',
|
|
470
|
-
choices: [
|
|
471
|
-
...storefronts.results.map((storefront) => ({
|
|
472
|
-
name: `${style.appConfigValue(storefront.name)} (${storefront.id}) ${storefront.primary ? '[primary]' : ''}`,
|
|
473
|
-
value: storefront.id,
|
|
474
|
-
})),
|
|
475
|
-
],
|
|
476
428
|
});
|
|
477
429
|
this.storefront = storefronts.results.find((storefront) => storefront.id === storefrontId);
|
|
478
430
|
const appId = this.appType === 'theme'
|
|
@@ -508,111 +460,84 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
508
460
|
this.swellConfig = swellConfig(this.appPath);
|
|
509
461
|
return this.storefront;
|
|
510
462
|
}
|
|
511
|
-
async
|
|
512
|
-
const
|
|
513
|
-
const
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
return app;
|
|
533
|
-
}
|
|
534
|
-
async createAppStorefront(hasOtherStorefronts = true) {
|
|
535
|
-
let appId;
|
|
536
|
-
let themeAppId;
|
|
537
|
-
if (this.app.type === 'theme') {
|
|
538
|
-
themeAppId = this.app.id;
|
|
539
|
-
const storefrontApps = await this.handleRequestErrors(async () => this.api.get({
|
|
540
|
-
adminPath: `/apps?type=storefront&kind=shop`,
|
|
541
|
-
}));
|
|
542
|
-
const compatibleStorefrontApps = storefrontApps.results.filter((app) => app.installed &&
|
|
543
|
-
app.storefront?.theme?.provider === 'app' &&
|
|
544
|
-
this.app.theme?.storefront?.app === toAppId(app.private_id));
|
|
545
|
-
if (!compatibleStorefrontApps.length) {
|
|
546
|
-
this.error('No compatible storefront apps found for this theme. Install a new storefront app first.');
|
|
547
|
-
}
|
|
548
|
-
let targetApp;
|
|
549
|
-
if (compatibleStorefrontApps.length === 1) {
|
|
550
|
-
targetApp = compatibleStorefrontApps[0];
|
|
551
|
-
}
|
|
552
|
-
else {
|
|
553
|
-
targetApp = await select({
|
|
554
|
-
message: 'Choose a storefront app',
|
|
555
|
-
choices: compatibleStorefrontApps.map((app) => ({
|
|
556
|
-
name: `${style.appConfigValue(app.name)} v${app.version}`,
|
|
557
|
-
value: app,
|
|
558
|
-
})),
|
|
463
|
+
async getStorefrontToPush(flags = {}) {
|
|
464
|
+
const targetStorefront = flags['storefront-id'];
|
|
465
|
+
const currentStorefront = localConfig.getDefaultStorefront(this.appPath);
|
|
466
|
+
let storefrontId = targetStorefront || currentStorefront;
|
|
467
|
+
if (!storefrontId || flags['storefront-select']) {
|
|
468
|
+
const storefronts = await this.getAllAppStorefronts({
|
|
469
|
+
type: this.appType,
|
|
470
|
+
});
|
|
471
|
+
if (storefronts.count > 0) {
|
|
472
|
+
storefrontId = await select({
|
|
473
|
+
choices: [
|
|
474
|
+
...storefronts.results.map((storefront) => ({
|
|
475
|
+
name: `${style.appConfigValue(storefront.name)} (${storefront.id}) ${storefront.primary ? '[primary]' : ''}`,
|
|
476
|
+
value: storefront.id,
|
|
477
|
+
})),
|
|
478
|
+
{
|
|
479
|
+
name: 'Create new storefront',
|
|
480
|
+
value: null,
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
message: 'Choose a storefront',
|
|
559
484
|
});
|
|
560
485
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
else if (this.app.type === 'storefront') {
|
|
565
|
-
appId = this.app.id;
|
|
566
|
-
const themeApps = await this.handleRequestErrors(async () => this.api.get({
|
|
567
|
-
adminPath: `/apps?type=theme`,
|
|
568
|
-
}));
|
|
569
|
-
const compatibleThemeApps = themeApps.results.filter((app) => app.installed &&
|
|
570
|
-
Object.keys(app.compatibilities || {}).includes(this.app.private_id));
|
|
571
|
-
if (!compatibleThemeApps.length) {
|
|
572
|
-
this.error('No compatible theme apps found for this storefront. Install a new theme app first.');
|
|
486
|
+
if (!storefrontId) {
|
|
487
|
+
this.storefront = await this.createAppStorefront(storefronts.count > 0);
|
|
573
488
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
489
|
+
}
|
|
490
|
+
if (storefrontId) {
|
|
491
|
+
this.storefront = await this.getAppStorefront({
|
|
492
|
+
storefront_id: storefrontId,
|
|
493
|
+
type: this.appType,
|
|
494
|
+
});
|
|
495
|
+
if (!this.storefront) {
|
|
496
|
+
if (targetStorefront) {
|
|
497
|
+
this.error(`Storefront ${style.appConfigValue(storefrontId)} not found.`);
|
|
498
|
+
}
|
|
499
|
+
this.log(`Storefront ${style.appConfigValue(storefrontId)} not found.\n`);
|
|
500
|
+
localConfig.setDefaultStorefront(this.appPath, '');
|
|
501
|
+
return this.getStorefrontToPush();
|
|
577
502
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
503
|
+
}
|
|
504
|
+
this.app = await this.getApp(this.app.id);
|
|
505
|
+
return this.storefront;
|
|
506
|
+
}
|
|
507
|
+
async handleWatchFileChange(configFile, configType, event) {
|
|
508
|
+
let config;
|
|
509
|
+
if (event === 'deleted') {
|
|
510
|
+
config = findAppConfig(this.app, configFile, configType);
|
|
511
|
+
if (!config) {
|
|
512
|
+
return;
|
|
587
513
|
}
|
|
588
|
-
|
|
589
|
-
|
|
514
|
+
this.watchingChangeQueue.push({
|
|
515
|
+
action: 'remove',
|
|
516
|
+
appConfig: config,
|
|
517
|
+
configFile,
|
|
518
|
+
});
|
|
590
519
|
}
|
|
591
520
|
else {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
message: 'Name your storefront',
|
|
598
|
-
default: this.app.name,
|
|
521
|
+
config = appConfigFromFile(configFile, configType, this.appPath);
|
|
522
|
+
this.watchingChangeQueue.push({
|
|
523
|
+
action: 'push',
|
|
524
|
+
appConfig: config,
|
|
525
|
+
configFile,
|
|
599
526
|
});
|
|
600
|
-
if (name === this.app.name) {
|
|
601
|
-
name = undefined; // undefined allows the api to auto increment name
|
|
602
|
-
}
|
|
603
527
|
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
528
|
+
await this.runWatchChangeQueue();
|
|
529
|
+
}
|
|
530
|
+
logAppFrontendUrl() {
|
|
531
|
+
const currentStore = localConfig.getDefaultStore();
|
|
532
|
+
// Shouldn't happen, but just in case
|
|
533
|
+
if (!this.app.installed?.id) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
this.log(`View your app at ${style.link(this.appFrontendUrl(currentStore, this.app.installed.id))}.`);
|
|
537
|
+
}
|
|
538
|
+
logDashboardUrl(flags) {
|
|
539
|
+
const currentStore = localConfig.getDefaultStore();
|
|
540
|
+
this.log(`View the ${style.appConfigValue(this.app.name)} app in your dashboard at ${style.link(this.dashboardUrl(flags, currentStore))}.`);
|
|
616
541
|
}
|
|
617
542
|
logStorefrontConnected() {
|
|
618
543
|
if (!this.storefront) {
|
|
@@ -624,9 +549,15 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
624
549
|
: '';
|
|
625
550
|
this.log(`[storefront] ${style.appName(name)}${themeLog}\n`);
|
|
626
551
|
}
|
|
627
|
-
|
|
552
|
+
logStorefrontFrontendUrl(storefront, branchId) {
|
|
628
553
|
const currentStore = localConfig.getDefaultStore();
|
|
629
|
-
this.log(`View
|
|
554
|
+
this.log(`View your storefront at ${style.link(this.storefrontFrontendUrl(currentStore, storefront, branchId))}.`);
|
|
555
|
+
}
|
|
556
|
+
async pushFile(relativePath) {
|
|
557
|
+
const basePath = relativePath.split(path.sep)[0];
|
|
558
|
+
const configType = getConfigTypeFromPath(basePath) || ConfigType.FILE;
|
|
559
|
+
const config = appConfigFromFile(relativePath, configType, this.appPath);
|
|
560
|
+
await this.pushRemoteFile(config);
|
|
630
561
|
}
|
|
631
562
|
async pushFilePath(relativePath, force) {
|
|
632
563
|
const topDir = relativePath.split(path.sep)[0];
|
|
@@ -638,23 +569,18 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
638
569
|
for (const { configFile } of allConfigFilesInDir(this.appPath, relativePath, configTypeKey)) {
|
|
639
570
|
const config = appConfigFromFile(configFile, configType, this.appPath);
|
|
640
571
|
allFiles.push(config);
|
|
641
|
-
const exConfig = exAppConfigs.find((exConfig) =>
|
|
642
|
-
return config.filePath === exConfig.filePath;
|
|
643
|
-
});
|
|
572
|
+
const exConfig = exAppConfigs.find((exConfig) => config.filePath === exConfig.filePath);
|
|
644
573
|
if (!force && exConfig && exConfig.hash === config.hash) {
|
|
645
574
|
continue;
|
|
646
575
|
}
|
|
647
576
|
pushFiles.push(config);
|
|
648
577
|
}
|
|
649
|
-
const filesToRemove = exAppConfigs.filter((config) =>
|
|
650
|
-
return !allFiles.find((appConfig) => {
|
|
651
|
-
return appConfig.filePath === config.filePath;
|
|
652
|
-
});
|
|
653
|
-
});
|
|
578
|
+
const filesToRemove = exAppConfigs.filter((config) => !allFiles.some((appConfig) => appConfig.filePath === config.filePath));
|
|
654
579
|
const hasChanges = pushFiles.length > 0 || filesToRemove.length > 0;
|
|
655
580
|
if (pushFiles.length > 0) {
|
|
656
581
|
const pushConcurrency = configTypeKey === 'ASSET' ? 1 : PUSH_CONCURRENCY;
|
|
657
|
-
while (pushFiles.length) {
|
|
582
|
+
while (pushFiles.length > 0) {
|
|
583
|
+
// eslint-disable-next-line no-await-in-loop
|
|
658
584
|
await Promise.all(pushFiles
|
|
659
585
|
.splice(0, pushConcurrency)
|
|
660
586
|
.map(async (appConfig) => {
|
|
@@ -664,7 +590,8 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
664
590
|
}
|
|
665
591
|
// Remove any deleted files
|
|
666
592
|
if (filesToRemove.length > 0) {
|
|
667
|
-
while (filesToRemove.length) {
|
|
593
|
+
while (filesToRemove.length > 0) {
|
|
594
|
+
// eslint-disable-next-line no-await-in-loop
|
|
668
595
|
await Promise.all(filesToRemove.splice(0, PUSH_CONCURRENCY).map(async (appConfig) => {
|
|
669
596
|
await this.removeRemoteFile(appConfig);
|
|
670
597
|
}));
|
|
@@ -678,26 +605,79 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
678
605
|
this.log(`${style.success('✔')} Pushed ${style.path(relativePath)} to ${style.appConfigValue(this.app.name)}.`);
|
|
679
606
|
}
|
|
680
607
|
}
|
|
681
|
-
|
|
682
|
-
const
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
608
|
+
resolveAppPath(appId, targetPath) {
|
|
609
|
+
const resolvedAppId = appId || '';
|
|
610
|
+
let resolvedPath = targetPath || resolvedAppId;
|
|
611
|
+
// If app_id matches directory, use it as target path
|
|
612
|
+
if (!targetPath &&
|
|
613
|
+
isPathDirectory(path.resolve(process.cwd(), resolvedAppId))) {
|
|
614
|
+
resolvedPath = resolvedAppId;
|
|
615
|
+
}
|
|
616
|
+
// Reset app config and path if appId is provided
|
|
617
|
+
if (resolvedPath && this.appPath === '.') {
|
|
618
|
+
this.appPath = path.resolve(process.cwd(), resolvedPath);
|
|
619
|
+
}
|
|
620
|
+
return this.appPath;
|
|
686
621
|
}
|
|
687
|
-
async
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
try {
|
|
691
|
-
return this.wranglerDeployFrontend(projectType);
|
|
622
|
+
async runWatchChangeQueue() {
|
|
623
|
+
if (this.watchingChangeQueueRunning) {
|
|
624
|
+
return;
|
|
692
625
|
}
|
|
693
|
-
|
|
694
|
-
this.
|
|
626
|
+
while (this.watchingChangeQueue.length > 0) {
|
|
627
|
+
this.watchingChangeQueueRunning = true;
|
|
628
|
+
// eslint-disable-next-line no-await-in-loop
|
|
629
|
+
await Promise.all(this.watchingChangeQueue
|
|
630
|
+
.splice(0, PUSH_CONCURRENCY)
|
|
631
|
+
.map(async ({ action, appConfig }) => {
|
|
632
|
+
const result = await (action === 'remove'
|
|
633
|
+
? this.removeRemoteFile(appConfig, this.logWatchChanges)
|
|
634
|
+
: this.pushRemoteFile(appConfig, this.logWatchChanges));
|
|
635
|
+
this.onWatchChange?.(appConfig, result);
|
|
636
|
+
}));
|
|
695
637
|
}
|
|
638
|
+
this.watchingChangeQueueRunning = false;
|
|
639
|
+
// refetch app to get updated configs
|
|
640
|
+
this.app = await this.getAppWithConfig(this.app.id);
|
|
696
641
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
642
|
+
saveCurrentStorefront() {
|
|
643
|
+
const currentStorefront = localConfig.getDefaultStorefront(this.appPath);
|
|
644
|
+
if (this.storefront && this.storefront.id !== currentStorefront) {
|
|
645
|
+
localConfig.setDefaultStorefront(this.appPath, this.storefront.id);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
setWatchingFiles() {
|
|
649
|
+
this.watchingFiles = globAllFilesByPath(this.appPath);
|
|
650
|
+
}
|
|
651
|
+
async updateFrontendDeployment(currentStore, projectType, deploymentUrl, deploymentHash) {
|
|
652
|
+
const spinner = ora();
|
|
653
|
+
spinner.start(`Updating frontend deployment...`);
|
|
654
|
+
await this.handleRequestErrors(async () => this.api.put({ adminPath: `/apps/${this.app.id}` }, {
|
|
655
|
+
body: {
|
|
656
|
+
$client_id: currentStore,
|
|
657
|
+
$environment_id: 'test',
|
|
658
|
+
frontend: {
|
|
659
|
+
deployment_hash: deploymentHash,
|
|
660
|
+
framework: projectType.slug,
|
|
661
|
+
service: 'cloudflare',
|
|
662
|
+
url: deploymentUrl,
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
}), () => spinner.fail('Error updating app deployment'));
|
|
666
|
+
spinner.succeed('Updated frontend deployment.\n');
|
|
667
|
+
}
|
|
668
|
+
async watchForChanges({ logChanges, onChange, syncAll, } = {}) {
|
|
669
|
+
this.logWatchChanges = logChanges ?? true;
|
|
670
|
+
this.onWatchChange = onChange;
|
|
671
|
+
this.setWatchingFiles();
|
|
672
|
+
if (syncAll) {
|
|
673
|
+
await this.pushAppConfigs();
|
|
674
|
+
fs.watch(this.appPath, { recursive: true }, async (_eventType, _filename) => {
|
|
675
|
+
await this.pushAppConfigs();
|
|
676
|
+
this.setWatchingFiles();
|
|
677
|
+
});
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
fs.watch(this.appPath, { recursive: true }, this.watchListener);
|
|
701
681
|
}
|
|
702
682
|
async wranglerDeployFrontend(projectType) {
|
|
703
683
|
let deploymentUrl;
|
|
@@ -706,11 +686,13 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
706
686
|
try {
|
|
707
687
|
await this.exec(`npx wrangler pages deploy ${this.appPath}/frontend/${projectType.deployPath}`, (string) => {
|
|
708
688
|
// Dependent on wrangler output
|
|
709
|
-
|
|
710
|
-
|
|
689
|
+
// Parse the deployment URL from the wrangler output.
|
|
690
|
+
const match = string.match(/Take a peek over at (http\S+)/);
|
|
691
|
+
if (match && match.length > 0) {
|
|
692
|
+
deploymentUrl = match[1];
|
|
711
693
|
return false;
|
|
712
694
|
}
|
|
713
|
-
|
|
695
|
+
if (interactiveError ||
|
|
714
696
|
string.includes('non-interactive mode') ||
|
|
715
697
|
string.includes('non-interactive environment')) {
|
|
716
698
|
interactiveError = true;
|
|
@@ -718,14 +700,15 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
718
700
|
}
|
|
719
701
|
});
|
|
720
702
|
}
|
|
721
|
-
catch (
|
|
703
|
+
catch (error) {
|
|
722
704
|
// noop
|
|
723
705
|
if (interactiveError) {
|
|
724
706
|
this.log(style.funcWarn(`Your Cloudflare environment must be initialized by logging in with \`wrangler login\`, and exporting the \`CLOUDFLARE_ACCOUNT_ID\` environment variable. Refer to https://developers.cloudflare.com/workers/wrangler/configuration/ for details, and re-run this command to connect the deployment with your app.`));
|
|
707
|
+
// eslint-disable-next-line no-process-exit, unicorn/no-process-exit
|
|
725
708
|
process.exit(1);
|
|
726
709
|
}
|
|
727
710
|
else {
|
|
728
|
-
throw
|
|
711
|
+
throw error;
|
|
729
712
|
}
|
|
730
713
|
}
|
|
731
714
|
if (!deploymentUrl) {
|
|
@@ -733,33 +716,39 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
733
716
|
}
|
|
734
717
|
return deploymentUrl;
|
|
735
718
|
}
|
|
736
|
-
async
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
await this.handleRequestErrors(async () => this.api.put({ adminPath: `/apps/${this.app.id}` }, {
|
|
740
|
-
body: {
|
|
741
|
-
$client_id: currentStore,
|
|
742
|
-
$environment_id: 'test',
|
|
743
|
-
frontend: {
|
|
744
|
-
url: deploymentUrl,
|
|
745
|
-
service: 'cloudflare',
|
|
746
|
-
framework: projectType.slug,
|
|
747
|
-
deployment_hash: deploymentHash,
|
|
748
|
-
},
|
|
749
|
-
},
|
|
750
|
-
}), () => spinner.fail('Error updating app deployment'));
|
|
751
|
-
spinner.succeed('Updated frontend deployment.\n');
|
|
752
|
-
}
|
|
753
|
-
logAppFrontendUrl() {
|
|
754
|
-
const currentStore = localConfig.getDefaultStore();
|
|
755
|
-
// Shouldn't happen, but just in case
|
|
756
|
-
if (!this.app.installed?.id) {
|
|
757
|
-
return;
|
|
719
|
+
async confirmRemoveInstalledApp(app, currentStore) {
|
|
720
|
+
if (!app?.private_id) {
|
|
721
|
+
return true;
|
|
758
722
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
723
|
+
const existingInstalledApp = await this.api.get({
|
|
724
|
+
adminPath: `/client/apps/${app.private_id}`,
|
|
725
|
+
}, { query: { versioned: true } });
|
|
726
|
+
if (existingInstalledApp) {
|
|
727
|
+
const continuePush = await confirm({
|
|
728
|
+
default: false,
|
|
729
|
+
message: `${style.storeId(currentStore)} has an installed version of ${app.name ? style.appConfigValue(app.name) : 'this app'} v${existingInstalledApp.version}. Do you want to replace it with a development instance?`,
|
|
730
|
+
});
|
|
731
|
+
if (!continuePush) {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
// uninstall the existing app
|
|
735
|
+
const spinner = ora();
|
|
736
|
+
spinner.start('Removing installed app...');
|
|
737
|
+
try {
|
|
738
|
+
await this.api.put({
|
|
739
|
+
adminPath: `/client/apps/${app.id}`,
|
|
740
|
+
}, {
|
|
741
|
+
body: { uninstalled: true },
|
|
742
|
+
});
|
|
743
|
+
spinner.succeed('Installed app removed.');
|
|
744
|
+
this.log();
|
|
745
|
+
}
|
|
746
|
+
catch (error) {
|
|
747
|
+
spinner.fail(`Error removing installed app from ${style.storeId(currentStore)}.`);
|
|
748
|
+
this.logError(error.message);
|
|
749
|
+
return false;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return existingInstalledApp;
|
|
764
753
|
}
|
|
765
754
|
}
|