Skip to content

Commit 0d162b2

Browse files
committed
merge master
2 parents 52b6f06 + a93f2da commit 0d162b2

File tree

9 files changed

+229
-19
lines changed

9 files changed

+229
-19
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ npm run build --rollup ./config/rollup.config.js
9696
| NGC | `ionic_ngc` | `--ngc` or `-n` |
9797
| Rollup | `ionic_rollup` | `--rollup` or `-r` |
9898
| Sass | `ionic_sass` | `--sass` or `-s` |
99-
| TSLint | `ionic_tslint` | `--tslint` or `-l` |
99+
| TSLint | `ionic_tslint` | `--tslint` or `-i` |
100100
| UglifyJS | `ionic_uglifyjs` | `--uglifyjs` or `-u` |
101101
| Webpack | `ionic_webpack` | `--webpack` or `-w` |
102102

config/watch.config.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ var buildUpdate = require('../dist/build').buildUpdate;
22
var templateUpdate = require('../dist/template').templateUpdate;
33
var copyUpdate = require('../dist/copy').copyUpdate;
44
var sassUpdate = require('../dist/sass').sassUpdate;
5+
var copyConfig = require('./copy.config').include;
56

67

78
// https://www.npmjs.com/package/chokidar
@@ -34,9 +35,7 @@ module.exports = {
3435
},
3536

3637
{
37-
paths: [
38-
'{{SRC}}/assets'
39-
],
38+
paths: copyConfig.map(f => f.src),
4039
callback: copyUpdate
4140
},
4241

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"rollup-plugin-node-globals": "1.0.9",
4343
"rollup-plugin-node-resolve": "2.0.0",
4444
"rollup-pluginutils": "1.5.2",
45+
"tiny-lr": "^1.0.2",
4546
"tslint": "3.15.1",
4647
"tslint-eslint-rules": "1.5.0",
4748
"typescript": "^2.0.3",
@@ -60,6 +61,7 @@
6061
"@types/uglify-js": "2.0.27",
6162
"@types/webpack": "^1.12.35",
6263
"jasmine": "2.5.2",
64+
"mime-types": "^2.1.12",
6365
"mock-fs": "^3.11.0",
6466
"tslint": "3.15.1",
6567
"tslint-ionic-rules": "0.0.5"

src/copy.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BuildContext, TaskInfo } from './util/interfaces';
22
import { BuildError, Logger } from './util/logger';
3+
import { emit, EventType } from './util/events';
34
import { fillConfigDefaults, generateContext, getUserConfigFile, replacePathVars } from './util/config';
45
import * as fs from 'fs-extra';
56
import * as path from 'path';
@@ -38,15 +39,25 @@ export function copyUpdate(event: string, filePath: string, context: BuildContex
3839
const promises = fileCopyOptions.map(copyOptions => {
3940
return copySrcToDest(context, copyOptions.src, copyOptions.dest, copyOptions.filter, true);
4041
});
41-
return Promise.all(promises);
42+
return Promise.all(promises).then(destFiles => {
43+
emit(EventType.FileChange, context, destFiles);
44+
});
4245
}
4346

4447
} else if (event === 'unlink' || event === 'unlinkDir') {
4548
return new Promise((resolve, reject) => {
46-
fs.remove(path.join(context.rootDir, filePath), (err) => {
49+
const destFile = path.join(context.rootDir, filePath);
50+
fs.remove(destFile, (err) => {
4751
if (err) {
4852
reject(new BuildError(err));
53+
4954
} else {
55+
if (event === 'unlink') {
56+
emit(EventType.FileDelete, context, destFile);
57+
} else if (event === 'unlinkDir') {
58+
emit(EventType.DirectoryDelete, context, destFile);
59+
}
60+
5061
resolve();
5162
}
5263
});
@@ -113,7 +124,7 @@ export function findFileCopyOptions(context: BuildContext, copyConfig: CopyConfi
113124
}
114125

115126

116-
function copySrcToDest(context: BuildContext, src: string, dest: string, filter: any, clobber: boolean) {
127+
function copySrcToDest(context: BuildContext, src: string, dest: string, filter: any, clobber: boolean): Promise<string> {
117128
return new Promise((resolve, reject) => {
118129
src = path.resolve(replacePathVars(context, src));
119130
dest = path.resolve(replacePathVars(context, dest));
@@ -133,7 +144,7 @@ function copySrcToDest(context: BuildContext, src: string, dest: string, filter:
133144
return;
134145
}
135146
}
136-
resolve();
147+
resolve(dest);
137148
});
138149
});
139150
}

src/declarations.d.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
declare module 'autoprefixer';
2+
declare module 'mime-types';
23
declare module 'rollup-pluginutils';
3-
declare module 'rollup';
4+
declare module 'rollup';
5+
declare module 'tiny-lr';

src/rollup.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BuildContext, TaskInfo } from './util/interfaces';
22
import { BuildError, Logger } from './util/logger';
3+
import { emit, EventType } from './util/events';
34
import { endsWith, setModulePathsCache } from './util/helpers';
45
import { fillConfigDefaults, generateContext, getUserConfigFile, replacePathVars } from './util/config';
56
import { ionCompiler } from './plugins/ion-compiler';
@@ -97,7 +98,9 @@ export function rollupWorker(context: BuildContext, configFile: string): Promise
9798
})
9899
.then(() => {
99100
// clean up any references (overkill yes, but let's play it safe)
101+
emit(EventType.FileChange, context, rollupConfig.dest);
100102
rollupConfig = rollupConfig.cache = rollupConfig.onwarn = rollupConfig.plugins = null;
103+
101104
resolve();
102105
})
103106
.catch((err: any) => {

src/sass.ts

+13-10
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { basename, dirname, join, sep } from 'path';
22
import { BuildContext, TaskInfo } from './util/interfaces';
33
import { BuildError, Logger } from './util/logger';
4+
import { emit, EventType } from './util/events';
45
import { ensureDirSync, readdirSync, writeFile } from 'fs-extra';
56
import { fillConfigDefaults, generateContext, getUserConfigFile, replacePathVars } from './util/config';
67
import { getModulePathsCache } from './util/helpers';
7-
import { runWorker } from './worker-client';
88
import { SassError, render as nodeSassRender, Result } from 'node-sass';
99
import * as postcss from 'postcss';
1010
import * as autoprefixer from 'autoprefixer';
@@ -18,7 +18,7 @@ export function sass(context?: BuildContext, configFile?: string) {
1818

1919
context.successfulCopy = false;
2020

21-
return runWorker('sass', context, configFile)
21+
return sassWorker(context, configFile)
2222
.then(() => {
2323
context.successfulSass = true;
2424
logger.finish();
@@ -34,11 +34,11 @@ export function sassUpdate(event: string, path: string, context: BuildContext) {
3434

3535
const logger = new Logger('sass update');
3636

37-
return runWorker('sass', context, configFile)
37+
return sassWorker(context, configFile)
3838
.then(() => {
3939
logger.finish();
40-
41-
}).catch(err => {
40+
})
41+
.catch(err => {
4242
throw logger.fail(err);
4343
});
4444
}
@@ -269,7 +269,7 @@ function render(context: BuildContext, sassConfig: SassConfig) {
269269

270270
} else {
271271
// sass render success!
272-
renderSassSuccess(sassResult, sassConfig).then(() => {
272+
renderSassSuccess(context, sassResult, sassConfig).then(() => {
273273
lastRenderKey = getRenderCacheKey(sassConfig);
274274
resolve();
275275

@@ -282,7 +282,7 @@ function render(context: BuildContext, sassConfig: SassConfig) {
282282
}
283283

284284

285-
function renderSassSuccess(sassResult: Result, sassConfig: SassConfig): Promise<any> {
285+
function renderSassSuccess(context: BuildContext, sassResult: Result, sassConfig: SassConfig): Promise<any> {
286286
if (sassConfig.autoprefixer) {
287287
// with autoprefixer
288288

@@ -313,7 +313,7 @@ function renderSassSuccess(sassResult: Result, sassConfig: SassConfig): Promise<
313313
}
314314

315315
Logger.debug(`sass: postcss/autoprefixer completed`);
316-
return writeOutput(sassConfig, postCssResult.css, apMapResult);
316+
return writeOutput(context, sassConfig, postCssResult.css, apMapResult);
317317
});
318318
}
319319

@@ -325,7 +325,7 @@ function renderSassSuccess(sassResult: Result, sassConfig: SassConfig): Promise<
325325
sassMapResult = JSON.parse(sassResult.map.toString()).mappings;
326326
}
327327

328-
return writeOutput(sassConfig, sassResult.css.toString(), sassMapResult);
328+
return writeOutput(context, sassConfig, sassResult.css.toString(), sassMapResult);
329329
}
330330

331331

@@ -370,7 +370,7 @@ function generateSourceMaps(sassResult: Result, sassConfig: SassConfig) {
370370
}
371371

372372

373-
function writeOutput(sassConfig: SassConfig, cssOutput: string, mappingsOutput: string) {
373+
function writeOutput(context: BuildContext, sassConfig: SassConfig, cssOutput: string, mappingsOutput: string) {
374374
return new Promise((resolve, reject) => {
375375

376376
Logger.debug(`sass start write output: ${sassConfig.outFile}`);
@@ -402,6 +402,9 @@ function writeOutput(sassConfig: SassConfig, cssOutput: string, mappingsOutput:
402402
});
403403
}
404404

405+
// notify a file has changed
406+
emit(EventType.FileChange, context, sassConfig.outFile);
407+
405408
// css file all saved
406409
// note that we're not waiting on the css map to finish saving
407410
resolve();

src/serve.ts

+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import { BuildContext } from './util/interfaces';
2+
import { generateContext, getConfigValueDefault } from './util/config';
3+
import { Logger } from './util/logger';
4+
import { join, relative } from 'path';
5+
import { readFile } from 'fs';
6+
import { watch } from './watch';
7+
import * as chalk from 'chalk';
8+
import * as events from './util/events';
9+
import * as http from 'http';
10+
import * as mime from 'mime-types';
11+
import * as tinylr from 'tiny-lr';
12+
13+
14+
let devServer: http.Server;
15+
let liveReloadServer: any;
16+
17+
18+
export function serve(context?: BuildContext) {
19+
context = generateContext(context);
20+
21+
setupEvents();
22+
23+
return watch(context)
24+
.then(() => {
25+
createDevServer(context);
26+
}, () => {
27+
createDevServer(context);
28+
});
29+
}
30+
31+
32+
export function createDevServer(context: BuildContext) {
33+
const port = getDevServerPort(context);
34+
const host = getDevServerHost(context);
35+
const devAddress = `http://${host || 'localhost'}:${port}/`;
36+
37+
function httpServerListen() {
38+
devServer.listen(port, host, undefined, () => {
39+
Logger.info(chalk.green(`dev server running: ${devAddress}`));
40+
createLiveReloadServer(host);
41+
});
42+
}
43+
44+
if (devServer) {
45+
devServer.close();
46+
}
47+
48+
devServer = http.createServer((request, response) => {
49+
devServerListener(context, request, response);
50+
});
51+
52+
devServer.on('error', (err: any) => {
53+
if (err.code === 'EADDRINUSE') {
54+
Logger.error(`server already in use, retrying....`);
55+
56+
setTimeout(() => {
57+
devServer.close();
58+
httpServerListen();
59+
}, 1500);
60+
}
61+
});
62+
63+
httpServerListen();
64+
}
65+
66+
67+
function devServerListener(context: BuildContext, request: http.IncomingMessage, response: http.ServerResponse) {
68+
let filePath = '.' + request.url;
69+
if (filePath === './') {
70+
filePath = './' + DEV_SERVER_DEFAULT_INDEX;
71+
}
72+
73+
filePath = join(context.wwwDir, filePath);
74+
filePath = filePath.split('?')[0];
75+
76+
if (filePath.indexOf('/cordova.js') > -1) {
77+
response.writeHead(200, { 'Content-Type': 'application/javascript' });
78+
response.end('// mock cordova file during development');
79+
return;
80+
}
81+
82+
readFile(filePath, (err, content) => {
83+
const contentType = {
84+
'Content-Type': 'text/html',
85+
'X-DEV-FILE-PATH': filePath
86+
};
87+
88+
if (err) {
89+
if (err.code === 'ENOENT') {
90+
response.writeHead(404, contentType);
91+
response.end(`File not found: ${request.url}<br>Local file: ${filePath}`);
92+
93+
} else {
94+
Logger.error(`http server error: ${err}`);
95+
response.writeHead(500, contentType);
96+
response.end(`Sorry, check with the site admin for error: ${err.code} ..\n`);
97+
}
98+
99+
} else {
100+
contentType['Content-Type'] = mime.lookup(filePath) || 'application/octet-stream';
101+
response.writeHead(200, contentType);
102+
response.end(content, mime.charset(contentType));
103+
}
104+
});
105+
}
106+
107+
108+
function createLiveReloadServer(host: string) {
109+
if (liveReloadServer) {
110+
return;
111+
}
112+
113+
liveReloadServer = tinylr();
114+
115+
liveReloadServer.listen(LIVE_RELOAD_DEFAULT_PORT, host, () => {
116+
Logger.info('live reloader');
117+
118+
});
119+
}
120+
121+
122+
function fileChanged(context: BuildContext, filePath: string|string[]) {
123+
if (liveReloadServer) {
124+
const files = Array.isArray(filePath) ? filePath : [filePath];
125+
126+
console.log('FileChange', files);
127+
128+
liveReloadServer.changed({
129+
body: {
130+
files: files.map(f => '/' + relative(context.wwwDir, f))
131+
}
132+
});
133+
}
134+
}
135+
136+
137+
function reload(context: BuildContext) {
138+
fileChanged(context, DEV_SERVER_DEFAULT_INDEX);
139+
}
140+
141+
142+
function setupEvents() {
143+
events.on(events.EventType.FileChange, (context: BuildContext, filePath: string) => {
144+
fileChanged(context, filePath);
145+
});
146+
}
147+
148+
149+
function getDevServerPort(context: BuildContext) {
150+
const port = getConfigValueDefault('--port', '-p', 'ionic_port', null);
151+
if (port) {
152+
return parseInt(port, 10);
153+
}
154+
return DEV_SERVER_DEFAULT_PORT;
155+
}
156+
157+
158+
function getDevServerHost(context: BuildContext) {
159+
const host = getConfigValueDefault('--host', '-h', 'ionic_host', null);
160+
if (host) {
161+
return host;
162+
}
163+
}
164+
165+
166+
const DEV_SERVER_DEFAULT_PORT = 8100;
167+
const DEV_SERVER_DEFAULT_INDEX = 'index.html';
168+
const LIVE_RELOAD_DEFAULT_PORT = 35729;

0 commit comments

Comments
 (0)