@swell/cli 2.0.19 → 2.1.0
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.d.ts +16 -2
- package/dist/commands/app/dev.js +359 -3
- package/dist/commands/app/frontend/dev.d.ts +7 -6
- package/dist/commands/app/frontend/dev.js +123 -60
- package/dist/commands/app/install.js +15 -4
- package/dist/create-app-command.d.ts +4 -0
- package/dist/create-app-command.js +161 -1
- package/dist/env/local.d.ts +1 -0
- package/dist/env/local.js +1 -0
- package/dist/env/production.d.ts +1 -0
- package/dist/env/production.js +1 -0
- package/dist/env/review.d.ts +1 -0
- package/dist/env/review.js +1 -0
- package/dist/env/staging.d.ts +1 -0
- package/dist/env/staging.js +1 -0
- package/dist/lib/apps/app-config.d.ts +49 -0
- package/dist/lib/apps/app-config.js +21 -14
- package/dist/lib/apps/index.d.ts +3 -4
- package/dist/lib/apps/index.js +102 -19
- package/dist/lib/bundle.js +2 -1
- package/dist/lib/constants.d.ts +1 -4
- package/dist/lib/constants.js +5 -4
- package/dist/lib/proxy.d.ts +1 -0
- package/dist/lib/proxy.js +29 -1
- package/dist/lib/swell-function-wrapper.d.ts +23 -4
- package/dist/lib/swell-function-wrapper.js +11 -4
- package/dist/push-app-command.d.ts +8 -4
- package/dist/push-app-command.js +100 -23
- package/dist/remote-app-command.js +24 -4
- package/oclif.manifest.json +52 -11
- package/package.json +3 -1
|
@@ -2,15 +2,29 @@ import { PushAppCommand } from '../../push-app-command.js';
|
|
|
2
2
|
export default class AppDev extends PushAppCommand {
|
|
3
3
|
static examples: string[];
|
|
4
4
|
static flags: {
|
|
5
|
-
'storefront-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
6
|
-
'storefront-select': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
7
5
|
'no-push': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
8
6
|
port: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
7
|
+
'frontend-port': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
'storefront-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
|
+
'storefront-select': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
9
10
|
};
|
|
10
11
|
static summary: string;
|
|
11
12
|
static delayOrientation: boolean;
|
|
12
13
|
static orientation: {
|
|
13
14
|
env: string;
|
|
14
15
|
};
|
|
16
|
+
tmpDir: string;
|
|
17
|
+
functionPorts: Map<string, number>;
|
|
18
|
+
functionErrors: Map<string, string>;
|
|
15
19
|
run(): Promise<void>;
|
|
20
|
+
private startAppFunctionServer;
|
|
21
|
+
runAppFrontendDevIfApplicable(frontendPort?: number): Promise<number | void>;
|
|
22
|
+
private getAppFunctions;
|
|
23
|
+
private logAllFunctions;
|
|
24
|
+
private logFunction;
|
|
25
|
+
private createTmpDirectory;
|
|
26
|
+
private startFunctionServers;
|
|
27
|
+
private generateWranglerConfig;
|
|
28
|
+
private createFunctionRouter;
|
|
29
|
+
private onChangeFunctionWatcher;
|
|
16
30
|
}
|
package/dist/commands/app/dev.js
CHANGED
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import getPort, { portNumbers } from 'get-port';
|
|
4
|
+
import * as fs from 'node:fs';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import * as os from 'node:os';
|
|
7
|
+
import * as http from 'node:http';
|
|
8
|
+
import { spawn } from 'node:child_process';
|
|
9
|
+
import { ConfigType, allConfigFilesInDir, appConfigFromFile, } from '../../lib/apps/index.js';
|
|
10
|
+
import { bundleFunction } from '../../lib/bundle.js';
|
|
11
|
+
import style from '../../lib/style.js';
|
|
2
12
|
import { PushAppCommand } from '../../push-app-command.js';
|
|
3
|
-
import AppFrontendDev from './frontend/dev.js';
|
|
4
13
|
export default class AppDev extends PushAppCommand {
|
|
5
14
|
static examples = [
|
|
6
15
|
'swell app dev',
|
|
7
16
|
'swell app dev --storefront-id <id>',
|
|
8
17
|
'swell app dev --port 3000',
|
|
18
|
+
'swell app dev --port 3000 --frontend-port 4000',
|
|
9
19
|
];
|
|
10
20
|
static flags = {
|
|
11
|
-
|
|
21
|
+
'no-push': Flags.boolean({
|
|
22
|
+
description: 'skip pushing app files initially',
|
|
23
|
+
}),
|
|
24
|
+
port: Flags.integer({
|
|
25
|
+
char: 'p',
|
|
26
|
+
description: 'override the default port to run the app locally',
|
|
27
|
+
}),
|
|
28
|
+
'frontend-port': Flags.integer({
|
|
29
|
+
description: 'specify the port for the frontend dev server when running with frontend',
|
|
30
|
+
}),
|
|
12
31
|
'storefront-id': Flags.string({
|
|
13
32
|
description: 'for storefront apps, identify a storefront to preview and push theme files to',
|
|
14
33
|
}),
|
|
@@ -22,7 +41,344 @@ export default class AppDev extends PushAppCommand {
|
|
|
22
41
|
static orientation = {
|
|
23
42
|
env: 'test',
|
|
24
43
|
};
|
|
44
|
+
// Directory for compiled function files and wrangler context
|
|
45
|
+
tmpDir = '';
|
|
46
|
+
// All available functions
|
|
47
|
+
functionPorts = new Map();
|
|
48
|
+
functionErrors = new Map();
|
|
25
49
|
async run() {
|
|
26
|
-
await this.
|
|
50
|
+
const { flags } = await this.parse(AppDev);
|
|
51
|
+
const { port, 'frontend-port': frontendPort } = flags;
|
|
52
|
+
const noPush = flags['no-push'];
|
|
53
|
+
if (!(await this.ensureAppExists(undefined, false))) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!noPush) {
|
|
57
|
+
await this.pushAppConfigs();
|
|
58
|
+
}
|
|
59
|
+
this.saveCurrentStorefront();
|
|
60
|
+
const spinner = ora();
|
|
61
|
+
spinner.start(`Starting app dev server...\n`);
|
|
62
|
+
const serverPort = await this.startProxyServer(port);
|
|
63
|
+
await this.startAppFunctionServer(spinner, serverPort);
|
|
64
|
+
await this.runAppFrontendDevIfApplicable(frontendPort);
|
|
65
|
+
}
|
|
66
|
+
async startAppFunctionServer(spinner, serverPort) {
|
|
67
|
+
// Get all functions in this app
|
|
68
|
+
const functions = await this.getAppFunctions();
|
|
69
|
+
if (functions.length === 0) {
|
|
70
|
+
spinner.stop();
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Create TMP directory for wrangler configs and bundled functions
|
|
74
|
+
await this.createTmpDirectory();
|
|
75
|
+
// Bundle functions and start wrangler dev servers for each
|
|
76
|
+
await this.startFunctionServers(functions);
|
|
77
|
+
// Start watching for function file changes
|
|
78
|
+
this.watchForChanges({
|
|
79
|
+
onChange: this.onChangeFunctionWatcher.bind(this),
|
|
80
|
+
});
|
|
81
|
+
// Create a routing server that proxies requests to function servers
|
|
82
|
+
await this.createFunctionRouter(serverPort);
|
|
83
|
+
spinner.succeed(`App function server running on port ${serverPort}\n`);
|
|
84
|
+
this.log(`${style.appConfigName(`Functions:`)}`);
|
|
85
|
+
await this.logAllFunctions(functions);
|
|
86
|
+
this.log(`${style.success('→')} Call functions at: http://localhost:${serverPort}/<function-name>\n`);
|
|
87
|
+
}
|
|
88
|
+
async runAppFrontendDevIfApplicable(frontendPort) {
|
|
89
|
+
const projectType = this.getFrontendProjectType(false);
|
|
90
|
+
if (projectType) {
|
|
91
|
+
// add app-dev flag to avoid watching for changes
|
|
92
|
+
this.argv.push('--app-dev');
|
|
93
|
+
// add proxy-port flag (use provided port or auto-detect)
|
|
94
|
+
const proxyPort = frontendPort || (await getPort({ port: portNumbers(4000, 4100) }));
|
|
95
|
+
this.argv.push('--proxy-port', String(proxyPort));
|
|
96
|
+
this.config.runCommand('app:frontend:dev', this.argv);
|
|
97
|
+
return proxyPort;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async getAppFunctions() {
|
|
101
|
+
const functions = [];
|
|
102
|
+
// Get all function files from the functions directory
|
|
103
|
+
try {
|
|
104
|
+
for (const { configFile } of allConfigFilesInDir(this.appPath, 'functions', ConfigType.FUNCTION)) {
|
|
105
|
+
const config = appConfigFromFile(configFile, ConfigType.FUNCTION, this.appPath);
|
|
106
|
+
if (!config.isRootFunction()) {
|
|
107
|
+
continue; // Skip if not a root function config
|
|
108
|
+
}
|
|
109
|
+
functions.push(config);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
// functions directory doesn't exist
|
|
114
|
+
}
|
|
115
|
+
return functions;
|
|
116
|
+
}
|
|
117
|
+
async logAllFunctions(functions) {
|
|
118
|
+
for (const func of functions) {
|
|
119
|
+
await this.logFunction(func);
|
|
120
|
+
}
|
|
121
|
+
this.log();
|
|
122
|
+
}
|
|
123
|
+
async logFunction(func) {
|
|
124
|
+
const functionName = path.parse(func.filePath).name;
|
|
125
|
+
this.log(` ${style.appConfigValue(functionName)}`);
|
|
126
|
+
if (this.functionErrors.has(functionName)) {
|
|
127
|
+
this.log(` ${style.error('Error starting function:')} ${this.functionErrors.get(functionName)}`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
// Bundle the function to get its config
|
|
132
|
+
const fullPath = path.join(this.appPath, func.filePath);
|
|
133
|
+
const { config } = await bundleFunction(fullPath);
|
|
134
|
+
if (config?.model?.events) {
|
|
135
|
+
const events = Array.isArray(config.model.events)
|
|
136
|
+
? config.model.events
|
|
137
|
+
: [config.model.events];
|
|
138
|
+
this.log(` Trigger: Model`);
|
|
139
|
+
this.log(` Events: ${style.dim(events.join(', '))}`);
|
|
140
|
+
}
|
|
141
|
+
else if (config?.cron?.schedule) {
|
|
142
|
+
this.log(` Trigger: Cron`);
|
|
143
|
+
this.log(` Schedule: ${style.dim(config.cron.schedule)}`);
|
|
144
|
+
}
|
|
145
|
+
else if (config?.route) {
|
|
146
|
+
this.log(` Trigger: Route`);
|
|
147
|
+
this.log(` Methods: ${style.dim(config.route.methods
|
|
148
|
+
.map((m) => String(m).toUpperCase())
|
|
149
|
+
.join(', '))}`);
|
|
150
|
+
if (config.route.headers) {
|
|
151
|
+
const headers = Object.entries(config.route.headers).map(([key, value]) => `${key}: ${value}`);
|
|
152
|
+
this.log(` Headers:`);
|
|
153
|
+
headers.forEach((header) => this.log(` ${style.dim(header)}`));
|
|
154
|
+
}
|
|
155
|
+
if (config.route.cache?.timeout) {
|
|
156
|
+
this.log(` Cache timeout: ${style.dim(config.route.cache.timeout)}`);
|
|
157
|
+
}
|
|
158
|
+
if (config.route.public) {
|
|
159
|
+
this.log(` Public: ${style.dim('true')}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (config?.description) {
|
|
163
|
+
this.log(` Description: ${style.dim(config.description)}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
this.log(` ${style.error('Error loading config:', error.message)}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async createTmpDirectory() {
|
|
171
|
+
const tmpBase = path.join(os.tmpdir(), 'swell-cli');
|
|
172
|
+
const appTmpDir = path.join(tmpBase, this.app.id || 'unknown-app', 'functions');
|
|
173
|
+
// Create the directory structure
|
|
174
|
+
await fs.promises.mkdir(appTmpDir, { recursive: true });
|
|
175
|
+
this.tmpDir = appTmpDir;
|
|
176
|
+
}
|
|
177
|
+
async startFunctionServers(functions) {
|
|
178
|
+
const functionStatus = new Map();
|
|
179
|
+
for (const func of functions) {
|
|
180
|
+
const functionName = func.name;
|
|
181
|
+
if (this.functionPorts.has(functionName)) {
|
|
182
|
+
// Function server already running
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const functionPort = await getPort({ port: portNumbers(9000, 9100) });
|
|
186
|
+
try {
|
|
187
|
+
// Bundle the function
|
|
188
|
+
const fullPath = path.join(this.appPath, func.filePath);
|
|
189
|
+
const { code } = await bundleFunction(fullPath);
|
|
190
|
+
// Write bundled function to tmp directory
|
|
191
|
+
const bundledPath = path.join(this.tmpDir, `${functionName}.js`);
|
|
192
|
+
await fs.promises.writeFile(bundledPath, code);
|
|
193
|
+
// Generate wrangler config
|
|
194
|
+
const wranglerConfig = this.generateWranglerConfig(functionName, bundledPath);
|
|
195
|
+
const configPath = path.join(this.tmpDir, `${functionName}.toml`);
|
|
196
|
+
await fs.promises.writeFile(configPath, wranglerConfig);
|
|
197
|
+
functionStatus.set(functionName, 'starting');
|
|
198
|
+
// Start wrangler process in background
|
|
199
|
+
const wranglerProcess = spawn('npx', [
|
|
200
|
+
'wrangler',
|
|
201
|
+
'dev',
|
|
202
|
+
`--config=${configPath}`,
|
|
203
|
+
`--port=${functionPort}`,
|
|
204
|
+
], {
|
|
205
|
+
cwd: this.tmpDir,
|
|
206
|
+
//stdio: 'pipe', // Capture output for debugging
|
|
207
|
+
detached: false,
|
|
208
|
+
});
|
|
209
|
+
const handleFunctionOutput = (data) => {
|
|
210
|
+
const output = data.toString();
|
|
211
|
+
// Remove ANSI escape sequences that cause line clearing
|
|
212
|
+
const cleanOutput = output.replace('\u001b[2K\u001b[1A\u001b[2K\u001b[G', '');
|
|
213
|
+
const lines = cleanOutput
|
|
214
|
+
.split('\n')
|
|
215
|
+
.filter((line) => line.trim());
|
|
216
|
+
for (const line of lines) {
|
|
217
|
+
// Capture running status
|
|
218
|
+
if (line.includes('Ready on http://localhost:')) {
|
|
219
|
+
functionStatus.set(functionName, 'running');
|
|
220
|
+
this.functionErrors.delete(functionName);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// Catch startup error
|
|
224
|
+
if (functionStatus.get(functionName) === 'starting' &&
|
|
225
|
+
line.includes('✘ [ERROR]')) {
|
|
226
|
+
functionStatus.set(functionName, 'error');
|
|
227
|
+
this.functionErrors.set(functionName, line);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (functionStatus.get(functionName) !== 'running') {
|
|
231
|
+
// No output until function is running
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
// Hide specific wrangler startup/info messages
|
|
235
|
+
if (line.includes('⎔ Starting local server') ||
|
|
236
|
+
line.includes('⎔ Reloading local server') ||
|
|
237
|
+
line.includes('Starting local server') ||
|
|
238
|
+
line.includes('⛅️ wrangler') ||
|
|
239
|
+
line.includes('-----') ||
|
|
240
|
+
line.includes('▲ [WARNING]') ||
|
|
241
|
+
line.includes('The version of Wrangler') ||
|
|
242
|
+
line.includes('Please update to the latest') ||
|
|
243
|
+
line.includes('Run `npm install') ||
|
|
244
|
+
line.includes('After installation') ||
|
|
245
|
+
line.includes('Your worker has access') ||
|
|
246
|
+
line.includes('- Vars:') ||
|
|
247
|
+
line.includes('- Bindings:')) {
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
// Skip wrangler info lines
|
|
251
|
+
if (line.includes('[wrangler:')) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (line) {
|
|
255
|
+
console.log(`${style.appConfigValue(`→ ${functionName}`)} ${this.timestampStyled()} ${line}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
wranglerProcess.stdout?.on('data', handleFunctionOutput);
|
|
260
|
+
wranglerProcess.stderr?.on('data', handleFunctionOutput);
|
|
261
|
+
wranglerProcess.on('error', (error) => {
|
|
262
|
+
functionStatus.set(functionName, 'error');
|
|
263
|
+
this.functionErrors.set(functionName, error.message);
|
|
264
|
+
});
|
|
265
|
+
// Store the port for routing
|
|
266
|
+
this.functionPorts.set(functionName, functionPort);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
functionStatus.set(functionName, 'error');
|
|
270
|
+
this.functionErrors.set(functionName, error.message);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
await new Promise((resolve) => {
|
|
274
|
+
if (!functions.length) {
|
|
275
|
+
resolve();
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const checkAllRunning = () => {
|
|
279
|
+
const allRunning = Array.from(functionStatus.values()).every((status) => status !== 'starting');
|
|
280
|
+
if (allRunning) {
|
|
281
|
+
resolve();
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
setTimeout(checkAllRunning, 250);
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
checkAllRunning();
|
|
288
|
+
});
|
|
289
|
+
return { functionStatus };
|
|
290
|
+
}
|
|
291
|
+
generateWranglerConfig(functionName, bundledPath) {
|
|
292
|
+
return `
|
|
293
|
+
name = "${functionName}"
|
|
294
|
+
main = "${bundledPath}"
|
|
295
|
+
compatibility_date = "2023-05-18"
|
|
296
|
+
|
|
297
|
+
[vars]
|
|
298
|
+
ENVIRONMENT = "development"
|
|
299
|
+
`.trim();
|
|
300
|
+
}
|
|
301
|
+
async createFunctionRouter(serverPort) {
|
|
302
|
+
const server = http.createServer((req, res) => {
|
|
303
|
+
const url = new URL(req.url, `http://localhost:${serverPort}`);
|
|
304
|
+
const functionName = url.pathname.slice(1); // Remove leading slash
|
|
305
|
+
if (this.functionPorts.has(functionName)) {
|
|
306
|
+
const targetPort = this.functionPorts.get(functionName);
|
|
307
|
+
// Proxy the request to the function server
|
|
308
|
+
const proxyReq = http.request({
|
|
309
|
+
hostname: 'localhost',
|
|
310
|
+
port: targetPort,
|
|
311
|
+
path: req.url,
|
|
312
|
+
method: req.method,
|
|
313
|
+
headers: {
|
|
314
|
+
...req.headers,
|
|
315
|
+
'Swell-Local-Dev': 'true',
|
|
316
|
+
},
|
|
317
|
+
}, (proxyRes) => {
|
|
318
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
319
|
+
proxyRes.pipe(res);
|
|
320
|
+
});
|
|
321
|
+
proxyReq.on('error', (error) => {
|
|
322
|
+
res.writeHead(500);
|
|
323
|
+
res.end(`Error proxying to function ${functionName}: ${error.message}`);
|
|
324
|
+
});
|
|
325
|
+
// Log when response is finished, method, status, and time to execute by proxy
|
|
326
|
+
const startTime = Date.now();
|
|
327
|
+
res.on('finish', async () => {
|
|
328
|
+
const duration = Date.now() - startTime;
|
|
329
|
+
// Log after a short delay to ensure output order
|
|
330
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
331
|
+
this.log(`\n${style.appConfigValue(`→ ${functionName}`)} ${this.timestampStyled()} [${res.statusCode}] ${req.method} ${req.url} (${duration}ms)\n`);
|
|
332
|
+
});
|
|
333
|
+
req.pipe(proxyReq);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
res.writeHead(404);
|
|
337
|
+
const functionError = this.functionErrors.get(functionName);
|
|
338
|
+
const functionsAvailable = Array.from(this.functionPorts.keys());
|
|
339
|
+
res.end(functionError
|
|
340
|
+
? `Function Error: ${functionError}`
|
|
341
|
+
: `Function '${functionName}' not found. ${functionsAvailable.length > 0
|
|
342
|
+
? `Available functions: ${Array.from(this.functionPorts.keys()).join(', ')}`
|
|
343
|
+
: ''}`);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
server.listen(serverPort);
|
|
347
|
+
}
|
|
348
|
+
async onChangeFunctionWatcher(appConfig, action, result) {
|
|
349
|
+
// If no result, skip
|
|
350
|
+
if (!result) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
// If deleted result, skip and remove port
|
|
354
|
+
if (action === 'remove' || !result) {
|
|
355
|
+
this.functionPorts.delete(appConfig?.name);
|
|
356
|
+
this.log();
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
// Skip everything except functions
|
|
360
|
+
if (appConfig?.type !== ConfigType.FUNCTION) {
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
const fullPath = path.join(this.appPath, appConfig.filePath);
|
|
365
|
+
// Re-bundle the function
|
|
366
|
+
const { code } = await bundleFunction(fullPath);
|
|
367
|
+
// Update the bundled file (wrangler dev will auto-reload)
|
|
368
|
+
const bundledPath = path.join(this.tmpDir, `${appConfig.name}.js`);
|
|
369
|
+
await fs.promises.writeFile(bundledPath, code);
|
|
370
|
+
if (!this.functionPorts.has(appConfig.name)) {
|
|
371
|
+
this.log(`\nStarting function ${appConfig.name}...`);
|
|
372
|
+
await this.startFunctionServers([appConfig]);
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
this.log(`\nUpdating function ${appConfig.name}...`);
|
|
376
|
+
}
|
|
377
|
+
await this.logFunction(appConfig);
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
this.log(`${style.error('Error re-bundling function')} ${appConfig.name}: ${error.message}`);
|
|
381
|
+
}
|
|
382
|
+
this.log();
|
|
27
383
|
}
|
|
28
384
|
}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import { FrontendProjectType } from '../../../lib/apps/index.js';
|
|
2
1
|
import { PushAppCommand } from '../../../push-app-command.js';
|
|
3
2
|
export default class AppFrontendDev extends PushAppCommand {
|
|
4
3
|
static examples: string[];
|
|
5
4
|
static flags: {
|
|
6
|
-
'
|
|
7
|
-
port: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
5
|
+
'proxy-port': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
6
|
'storefront-id': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
7
|
'storefront-select': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
8
|
+
'app-dev': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
9
|
+
'no-push': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
|
+
port: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
|
+
'frontend-port': import("@oclif/core/lib/interfaces/parser.js").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
10
12
|
};
|
|
11
13
|
static orientation: {
|
|
12
14
|
env: string;
|
|
13
15
|
};
|
|
14
16
|
static summary: string;
|
|
15
|
-
logAppLocalUrl(storeId: string, sessionId: string): void;
|
|
16
17
|
run(): Promise<void>;
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
private execFrontendProject;
|
|
19
|
+
logAppLocalUrl(storeId: string, sessionId: string): void;
|
|
19
20
|
}
|