1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const cli_framework_1 = require("@ionic/cli-framework");
|
4 | const string_1 = require("@ionic/cli-framework/utils/string");
|
5 | const utils_fs_1 = require("@ionic/utils-fs");
|
6 | const utils_network_1 = require("@ionic/utils-network");
|
7 | const utils_process_1 = require("@ionic/utils-process");
|
8 | const chalk_1 = require("chalk");
|
9 | const Debug = require("debug");
|
10 | const events_1 = require("events");
|
11 | const lodash = require("lodash");
|
12 | const os = require("os");
|
13 | const path = require("path");
|
14 | const split2 = require("split2");
|
15 | const through2 = require("through2");
|
16 | const constants_1 = require("../constants");
|
17 | const guards_1 = require("../guards");
|
18 | const color_1 = require("./color");
|
19 | const errors_1 = require("./errors");
|
20 | const events_2 = require("./events");
|
21 | const hooks_1 = require("./hooks");
|
22 | const open_1 = require("./open");
|
23 | const logger_1 = require("./utils/logger");
|
24 | const debug = Debug('ionic:lib:serve');
|
25 | exports.DEFAULT_DEV_LOGGER_PORT = 53703;
|
26 | exports.DEFAULT_LIVERELOAD_PORT = 35729;
|
27 | exports.DEFAULT_SERVER_PORT = 8100;
|
28 | exports.DEFAULT_LAB_PORT = 8200;
|
29 | exports.DEFAULT_DEVAPP_COMM_PORT = 53233;
|
30 | exports.DEFAULT_ADDRESS = 'localhost';
|
31 | exports.BIND_ALL_ADDRESS = '0.0.0.0';
|
32 | exports.LOCAL_ADDRESSES = ['localhost', '127.0.0.1'];
|
33 | exports.BROWSERS = ['safari', 'firefox', process.platform === 'win32' ? 'chrome' : (process.platform === 'darwin' ? 'google chrome' : 'google-chrome')];
|
34 |
|
35 | exports.SERVE_SCRIPT = 'ionic:serve';
|
36 | exports.COMMON_SERVE_COMMAND_OPTIONS = [
|
37 | {
|
38 | name: 'external',
|
39 | summary: `Host dev server on all network interfaces (i.e. ${color_1.input('--address=0.0.0.0')})`,
|
40 | type: Boolean,
|
41 | },
|
42 | {
|
43 | name: 'address',
|
44 | summary: 'Use specific address for the dev server',
|
45 | default: exports.DEFAULT_ADDRESS,
|
46 | groups: ["advanced" ],
|
47 | },
|
48 | {
|
49 | name: 'port',
|
50 | summary: 'Use specific port for HTTP',
|
51 | default: exports.DEFAULT_SERVER_PORT.toString(),
|
52 | aliases: ['p'],
|
53 | groups: ["advanced" ],
|
54 | },
|
55 | {
|
56 | name: 'livereload',
|
57 | summary: 'Do not spin up dev server--just serve files',
|
58 | type: Boolean,
|
59 | default: true,
|
60 | },
|
61 | {
|
62 | name: 'engine',
|
63 | summary: `Target engine (e.g. ${['browser', 'cordova'].map(e => color_1.input(e)).join(', ')})`,
|
64 | groups: ["hidden" , "advanced" ],
|
65 | },
|
66 | {
|
67 | name: 'platform',
|
68 | summary: `Target platform on chosen engine (e.g. ${['ios', 'android'].map(e => color_1.input(e)).join(', ')})`,
|
69 | groups: ["hidden" , "advanced" ],
|
70 | },
|
71 | {
|
72 | name: 'devapp',
|
73 | summary: 'Publish DevApp service',
|
74 | type: Boolean,
|
75 | default: false,
|
76 | groups: ["advanced" ],
|
77 | },
|
78 | ];
|
79 | class ServeRunner {
|
80 | constructor() {
|
81 | this.devAppConnectionMade = false;
|
82 | }
|
83 | getPkgManagerServeCLI() {
|
84 | return this.e.config.get('npmClient') === 'npm' ? new NpmServeCLI(this.e) : new YarnServeCLI(this.e);
|
85 | }
|
86 | createOptionsFromCommandLine(inputs, options) {
|
87 | const separatedArgs = options['--'];
|
88 | if (options['external'] || (options['devapp'] && options['address'] === exports.DEFAULT_ADDRESS)) {
|
89 | options['address'] = '0.0.0.0';
|
90 | }
|
91 | const engine = this.determineEngineFromCommandLine(options);
|
92 | const address = options['address'] ? String(options['address']) : exports.DEFAULT_ADDRESS;
|
93 | const labPort = string_1.str2num(options['lab-port'], exports.DEFAULT_LAB_PORT);
|
94 | const port = string_1.str2num(options['port'], exports.DEFAULT_SERVER_PORT);
|
95 | return {
|
96 | '--': separatedArgs ? separatedArgs : [],
|
97 | address,
|
98 | browser: options['browser'] ? String(options['browser']) : undefined,
|
99 | browserOption: options['browseroption'] ? String(options['browseroption']) : undefined,
|
100 | devapp: !!options['devapp'],
|
101 | engine,
|
102 | externalAddressRequired: !!options['externalAddressRequired'],
|
103 | lab: !!options['lab'],
|
104 | labHost: options['lab-host'] ? String(options['lab-host']) : 'localhost',
|
105 | labPort,
|
106 | livereload: typeof options['livereload'] === 'boolean' ? Boolean(options['livereload']) : true,
|
107 | open: !!options['open'],
|
108 | platform: options['platform'] ? String(options['platform']) : undefined,
|
109 | port,
|
110 | proxy: typeof options['proxy'] === 'boolean' ? Boolean(options['proxy']) : true,
|
111 | project: options['project'] ? String(options['project']) : undefined,
|
112 | verbose: !!options['verbose'],
|
113 | };
|
114 | }
|
115 | determineEngineFromCommandLine(options) {
|
116 | if (options['engine']) {
|
117 | return String(options['engine']);
|
118 | }
|
119 | if (options['cordova']) {
|
120 | return 'cordova';
|
121 | }
|
122 | return 'browser';
|
123 | }
|
124 | async displayDevAppMessage(options) {
|
125 | const pkg = await this.e.project.requirePackageJson();
|
126 |
|
127 |
|
128 | if (options.devapp && guards_1.isCordovaPackageJson(pkg)) {
|
129 | const plugins = await this.getSupportedDevAppPlugins();
|
130 | const packageCordovaPlugins = Object.keys(pkg.cordova.plugins);
|
131 | const packageCordovaPluginsDiff = packageCordovaPlugins.filter(p => !plugins.has(p));
|
132 | if (packageCordovaPluginsDiff.length > 0) {
|
133 | this.e.log.warn('Detected unsupported Cordova plugins with Ionic DevApp:\n' +
|
134 | `${packageCordovaPluginsDiff.map(p => `- ${color_1.strong(p)}`).join('\n')}\n\n` +
|
135 | `App may not function as expected in Ionic DevApp.`);
|
136 | this.e.log.nl();
|
137 | }
|
138 | }
|
139 | }
|
140 | async beforeServe(options) {
|
141 | const hook = new ServeBeforeHook(this.e);
|
142 | try {
|
143 | await hook.run({ name: hook.name, serve: options });
|
144 | }
|
145 | catch (e) {
|
146 | if (e instanceof cli_framework_1.BaseError) {
|
147 | throw new errors_1.FatalException(e.message);
|
148 | }
|
149 | throw e;
|
150 | }
|
151 | }
|
152 | async run(options) {
|
153 | debug('serve options: %O', options);
|
154 | await this.beforeServe(options);
|
155 | const details = await this.serveProject(options);
|
156 | const devAppDetails = await this.gatherDevAppDetails(options, details);
|
157 | const labDetails = options.lab ? await this.runLab(options, details) : undefined;
|
158 | if (devAppDetails) {
|
159 | const devAppName = await this.publishDevApp(options, devAppDetails);
|
160 | devAppDetails.channel = devAppName;
|
161 | }
|
162 | const localAddress = `${details.protocol}://localhost:${details.port}`;
|
163 | const fmtExternalAddress = (address) => `${details.protocol}://${address}:${details.port}`;
|
164 | const labAddress = labDetails ? `http://${labDetails.address}:${labDetails.port}` : undefined;
|
165 | this.e.log.nl();
|
166 | this.e.log.info(`Development server running!` +
|
167 | (labAddress ? `\nLab: ${color_1.strong(labAddress)}` : '') +
|
168 | `\nLocal: ${color_1.strong(localAddress)}` +
|
169 | (details.externalNetworkInterfaces.length > 0 ? `\nExternal: ${details.externalNetworkInterfaces.map(v => color_1.strong(fmtExternalAddress(v.address))).join(', ')}` : '') +
|
170 | (devAppDetails && devAppDetails.channel ? `\nDevApp: ${color_1.strong(devAppDetails.channel)} on ${color_1.strong(os.hostname())}` : '') +
|
171 | `\n\n${chalk_1.default.yellow('Use Ctrl+C to quit this process')}`);
|
172 | this.e.log.nl();
|
173 | if (options.open) {
|
174 | const openAddress = labAddress ? labAddress : localAddress;
|
175 | const openURL = this.modifyOpenURL(openAddress, options);
|
176 | await open_1.open(openURL, { app: options.browser });
|
177 | this.e.log.info(`Browser window opened to ${color_1.strong(openURL)}!`);
|
178 | this.e.log.nl();
|
179 | }
|
180 | events_2.emit('serve:ready', details);
|
181 | debug('serve details: %O', details);
|
182 | this.scheduleAfterServe(options, details);
|
183 | return details;
|
184 | }
|
185 | async afterServe(options, details) {
|
186 | const hook = new ServeAfterHook(this.e);
|
187 | try {
|
188 | await hook.run({ name: hook.name, serve: lodash.assign({}, options, details) });
|
189 | }
|
190 | catch (e) {
|
191 | if (e instanceof cli_framework_1.BaseError) {
|
192 | throw new errors_1.FatalException(e.message);
|
193 | }
|
194 | throw e;
|
195 | }
|
196 | }
|
197 | scheduleAfterServe(options, details) {
|
198 | utils_process_1.onBeforeExit(async () => this.afterServe(options, details));
|
199 | }
|
200 | getUsedPorts(options, details) {
|
201 | return [details.port];
|
202 | }
|
203 | async gatherDevAppDetails(options, details) {
|
204 | if (options.devapp) {
|
205 | const { computeBroadcastAddress } = await Promise.resolve().then(() => require('./devapp'));
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | const interfaces = utils_network_1.getExternalIPv4Interfaces()
|
211 | .map(i => ({ ...i, broadcast: computeBroadcastAddress(i.address, i.netmask) }));
|
212 | const { port } = details;
|
213 |
|
214 | const commPort = await utils_network_1.findClosestOpenPort(exports.DEFAULT_DEVAPP_COMM_PORT);
|
215 | return { port, commPort, interfaces };
|
216 | }
|
217 | }
|
218 | async publishDevApp(options, details) {
|
219 | if (options.devapp) {
|
220 | const { createCommServer, createPublisher } = await Promise.resolve().then(() => require('./devapp'));
|
221 | const publisher = await createPublisher(this.e.project.config.get('name'), details.port, details.commPort);
|
222 | const comm = await createCommServer(publisher.id, details.commPort);
|
223 | publisher.interfaces = details.interfaces;
|
224 | comm.on('error', (err) => {
|
225 | debug(`Error in DevApp service: ${String(err.stack ? err.stack : err)}`);
|
226 | });
|
227 | comm.on('connect', async (data) => {
|
228 | this.e.log.info(`DevApp connection established from ${color_1.strong(data.device)}`);
|
229 | this.e.log.nl();
|
230 | if (!this.devAppConnectionMade) {
|
231 | this.devAppConnectionMade = true;
|
232 | await this.displayDevAppMessage(options);
|
233 | }
|
234 | });
|
235 | publisher.on('error', (err) => {
|
236 | debug(`Error in DevApp service: ${String(err.stack ? err.stack : err)}`);
|
237 | });
|
238 | try {
|
239 | await comm.start();
|
240 | }
|
241 | catch (e) {
|
242 | this.e.log.error(`Could not create DevApp comm server: ${String(e.stack ? e.stack : e)}`);
|
243 | }
|
244 | try {
|
245 | await publisher.start();
|
246 | }
|
247 | catch (e) {
|
248 | this.e.log.error(`Could not publish DevApp service: ${String(e.stack ? e.stack : e)}`);
|
249 | }
|
250 | return publisher.name;
|
251 | }
|
252 | }
|
253 | async getSupportedDevAppPlugins() {
|
254 | const p = path.resolve(constants_1.ASSETS_DIRECTORY, 'devapp', 'plugins.json');
|
255 | const plugins = await utils_fs_1.readJson(p);
|
256 | if (!Array.isArray(plugins)) {
|
257 | throw new Error(`Cannot read ${p} file of supported plugins.`);
|
258 | }
|
259 |
|
260 |
|
261 | plugins.push('cordova-plugin-splashscreen');
|
262 | return new Set(plugins);
|
263 | }
|
264 | async runLab(options, serveDetails) {
|
265 | const labDetails = {
|
266 | projectType: this.e.project.type,
|
267 | address: options.labHost,
|
268 | port: await utils_network_1.findClosestOpenPort(options.labPort),
|
269 | };
|
270 | const lab = new IonicLabServeCLI(this.e);
|
271 | await lab.serve({ serveDetails, ...labDetails });
|
272 | return labDetails;
|
273 | }
|
274 | async selectExternalIP(options) {
|
275 | let availableInterfaces = [];
|
276 | let chosenIP = options.address;
|
277 | if (options.address === exports.BIND_ALL_ADDRESS) {
|
278 |
|
279 | availableInterfaces = utils_network_1.getExternalIPv4Interfaces().filter(i => !i.address.startsWith('169.254'));
|
280 | if (availableInterfaces.length === 0) {
|
281 | if (options.externalAddressRequired) {
|
282 | throw new errors_1.FatalException(`No external network interfaces detected. In order to use the dev server externally you will need one.\n` +
|
283 | `Are you connected to a local network?\n`);
|
284 | }
|
285 | }
|
286 | else if (availableInterfaces.length === 1) {
|
287 | chosenIP = availableInterfaces[0].address;
|
288 | }
|
289 | else if (availableInterfaces.length > 1) {
|
290 | if (options.externalAddressRequired) {
|
291 | if (this.e.flags.interactive) {
|
292 | this.e.log.warn('Multiple network interfaces detected!\n' +
|
293 | 'You will be prompted to select an external-facing IP for the dev server that your device or emulator has access to.\n\n' +
|
294 | `You may also use the ${color_1.input('--address')} option to skip this prompt.`);
|
295 | const promptedIp = await this.e.prompt({
|
296 | type: 'list',
|
297 | name: 'promptedIp',
|
298 | message: 'Please select which IP to use:',
|
299 | choices: availableInterfaces.map(i => ({
|
300 | name: `${i.address} ${color_1.weak(`(${i.device})`)}`,
|
301 | value: i.address,
|
302 | })),
|
303 | });
|
304 | chosenIP = promptedIp;
|
305 | }
|
306 | else {
|
307 | throw new errors_1.FatalException(`Multiple network interfaces detected!\n` +
|
308 | `You must select an external-facing IP for the dev server that your device or emulator has access to with the ${color_1.input('--address')} option.`);
|
309 | }
|
310 | }
|
311 | }
|
312 | }
|
313 | else if (options.externalAddressRequired && exports.LOCAL_ADDRESSES.includes(options.address)) {
|
314 | this.e.log.warn('An external host may be required to serve for this target device/platform.\n' +
|
315 | 'If you get connection issues on your device or emulator, try connecting the device to the same Wi-Fi network and selecting an accessible IP address for your computer on that network.\n\n' +
|
316 | `You can use ${color_1.input('--external')} to run the dev server on all network interfaces, in which case an external address will be selected.\n`);
|
317 | }
|
318 | return [chosenIP, availableInterfaces];
|
319 | }
|
320 | }
|
321 | exports.ServeRunner = ServeRunner;
|
322 | class ServeBeforeHook extends hooks_1.Hook {
|
323 | constructor() {
|
324 | super(...arguments);
|
325 | this.name = 'serve:before';
|
326 | }
|
327 | }
|
328 | class ServeAfterHook extends hooks_1.Hook {
|
329 | constructor() {
|
330 | super(...arguments);
|
331 | this.name = 'serve:after';
|
332 | }
|
333 | }
|
334 | class ServeCLI extends events_1.EventEmitter {
|
335 | constructor(e) {
|
336 | super();
|
337 | this.e = e;
|
338 | |
339 |
|
340 |
|
341 | this.global = false;
|
342 | }
|
343 | get resolvedProgram() {
|
344 | if (this._resolvedProgram) {
|
345 | return this._resolvedProgram;
|
346 | }
|
347 | return this.program;
|
348 | }
|
349 | |
350 |
|
351 |
|
352 | async buildEnvVars(options) {
|
353 | return process.env;
|
354 | }
|
355 | |
356 |
|
357 |
|
358 |
|
359 |
|
360 |
|
361 |
|
362 |
|
363 |
|
364 |
|
365 | stdoutFilter(line) {
|
366 | this.emit('ready');
|
367 | return true;
|
368 | }
|
369 | |
370 |
|
371 |
|
372 |
|
373 |
|
374 | stderrFilter(line) {
|
375 | return true;
|
376 | }
|
377 | async resolveScript() {
|
378 | if (typeof this.script === 'undefined') {
|
379 | return;
|
380 | }
|
381 | const pkg = await this.e.project.requirePackageJson();
|
382 | return pkg.scripts && pkg.scripts[this.script];
|
383 | }
|
384 | async serve(options) {
|
385 | this._resolvedProgram = await this.resolveProgram();
|
386 | await this.spawnWrapper(options);
|
387 | const interval = setInterval(() => {
|
388 | this.e.log.info(`Waiting for connectivity with ${color_1.input(this.resolvedProgram)}...`);
|
389 | }, 5000);
|
390 | debug('awaiting TCP connection to %s:%d', options.address, options.port);
|
391 | await utils_network_1.isHostConnectable(options.address, options.port);
|
392 | clearInterval(interval);
|
393 | }
|
394 | async spawnWrapper(options) {
|
395 | try {
|
396 | return await this.spawn(options);
|
397 | }
|
398 | catch (e) {
|
399 | if (!(e instanceof errors_1.ServeCLIProgramNotFoundException)) {
|
400 | throw e;
|
401 | }
|
402 | if (this.global) {
|
403 | this.e.log.nl();
|
404 | throw new errors_1.FatalException(`${color_1.input(this.pkg)} is required for this command to work properly.`);
|
405 | }
|
406 | this.e.log.nl();
|
407 | this.e.log.info(`Looks like ${color_1.input(this.pkg)} isn't installed in this project.\n` +
|
408 | `This package is required for this command to work properly. The package provides a CLI utility, but the ${color_1.input(this.resolvedProgram)} binary was not found in your PATH.`);
|
409 | const installed = await this.promptToInstall();
|
410 | if (!installed) {
|
411 | this.e.log.nl();
|
412 | throw new errors_1.FatalException(`${color_1.input(this.pkg)} is required for this command to work properly.`);
|
413 | }
|
414 | return this.spawn(options);
|
415 | }
|
416 | }
|
417 | async spawn(options) {
|
418 | const args = await this.buildArgs(options);
|
419 | const envVars = await this.buildEnvVars(options);
|
420 | const p = await this.e.shell.spawn(this.resolvedProgram, args, { stdio: 'pipe', cwd: this.e.project.directory, env: utils_process_1.createProcessEnv(envVars) });
|
421 | return new Promise((resolve, reject) => {
|
422 | const errorHandler = (err) => {
|
423 | debug('received error for %s: %o', this.resolvedProgram, err);
|
424 | if (this.resolvedProgram === this.program && err.code === 'ENOENT') {
|
425 | p.removeListener('close', closeHandler);
|
426 | reject(new errors_1.ServeCLIProgramNotFoundException(`${color_1.strong(this.resolvedProgram)} command not found.`));
|
427 | }
|
428 | else {
|
429 | reject(err);
|
430 | }
|
431 | };
|
432 | const closeHandler = (code) => {
|
433 | if (code !== null) {
|
434 | debug('received unexpected close for %s (code: %d)', this.resolvedProgram, code);
|
435 | this.e.log.nl();
|
436 | this.e.log.error(`${color_1.input(this.resolvedProgram)} has unexpectedly closed (exit code ${code}).\n` +
|
437 | 'The Ionic CLI will exit. Please check any output above for error details.');
|
438 | utils_process_1.processExit(1);
|
439 | }
|
440 | };
|
441 | p.on('error', errorHandler);
|
442 | p.on('close', closeHandler);
|
443 | utils_process_1.onBeforeExit(async () => {
|
444 | p.removeListener('close', closeHandler);
|
445 | if (p.pid) {
|
446 | await utils_process_1.killProcessTree(p.pid);
|
447 | }
|
448 | });
|
449 | const ws = this.createLoggerStream();
|
450 | p.stdout.pipe(split2()).pipe(this.createStreamFilter(line => this.stdoutFilter(line))).pipe(ws);
|
451 | p.stderr.pipe(split2()).pipe(this.createStreamFilter(line => this.stderrFilter(line))).pipe(ws);
|
452 | this.once('ready', () => {
|
453 | resolve();
|
454 | });
|
455 | });
|
456 | }
|
457 | createLoggerStream() {
|
458 | const log = this.e.log.clone();
|
459 | log.handlers = logger_1.createDefaultLoggerHandlers(cli_framework_1.createPrefixedFormatter(color_1.weak(`[${this.resolvedProgram === this.program ? this.prefix : this.resolvedProgram}]`)));
|
460 | return log.createWriteStream(cli_framework_1.LOGGER_LEVELS.INFO);
|
461 | }
|
462 | async resolveProgram() {
|
463 | if (typeof this.script !== 'undefined') {
|
464 | debug(`Looking for ${color_1.ancillary(this.script)} npm script.`);
|
465 | if (await this.resolveScript()) {
|
466 | debug(`Using ${color_1.ancillary(this.script)} npm script.`);
|
467 | return this.e.config.get('npmClient');
|
468 | }
|
469 | }
|
470 | return this.program;
|
471 | }
|
472 | createStreamFilter(filter) {
|
473 | return through2(function (chunk, enc, callback) {
|
474 | const str = chunk.toString();
|
475 | if (filter(str)) {
|
476 | this.push(chunk);
|
477 | }
|
478 | callback();
|
479 | });
|
480 | }
|
481 | async promptToInstall() {
|
482 | const { pkgManagerArgs } = await Promise.resolve().then(() => require('./utils/npm'));
|
483 | const [manager, ...managerArgs] = await pkgManagerArgs(this.e.config.get('npmClient'), { command: 'install', pkg: this.pkg, saveDev: true, saveExact: true });
|
484 | this.e.log.nl();
|
485 | const confirm = await this.e.prompt({
|
486 | name: 'confirm',
|
487 | message: `Install ${color_1.input(this.pkg)}?`,
|
488 | type: 'confirm',
|
489 | });
|
490 | if (!confirm) {
|
491 | this.e.log.warn(`Not installing--here's how to install manually: ${color_1.input(`${manager} ${managerArgs.join(' ')}`)}`);
|
492 | return false;
|
493 | }
|
494 | await this.e.shell.run(manager, managerArgs, { cwd: this.e.project.directory });
|
495 | return true;
|
496 | }
|
497 | }
|
498 | exports.ServeCLI = ServeCLI;
|
499 | class PkgManagerServeCLI extends ServeCLI {
|
500 | constructor() {
|
501 | super(...arguments);
|
502 | this.global = true;
|
503 | this.script = exports.SERVE_SCRIPT;
|
504 | }
|
505 | async resolveProgram() {
|
506 | return this.program;
|
507 | }
|
508 | async buildArgs(options) {
|
509 | const { pkgManagerArgs } = await Promise.resolve().then(() => require('./utils/npm'));
|
510 |
|
511 |
|
512 |
|
513 | const args = {
|
514 | _: [],
|
515 | host: options.address,
|
516 | port: options.port.toString(),
|
517 | };
|
518 | const scriptArgs = [...cli_framework_1.unparseArgs(args), ...options['--'] || []];
|
519 | const [, ...pkgArgs] = await pkgManagerArgs(this.program, { command: 'run', script: this.script, scriptArgs });
|
520 | return pkgArgs;
|
521 | }
|
522 | }
|
523 | class NpmServeCLI extends PkgManagerServeCLI {
|
524 | constructor() {
|
525 | super(...arguments);
|
526 | this.name = 'npm CLI';
|
527 | this.pkg = 'npm';
|
528 | this.program = 'npm';
|
529 | this.prefix = 'npm';
|
530 | }
|
531 | }
|
532 | exports.NpmServeCLI = NpmServeCLI;
|
533 | class YarnServeCLI extends PkgManagerServeCLI {
|
534 | constructor() {
|
535 | super(...arguments);
|
536 | this.name = 'Yarn';
|
537 | this.pkg = 'yarn';
|
538 | this.program = 'yarn';
|
539 | this.prefix = 'yarn';
|
540 | }
|
541 | }
|
542 | exports.YarnServeCLI = YarnServeCLI;
|
543 | class IonicLabServeCLI extends ServeCLI {
|
544 | constructor() {
|
545 | super(...arguments);
|
546 | this.name = 'Ionic Lab';
|
547 | this.pkg = '@ionic/lab';
|
548 | this.program = 'ionic-lab';
|
549 | this.prefix = 'lab';
|
550 | this.script = undefined;
|
551 | }
|
552 | stdoutFilter(line) {
|
553 | if (line.includes('running')) {
|
554 | this.emit('ready');
|
555 | }
|
556 | return false;
|
557 | }
|
558 | async buildArgs(options) {
|
559 | const { serveDetails, ...labDetails } = options;
|
560 | const pkg = await this.e.project.requirePackageJson();
|
561 | const url = `${serveDetails.protocol}://localhost:${serveDetails.port}`;
|
562 | const appName = this.e.project.config.get('name');
|
563 | const labArgs = [url, '--host', labDetails.address, '--port', String(labDetails.port), '--project-type', labDetails.projectType];
|
564 | const nameArgs = appName ? ['--app-name', appName] : [];
|
565 | const versionArgs = pkg.version ? ['--app-version', pkg.version] : [];
|
566 | return [...labArgs, ...nameArgs, ...versionArgs];
|
567 | }
|
568 | }
|