@swell/cli 2.5.2 → 2.5.4
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/dev.js +30 -6
- package/dist/commands/app/frontend/dev.d.ts +0 -1
- package/dist/commands/app/frontend/dev.js +13 -25
- package/dist/create-app-command.d.ts +9 -0
- package/dist/create-app-command.js +26 -1
- package/dist/lib/apps/index.js +3 -1
- package/dist/lib/theme-sync.js +5 -4
- package/dist/push-app-command.d.ts +2 -0
- package/dist/push-app-command.js +13 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/dist/commands/app/dev.js
CHANGED
|
@@ -69,12 +69,12 @@ export default class AppDev extends PushAppCommand {
|
|
|
69
69
|
this.saveCurrentStorefront();
|
|
70
70
|
const spinner = ora();
|
|
71
71
|
spinner.start(`Starting app dev server...\n`);
|
|
72
|
-
const serverPort = await this.startProxyServer(port);
|
|
73
|
-
await this.startAppFunctionServer(spinner, serverPort);
|
|
74
|
-
await this.runAppFrontendDevIfApplicable(frontendPort);
|
|
75
72
|
// Register cleanup handlers to clear local proxy on exit
|
|
76
73
|
process.on('SIGINT', this.cleanupOnExit.bind(this));
|
|
77
74
|
process.on('SIGTERM', this.cleanupOnExit.bind(this));
|
|
75
|
+
const serverPort = await this.startProxyServer(port);
|
|
76
|
+
await this.startAppFunctionServer(spinner, serverPort);
|
|
77
|
+
await this.runAppFrontendDevIfApplicable(frontendPort);
|
|
78
78
|
}
|
|
79
79
|
async cleanupOnExit() {
|
|
80
80
|
if (this.isCleaningUp)
|
|
@@ -86,7 +86,9 @@ export default class AppDev extends PushAppCommand {
|
|
|
86
86
|
catch {
|
|
87
87
|
// Ignore errors during cleanup
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
this.log();
|
|
90
|
+
// eslint-disable-next-line no-process-exit
|
|
91
|
+
process.exit();
|
|
90
92
|
}
|
|
91
93
|
async runAppFrontendDevIfApplicable(frontendPort) {
|
|
92
94
|
const projectType = this.getFrontendProjectType(false);
|
|
@@ -98,7 +100,12 @@ export default class AppDev extends PushAppCommand {
|
|
|
98
100
|
// Store frontend port for function router to use for fallback routing
|
|
99
101
|
this.frontendPort = proxyPort;
|
|
100
102
|
this.argv.push('--proxy-port', String(proxyPort));
|
|
101
|
-
this.config.runCommand('app:frontend:dev', this.argv)
|
|
103
|
+
this.config.runCommand('app:frontend:dev', this.argv).catch((error) => {
|
|
104
|
+
// SIGTERM/SIGINT during dev server shutdown is expected
|
|
105
|
+
if (error?.signal === 'SIGTERM' || error?.signal === 'SIGINT')
|
|
106
|
+
return;
|
|
107
|
+
this.error(error);
|
|
108
|
+
});
|
|
102
109
|
return proxyPort;
|
|
103
110
|
}
|
|
104
111
|
}
|
|
@@ -302,10 +309,24 @@ ENVIRONMENT = "development"
|
|
|
302
309
|
async startAppFunctionServer(spinner, serverPort) {
|
|
303
310
|
// Get all functions in this app
|
|
304
311
|
const functions = await this.getAppFunctions();
|
|
312
|
+
const projectType = this.getFrontendProjectType(false);
|
|
313
|
+
const shouldLogPreviewUrl = this.app.type === 'storefront' && !projectType;
|
|
305
314
|
if (functions.length === 0) {
|
|
315
|
+
if (!projectType && this.app.type !== 'storefront') {
|
|
316
|
+
spinner.stop();
|
|
317
|
+
this.log(`No functions or frontend detected. Exiting.\n`);
|
|
318
|
+
await this.cleanupOnExit();
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
306
321
|
// Create routing server to proxy requests to frontend even without functions
|
|
307
322
|
await this.createFunctionRouter(serverPort);
|
|
308
|
-
|
|
323
|
+
if (shouldLogPreviewUrl) {
|
|
324
|
+
spinner.succeed(`App dev server running on port ${serverPort}\n`);
|
|
325
|
+
this.logLocalPreviewUrl();
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
spinner.stop();
|
|
329
|
+
}
|
|
309
330
|
return;
|
|
310
331
|
}
|
|
311
332
|
// Create TMP directory for wrangler configs and bundled functions
|
|
@@ -322,6 +343,9 @@ ENVIRONMENT = "development"
|
|
|
322
343
|
this.log(`${style.appConfigName(`Functions:`)}`);
|
|
323
344
|
await this.logAllFunctions(functions);
|
|
324
345
|
this.log(`${style.success('→')} Call functions at: http://localhost:${serverPort}/<function-name>\n`);
|
|
346
|
+
if (shouldLogPreviewUrl) {
|
|
347
|
+
this.logLocalPreviewUrl();
|
|
348
|
+
}
|
|
325
349
|
}
|
|
326
350
|
async startFunctionServers(functions) {
|
|
327
351
|
const functionStatus = new Map();
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
import { CUSTOM_FRAMEWORK_SLUG, getManualDevHint, getProjectCommands, } from '../../../lib/apps/index.js';
|
|
3
|
-
import { default as localConfig } from '../../../lib/config.js';
|
|
4
|
-
import style from '../../../lib/style.js';
|
|
5
3
|
import { PushAppCommand } from '../../../push-app-command.js';
|
|
6
4
|
import AppDev from '../dev.js';
|
|
7
5
|
export default class AppFrontendDev extends PushAppCommand {
|
|
@@ -37,12 +35,6 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
37
35
|
env: 'test',
|
|
38
36
|
};
|
|
39
37
|
static summary = `Run a frontend app in dev mode from your local machine.`;
|
|
40
|
-
logAppLocalUrl(storeId, sessionId) {
|
|
41
|
-
const frontendAppRoute = this.app.type === 'storefront' && this.app.storefront?.external
|
|
42
|
-
? `/${this.app.private_id}`
|
|
43
|
-
: '';
|
|
44
|
-
this.log(`View it at ${style.link(`${this.localFrontendUrl(storeId, sessionId)}${frontendAppRoute}`)}\n`);
|
|
45
|
-
}
|
|
46
38
|
async run() {
|
|
47
39
|
const { flags } = await this.parse(AppFrontendDev);
|
|
48
40
|
const { port, 'proxy-port': proxyPort, 'app-dev': isAppDev } = flags;
|
|
@@ -66,7 +58,12 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
66
58
|
const serverPort = isAppDev && proxyPort
|
|
67
59
|
? proxyPort
|
|
68
60
|
: await this.startProxyServer(proxyPort || port);
|
|
69
|
-
await this.execFrontendProject(serverPort, isAppDev)
|
|
61
|
+
await this.execFrontendProject(serverPort, isAppDev).catch((error) => {
|
|
62
|
+
// SIGTERM/SIGINT during dev server shutdown is expected
|
|
63
|
+
if (error?.signal === 'SIGTERM' || error?.signal === 'SIGINT')
|
|
64
|
+
return;
|
|
65
|
+
throw error;
|
|
66
|
+
});
|
|
70
67
|
}
|
|
71
68
|
async execFrontendProject(serverPort, isAppDev) {
|
|
72
69
|
const projectType = this.getFrontendProjectType(!isAppDev);
|
|
@@ -76,13 +73,11 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
76
73
|
// Manual mode for unrecognized frameworks
|
|
77
74
|
if (projectType.slug === CUSTOM_FRAMEWORK_SLUG) {
|
|
78
75
|
const hintCommand = getManualDevHint(this.appPath, serverPort);
|
|
79
|
-
const currentStore = localConfig.getDefaultStore();
|
|
80
|
-
const sessionId = localConfig.getSessionId(currentStore);
|
|
81
76
|
this.log(`Detected a frontend project but could not identify the framework.`);
|
|
82
77
|
this.log(`Start your dev server manually on port ${serverPort}:\n`);
|
|
83
78
|
this.log(` cd frontend && ${hintCommand}\n`);
|
|
84
79
|
this.log(`The Swell tunnel is ready and will route traffic once your server is running.\n`);
|
|
85
|
-
this.
|
|
80
|
+
this.logLocalPreviewUrl();
|
|
86
81
|
if (!isAppDev) {
|
|
87
82
|
this.watchForChanges();
|
|
88
83
|
}
|
|
@@ -92,7 +87,6 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
92
87
|
}
|
|
93
88
|
// Get commands transformed for the detected package manager
|
|
94
89
|
const { devCommand } = getProjectCommands(this.appPath, projectType);
|
|
95
|
-
const currentStore = localConfig.getDefaultStore();
|
|
96
90
|
let serverReadyDetected = false;
|
|
97
91
|
await this.execFrontend(devCommand.replace('${PORT}', String(serverPort)), (string) => {
|
|
98
92
|
// Shared: Hide Vite noise output common across frameworks
|
|
@@ -117,8 +111,7 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
117
111
|
(/✓ ready in \d+/i.test(string) || /✓ starting/i.test(string))) {
|
|
118
112
|
serverReadyDetected = true;
|
|
119
113
|
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
120
|
-
|
|
121
|
-
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
114
|
+
setTimeout(() => this.logLocalPreviewUrl(), 100);
|
|
122
115
|
if (!isAppDev) {
|
|
123
116
|
this.watchForChanges();
|
|
124
117
|
}
|
|
@@ -135,8 +128,7 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
135
128
|
/watching for file changes/i.test(string)) {
|
|
136
129
|
serverReadyDetected = true;
|
|
137
130
|
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
138
|
-
|
|
139
|
-
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
131
|
+
setTimeout(() => this.logLocalPreviewUrl(), 100);
|
|
140
132
|
if (!isAppDev) {
|
|
141
133
|
this.watchForChanges();
|
|
142
134
|
}
|
|
@@ -154,8 +146,7 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
154
146
|
/watch mode enabled/i.test(string))) {
|
|
155
147
|
serverReadyDetected = true;
|
|
156
148
|
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
157
|
-
|
|
158
|
-
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
149
|
+
setTimeout(() => this.logLocalPreviewUrl(), 100);
|
|
159
150
|
if (!isAppDev) {
|
|
160
151
|
this.watchForChanges();
|
|
161
152
|
}
|
|
@@ -171,8 +162,7 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
171
162
|
if (!serverReadyDetected) {
|
|
172
163
|
serverReadyDetected = true;
|
|
173
164
|
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
174
|
-
|
|
175
|
-
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
165
|
+
setTimeout(() => this.logLocalPreviewUrl(), 100);
|
|
176
166
|
if (!isAppDev) {
|
|
177
167
|
this.watchForChanges();
|
|
178
168
|
}
|
|
@@ -184,8 +174,7 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
184
174
|
/ready on http:\/\/localhost:\d+/i.test(string)) {
|
|
185
175
|
serverReadyDetected = true;
|
|
186
176
|
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
187
|
-
|
|
188
|
-
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
177
|
+
setTimeout(() => this.logLocalPreviewUrl(), 100);
|
|
189
178
|
if (!isAppDev) {
|
|
190
179
|
this.watchForChanges();
|
|
191
180
|
}
|
|
@@ -203,8 +192,7 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
203
192
|
if (!serverReadyDetected && /nuxt \d+\.\d+\.\d+/i.test(string)) {
|
|
204
193
|
serverReadyDetected = true;
|
|
205
194
|
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
206
|
-
|
|
207
|
-
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
195
|
+
setTimeout(() => this.logLocalPreviewUrl(), 100);
|
|
208
196
|
if (!isAppDev) {
|
|
209
197
|
this.watchForChanges();
|
|
210
198
|
}
|
|
@@ -37,5 +37,14 @@ export declare abstract class CreateAppCommand extends SwellCommand {
|
|
|
37
37
|
};
|
|
38
38
|
setupPackage(name: string, config: any, pkg: PackageManager): Promise<void>;
|
|
39
39
|
tryPackageSetup(name: string, config: any, pkg: PackageManager): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Fix wrangler.jsonc `main` field when it points to a build artifact that
|
|
42
|
+
* doesn't exist at dev time. The @cloudflare/vite-plugin validates this path
|
|
43
|
+
* at startup and crashes during `astro dev` if it can't resolve it.
|
|
44
|
+
*
|
|
45
|
+
* Only replaces the exact legacy value that C3 currently scaffolds.
|
|
46
|
+
* Once C3 updates its template, this becomes a no-op.
|
|
47
|
+
*/
|
|
48
|
+
private fixAstroWranglerEntrypoint;
|
|
40
49
|
private addFrontendAllowedHosts;
|
|
41
50
|
}
|
|
@@ -14,7 +14,7 @@ import style from './lib/style.js';
|
|
|
14
14
|
import { SwellCommand } from './swell-command.js';
|
|
15
15
|
const execAsync = promisify(exec);
|
|
16
16
|
/** Allowed hosts for tunnel providers used in local development */
|
|
17
|
-
const TUNNEL_ALLOWED_HOSTS = ['.
|
|
17
|
+
const TUNNEL_ALLOWED_HOSTS = ['.trycloudflare.com', '.swell.store'];
|
|
18
18
|
/**
|
|
19
19
|
* Find the closing brace position for a config function call (e.g., defineConfig, defineNuxtConfig).
|
|
20
20
|
* Uses brace counting to find the matching `}` for the opening `{`.
|
|
@@ -477,12 +477,37 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
477
477
|
this.log(`There was an error setting up the package manager: ${error.message}`);
|
|
478
478
|
}
|
|
479
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* Fix wrangler.jsonc `main` field when it points to a build artifact that
|
|
482
|
+
* doesn't exist at dev time. The @cloudflare/vite-plugin validates this path
|
|
483
|
+
* at startup and crashes during `astro dev` if it can't resolve it.
|
|
484
|
+
*
|
|
485
|
+
* Only replaces the exact legacy value that C3 currently scaffolds.
|
|
486
|
+
* Once C3 updates its template, this becomes a no-op.
|
|
487
|
+
*/
|
|
488
|
+
async fixAstroWranglerEntrypoint(configPath) {
|
|
489
|
+
const filePath = path.join(configPath, 'frontend', 'wrangler.jsonc');
|
|
490
|
+
let content;
|
|
491
|
+
try {
|
|
492
|
+
content = await fs.readFile(filePath, 'utf8');
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const legacy = '"main": "./dist/_worker.js/index.js"';
|
|
498
|
+
if (!content.includes(legacy)) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
const updated = content.replace(legacy, '"main": "@astrojs/cloudflare/entrypoints/server"');
|
|
502
|
+
await fs.writeFile(filePath, updated, 'utf8');
|
|
503
|
+
}
|
|
480
504
|
async addFrontendAllowedHosts(projectType, configPath) {
|
|
481
505
|
// Configure allowedHosts for frameworks using Vite dev server
|
|
482
506
|
// This allows tunneling services (ngrok, localtunnel) to proxy requests to the local dev environment
|
|
483
507
|
switch (projectType.slug) {
|
|
484
508
|
case 'astro': {
|
|
485
509
|
await this.addAllowedHostsToAstro(configPath);
|
|
510
|
+
await this.fixAstroWranglerEntrypoint(configPath);
|
|
486
511
|
break;
|
|
487
512
|
}
|
|
488
513
|
case 'angular': {
|
package/dist/lib/apps/index.js
CHANGED
|
@@ -368,7 +368,9 @@ export function batchAppFilesByType(configs) {
|
|
|
368
368
|
for (const configType of ConfigTypeBatchOrder) {
|
|
369
369
|
const configsByType = sorted.filter((config) => config.type === configType);
|
|
370
370
|
const label = configType === ConfigType.FILE ? 'App files' : titleize(configType);
|
|
371
|
-
const concurrency = configType === ConfigType.NOTIFICATION ||
|
|
371
|
+
const concurrency = configType === ConfigType.NOTIFICATION ||
|
|
372
|
+
configType === ConfigType.ASSET ||
|
|
373
|
+
configType === ConfigType.SETTING
|
|
372
374
|
? 1
|
|
373
375
|
: PUSH_CONCURRENCY;
|
|
374
376
|
batches.push({
|
package/dist/lib/theme-sync.js
CHANGED
|
@@ -58,18 +58,19 @@ export class ThemeSync {
|
|
|
58
58
|
proxyReq.setHeader('swell-request-id', `${Date.now()}`);
|
|
59
59
|
});
|
|
60
60
|
this.proxy.on('proxyRes', (proxyRes, req, res) => {
|
|
61
|
-
|
|
61
|
+
const chunks = [];
|
|
62
62
|
proxyRes.on('data', (chunk) => {
|
|
63
|
-
|
|
63
|
+
chunks.push(chunk);
|
|
64
64
|
});
|
|
65
65
|
// Preserve headers
|
|
66
66
|
for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {
|
|
67
67
|
const header = proxyRes.rawHeaders[i];
|
|
68
68
|
res.setHeader(header, proxyRes.rawHeaders[i + 1]);
|
|
69
69
|
}
|
|
70
|
-
|
|
71
|
-
res.setHeader('Content-Encoding', 'utf8');
|
|
70
|
+
res.removeHeader('Content-Encoding');
|
|
72
71
|
proxyRes.on('end', async () => {
|
|
72
|
+
const body = Buffer.concat(chunks);
|
|
73
|
+
chunks.length = 0;
|
|
73
74
|
const html = await this.parseRequestBody(proxyRes, body);
|
|
74
75
|
if (this.shouldInjectScript(proxyRes, html)) {
|
|
75
76
|
const modifiedHtml = this.injectScript(html, `localhost:${this.options.port}`);
|
|
@@ -11,6 +11,7 @@ interface WatchingChange {
|
|
|
11
11
|
}
|
|
12
12
|
export declare abstract class PushAppCommand extends RemoteAppCommand {
|
|
13
13
|
frontendPath: string;
|
|
14
|
+
hasLoggedLocalPreviewUrl: boolean;
|
|
14
15
|
logWatchChanges: boolean;
|
|
15
16
|
onWatchChange?: (appConfig?: AppConfig, action?: string, result?: any) => void;
|
|
16
17
|
watchingChangeQueue: Map<string, WatchingChange>;
|
|
@@ -78,6 +79,7 @@ export declare abstract class PushAppCommand extends RemoteAppCommand {
|
|
|
78
79
|
resolveAppPath(appId?: string, targetPath?: string): string;
|
|
79
80
|
runWatchChangeQueue(): Promise<void>;
|
|
80
81
|
saveCurrentStorefront(): void;
|
|
82
|
+
logLocalPreviewUrl(): void;
|
|
81
83
|
setWatchingFiles(): Promise<void>;
|
|
82
84
|
startProxyServer(port?: number): Promise<number>;
|
|
83
85
|
updateFrontendDeployment(currentStore: string, projectType: FrontendProjectType, deploymentUrl: string, deploymentHash: string): Promise<void>;
|
package/dist/push-app-command.js
CHANGED
|
@@ -20,6 +20,7 @@ const WATCH_WINDOW_MS = 100;
|
|
|
20
20
|
const DEV_SERVER_FALLBACK_PORT = 3000;
|
|
21
21
|
export class PushAppCommand extends RemoteAppCommand {
|
|
22
22
|
frontendPath = '';
|
|
23
|
+
hasLoggedLocalPreviewUrl = false;
|
|
23
24
|
logWatchChanges = true;
|
|
24
25
|
onWatchChange;
|
|
25
26
|
watchingChangeQueue = new Map();
|
|
@@ -778,6 +779,18 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
778
779
|
localConfig.setDefaultStorefront(this.appPath, this.storefront.id, this.api.envId);
|
|
779
780
|
}
|
|
780
781
|
}
|
|
782
|
+
logLocalPreviewUrl() {
|
|
783
|
+
if (this.hasLoggedLocalPreviewUrl) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
const currentStore = localConfig.getDefaultStore();
|
|
787
|
+
const sessionId = localConfig.getSessionId(currentStore);
|
|
788
|
+
const frontendAppRoute = this.app.type === 'storefront' && this.app.storefront?.external
|
|
789
|
+
? `/${this.app.private_id}`
|
|
790
|
+
: '';
|
|
791
|
+
this.log(`View it at ${style.link(`${this.localFrontendUrl(currentStore, sessionId)}${frontendAppRoute}`)}\n`);
|
|
792
|
+
this.hasLoggedLocalPreviewUrl = true;
|
|
793
|
+
}
|
|
781
794
|
async setWatchingFiles() {
|
|
782
795
|
this.watchingFiles = new Set(await globAllFilesByPath(this.appPath));
|
|
783
796
|
}
|
package/oclif.manifest.json
CHANGED