Skip to content

Commit 0189588

Browse files
committed
refactor(serve): pull out livereload logic
1 parent 7f8ffc1 commit 0189588

7 files changed

Lines changed: 118 additions & 98 deletions

File tree

packages/@ionic/cli-utils/src/commands/serve.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ export async function serve(env: IonicEnvironment, inputs: CommandLineInputs, op
1818
livereloadPort,
1919
consolelogs: options['consolelogs'] ? true : false,
2020
serverlogs: options['serverlogs'] ? true : false,
21-
nobrowser: options['nobrowser'] ? true : false,
22-
nolivereload: options['nolivereload'] ? true : false,
23-
noproxy: options['noproxy'] ? true : false,
21+
livereload: options['nolivereload'] ? false : true,
22+
proxy: options['noproxy'] ? false : true,
2423
lab: options['lab'] ? true : false,
25-
browser: options['browser'] ? String(options['browser']) : undefined,
26-
browseroption: options['browseroption'] ? String(options['browseroption']) : undefined,
24+
browser: options['nobrowser'] ? false : true,
25+
browserName: options['browser'] ? String(options['browser']) : undefined,
26+
browserOption: options['browseroption'] ? String(options['browseroption']) : undefined,
2727
platform: options['platform'] ? String(options['platform']) : undefined,
2828
externalAddressRequired: options['externalAddressRequired'] ? true : false,
2929
iscordovaserve: typeof options['iscordovaserve'] === 'boolean' ? Boolean(options['iscordovaserve']) : false,
@@ -53,14 +53,14 @@ export async function serve(env: IonicEnvironment, inputs: CommandLineInputs, op
5353
);
5454

5555
if (project.type !== 'ionic-angular') { // TODO: app-scripts calls opn internally
56-
if (!serveOptions.nobrowser) {
56+
if (serveOptions.browser) {
5757
const openOptions: string[] = [localAddress]
5858
.concat(serveOptions.lab ? [IONIC_LAB_URL] : [])
59-
.concat(serveOptions.browseroption ? [serveOptions.browseroption] : [])
59+
.concat(serveOptions.browserOption ? [serveOptions.browserOption] : [])
6060
.concat(serveOptions.platform ? ['?ionicplatform=', serveOptions.platform] : []);
6161

6262
const opn = await import('opn');
63-
opn(openOptions.join(''), { app: serveOptions.browser, wait: false });
63+
opn(openOptions.join(''), { app: serveOptions.browserName, wait: false });
6464
}
6565
}
6666

packages/@ionic/cli-utils/src/definitions.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -434,12 +434,12 @@ export interface ServeOptions {
434434
livereloadPort: number;
435435
consolelogs: boolean;
436436
serverlogs: boolean;
437-
nobrowser: boolean;
438-
nolivereload: boolean;
439-
noproxy: boolean;
437+
livereload: boolean;
438+
proxy: boolean;
440439
lab: boolean;
441-
browser?: string;
442-
browseroption?: string;
440+
browser: boolean;
441+
browserName?: string;
442+
browserOption?: string;
443443
platform?: string;
444444

445445
// Additional Options
@@ -704,3 +704,5 @@ export interface StarterTemplateType {
704704
globalDependencies: string[];
705705
localDependencies: string[];
706706
}
707+
708+
export type LiveReloadFunction = (changedFiles: string[]) => void;

packages/@ionic/cli-utils/src/lib/ionic-angular/__tests__/serve.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ describe('@ionic/cli-utils', () => {
1414
livereloadPort: 27000,
1515
consolelogs: true,
1616
serverlogs: true,
17-
nobrowser: false,
18-
nolivereload: false,
19-
noproxy: false,
17+
browser: true,
18+
livereload: true,
19+
proxy: true,
2020
lab: false,
2121
};
2222

@@ -26,7 +26,7 @@ describe('@ionic/cli-utils', () => {
2626
});
2727

2828
it('should transform extra options', async () => {
29-
const result = await serveOptionsToAppScriptsArgs({ browser: 'firefox', browseroption: '/#/tab/dash', platform: 'android', ...options, consolelogs: false, serverlogs: false, nobrowser: true, nolivereload: true, noproxy: true, lab: true });
29+
const result = await serveOptionsToAppScriptsArgs({ browserName: 'firefox', browserOption: '/#/tab/dash', platform: 'android', ...options, consolelogs: false, serverlogs: false, browser: false, livereload: false, proxy: false, lab: true });
3030
expect(result).toEqual(['--address', '0.0.0.0', '--port', '8100', '--livereload-port', '27000', '--nobrowser', '--nolivereload', '--noproxy', '--lab', '--browser', 'firefox', '--browseroption', '/#/tab/dash', '--platform', 'android']);
3131
});
3232

packages/@ionic/cli-utils/src/lib/ionic-angular/serve.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@ export async function serveOptionsToAppScriptsArgs(options: ServeOptions) {
8181
livereloadPort: String(options.livereloadPort),
8282
consolelogs: options.consolelogs,
8383
serverlogs: options.serverlogs,
84-
nobrowser: options.nobrowser,
85-
nolivereload: options.nolivereload,
86-
noproxy: options.noproxy,
84+
nobrowser: !options.browser,
85+
nolivereload: !options.livereload,
86+
noproxy: !options.proxy,
8787
lab: options.lab,
88-
browser: options.browser,
89-
browseroption: options.browseroption,
88+
browser: options.browserName,
89+
browseroption: options.browserOption,
9090
platform: options.platform,
9191
iscordovaserve: options.iscordovaserve,
9292
};

packages/@ionic/cli-utils/src/lib/ionic1/serve.ts

Lines changed: 30 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
import * as fs from 'fs';
21
import * as path from 'path';
32

43
import * as chalk from 'chalk';
54

65
import * as expressType from 'express';
76

8-
import { IonicEnvironment, ServeDetails, ServeOptions } from '../../definitions';
7+
import { IonicEnvironment, LiveReloadFunction, ServeDetails, ServeOptions } from '../../definitions';
98

109
import { BIND_ALL_ADDRESS, IONIC_LAB_URL, LOCAL_ADDRESSES } from '../serve';
1110
import { FatalException } from '../errors';
11+
import { fsReadFile, pathExists } from '../utils/fs';
1212

1313
const WATCH_PATTERNS = [
1414
'www/**/*',
@@ -99,7 +99,13 @@ export async function serve({ env, options }: { env: IonicEnvironment; options:
9999
}
100100

101101
async function setupServer(env: IonicEnvironment, options: ServeMetaOptions): Promise<ServeMetaOptions> {
102-
const liveReloadBrowser = await createLiveReloadServer(env, options);
102+
let reloadfn: LiveReloadFunction | undefined;
103+
104+
if (options.livereload) {
105+
const { createLiveReloadServer } = await import('../livereload');
106+
reloadfn = await createLiveReloadServer(env, { port: options.livereloadPort, wwwDir: options.wwwDir });
107+
}
108+
103109
await createHttpServer(env, options);
104110

105111
const chokidar = await import('chokidar');
@@ -116,7 +122,11 @@ async function setupServer(env: IonicEnvironment, options: ServeMetaOptions): Pr
116122

117123
watcher.on('change', (filePath: string) => {
118124
env.log.info(`[${new Date().toTimeString().slice(0, 8)}] ${chalk.bold(filePath)} changed`);
119-
liveReloadBrowser([filePath]);
125+
126+
if (reloadfn) {
127+
reloadfn([filePath]);
128+
}
129+
120130
env.events.emit('watch:change', filePath);
121131
});
122132

@@ -127,54 +137,6 @@ async function setupServer(env: IonicEnvironment, options: ServeMetaOptions): Pr
127137
return options;
128138
}
129139

130-
async function createLiveReloadServer(env: IonicEnvironment, options: ServeMetaOptions): Promise<(changedFile: string[]) => void> {
131-
const tinylr = await import('tiny-lr');
132-
const liveReloadServer = tinylr();
133-
liveReloadServer.listen(options.livereloadPort);
134-
135-
return (changedFiles) => {
136-
liveReloadServer.changed({
137-
body: {
138-
files: changedFiles.map(changedFile => (
139-
'/' + path.relative(options.wwwDir, changedFile)
140-
))
141-
}
142-
});
143-
};
144-
}
145-
146-
export function injectLiveReloadScript(content: any, host: string, port: number): any {
147-
let contentStr = content.toString();
148-
const liveReloadScript = getLiveReloadScript(host, port);
149-
150-
if (contentStr.indexOf('/livereload.js') > -1) {
151-
// already added script
152-
return content;
153-
}
154-
155-
let match = contentStr.match(/<\/body>(?![\s\S]*<\/body>)/i);
156-
if (!match) {
157-
match = contentStr.match(/<\/html>(?![\s\S]*<\/html>)/i);
158-
}
159-
if (match) {
160-
contentStr = contentStr.replace(match[0], `${liveReloadScript}\n${match[0]}`);
161-
} else {
162-
contentStr += liveReloadScript;
163-
}
164-
165-
return contentStr;
166-
}
167-
168-
function getLiveReloadScript(host: string, port: number) {
169-
if (host === BIND_ALL_ADDRESS) {
170-
host = 'localhost';
171-
}
172-
const src = `//${host}:${port}/livereload.js?snipver=1`;
173-
174-
return ` <!-- Ionic Dev Server: Injected LiveReload Script -->\n` +
175-
` <script src="${src}" async="" defer=""></script>`;
176-
}
177-
178140
/**
179141
* Create HTTP server
180142
*/
@@ -186,23 +148,24 @@ async function createHttpServer(env: IonicEnvironment, options: ServeMetaOptions
186148
/**
187149
* http responder for /index.html base entrypoint
188150
*/
189-
const serveIndex = (req: expressType.Request, res: expressType.Response) => {
151+
const serveIndex = async (req: expressType.Request, res: expressType.Response) => {
190152
// respond with the index.html file
191153
const indexFileName = path.join(options.wwwDir, 'index.html');
192-
fs.readFile(indexFileName, (err, indexHtml) => {
193-
if (!options.nolivereload) {
194-
indexHtml = injectLiveReloadScript(indexHtml, options.externalIP, options.livereloadPort);
195-
}
196-
197-
res.set('Content-Type', 'text/html');
198-
res.send(indexHtml);
199-
});
154+
let indexHtml = await fsReadFile(indexFileName, { encoding: 'utf8' });
155+
156+
if (options.livereload) {
157+
const { injectLiveReloadScript } = await import('../livereload');
158+
indexHtml = injectLiveReloadScript(indexHtml, options.externalIP, options.livereloadPort);
159+
}
160+
161+
res.set('Content-Type', 'text/html');
162+
res.send(indexHtml);
200163
};
201164

202165
/**
203166
* Middleware to serve platform resources
204167
*/
205-
const servePlatformResource = (req: expressType.Request, res: expressType.Response, next: expressType.NextFunction) => {
168+
const servePlatformResource = async (req: expressType.Request, res: expressType.Response, next: expressType.NextFunction) => {
206169
const userAgent = req.header('user-agent') || '';
207170
let resourcePath = options.wwwDir;
208171

@@ -216,12 +179,11 @@ async function createHttpServer(env: IonicEnvironment, options: ServeMetaOptions
216179
resourcePath = path.join(env.project.directory, ANDROID_PLATFORM_PATH);
217180
}
218181

219-
fs.stat(path.join(resourcePath, req.url), (err, stats) => {
220-
if (err) {
221-
return next();
222-
}
182+
if (await pathExists(path.join(resourcePath, req.url))) {
223183
res.sendFile(req.url, { root: resourcePath });
224-
});
184+
} else {
185+
next();
186+
}
225187
};
226188

227189
app.get('/', serveIndex);
@@ -244,7 +206,7 @@ async function createHttpServer(env: IonicEnvironment, options: ServeMetaOptions
244206
app.get('/cordova_plugins.js', servePlatformResource);
245207
app.get('/plugins/*', servePlatformResource);
246208

247-
if (!options.noproxy) {
209+
if (options.proxy) {
248210
await setupProxies(env, app);
249211
}
250212

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import * as path from 'path';
2+
3+
import { IonicEnvironment, LiveReloadFunction } from '../definitions';
4+
5+
import { BIND_ALL_ADDRESS } from './serve';
6+
7+
export async function createLiveReloadServer(env: IonicEnvironment, { port, wwwDir }: { port: number; wwwDir: string; }): Promise<LiveReloadFunction> {
8+
const tinylr = await import('tiny-lr');
9+
const lrserver = tinylr();
10+
lrserver.listen(port);
11+
12+
return (changedFiles) => {
13+
lrserver.changed({
14+
body: {
15+
files: changedFiles.map(changedFile => (
16+
'/' + path.relative(wwwDir, changedFile)
17+
))
18+
}
19+
});
20+
};
21+
}
22+
23+
export function injectLiveReloadScript(content: any, host: string, port: number): any {
24+
let contentStr = content.toString();
25+
const liveReloadScript = getLiveReloadScript(host, port);
26+
27+
if (contentStr.indexOf('/livereload.js') > -1) {
28+
// already added script
29+
return content;
30+
}
31+
32+
let match = contentStr.match(/<\/body>(?![\s\S]*<\/body>)/i);
33+
if (!match) {
34+
match = contentStr.match(/<\/html>(?![\s\S]*<\/html>)/i);
35+
}
36+
if (match) {
37+
contentStr = contentStr.replace(match[0], `${liveReloadScript}\n${match[0]}`);
38+
} else {
39+
contentStr += liveReloadScript;
40+
}
41+
42+
return contentStr;
43+
}
44+
45+
function getLiveReloadScript(host: string, port: number) {
46+
if (host === BIND_ALL_ADDRESS) {
47+
host = 'localhost';
48+
}
49+
const src = `//${host}:${port}/livereload.js?snipver=1`;
50+
51+
return ` <!-- Ionic Dev Server: Injected LiveReload Script -->\n` +
52+
` <script src="${src}" async="" defer=""></script>`;
53+
}

types/tiny-lr.d.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
declare module "tiny-lr" {
2-
namespace tinyLr {}
3-
interface server {
4-
on: Function;
5-
listen: Function;
6-
close: Function;
7-
changed: Function;
2+
import * as http from 'http';
3+
4+
namespace TinyLR {}
5+
6+
interface TinyLRServer {
7+
listen(port: number, cb?: () => {});
8+
close();
9+
changed(p: any);
810
}
9-
function tinyLr(): server;
1011

11-
export = tinyLr;
12+
function TinyLR(): TinyLRServer;
13+
14+
export = TinyLR;
1215
}

0 commit comments

Comments
 (0)