Skip to content
This repository was archived by the owner on May 1, 2020. It is now read-only.

Commit 254bb6c

Browse files
authored
fix(build): better support for saving multiple files at a time
1 parent 64eb845 commit 254bb6c

14 files changed

+208
-234
lines changed

src/build.ts

+66-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
import { BuildContext, BuildState, BuildUpdateMessage } from './util/interfaces';
1+
import { FILE_CHANGE_EVENT, FILE_DELETE_EVENT } from './util/constants';
2+
import { BuildContext, BuildState, BuildUpdateMessage, ChangedFile } from './util/interfaces';
23
import { BuildError } from './util/errors';
4+
import { readFileAsync } from './util/helpers';
35
import { bundle, bundleUpdate } from './bundle';
46
import { clean } from './clean';
57
import { copy } from './copy';
@@ -121,7 +123,7 @@ function buildDev(context: BuildContext) {
121123
}
122124

123125

124-
export function buildUpdate(event: string, filePath: string, context: BuildContext) {
126+
export function buildUpdate(changedFiles: ChangedFile[], context: BuildContext) {
125127
return new Promise(resolve => {
126128
const logger = new Logger('build');
127129

@@ -151,13 +153,20 @@ export function buildUpdate(event: string, filePath: string, context: BuildConte
151153
// this one is useful when only a sass changed happened
152154
// and the webpack only needs to livereload the css
153155
// but does not need to do a full page refresh
154-
emit(EventType.FileChange, resolveValue.changedFile);
156+
emit(EventType.FileChange, resolveValue.changedFiles);
155157
}
156158

157-
if (filePath.endsWith('.ts')) {
159+
let requiresLintUpdate = false;
160+
for (const changedFile of changedFiles) {
161+
if (changedFile.ext === '.ts' && changedFile.event === 'ch') {
162+
requiresLintUpdate = true;
163+
break;
164+
}
165+
}
166+
if (requiresLintUpdate) {
158167
// a ts file changed, so let's lint it too, however
159168
// this task should run as an after thought
160-
lintUpdate(event, filePath, context);
169+
lintUpdate(changedFiles, context);
161170
}
162171

163172
logger.finish('green', true);
@@ -170,16 +179,16 @@ export function buildUpdate(event: string, filePath: string, context: BuildConte
170179

171180
// kick off all the build tasks
172181
// and the tasks that can run parallel to all the build tasks
173-
const buildTasksPromise = buildUpdateTasks(event, filePath, context);
174-
const parallelTasksPromise = buildUpdateParallelTasks(event, filePath, context);
182+
const buildTasksPromise = buildUpdateTasks(changedFiles, context);
183+
const parallelTasksPromise = buildUpdateParallelTasks(changedFiles, context);
175184

176185
// whether it was resolved or rejected, we need to do the same thing
177186
buildTasksPromise
178187
.then(buildTasksDone)
179188
.catch(() => {
180189
buildTasksDone({
181190
requiresAppReload: false,
182-
changedFile: filePath
191+
changedFiles: changedFiles
183192
});
184193
});
185194
});
@@ -189,18 +198,21 @@ export function buildUpdate(event: string, filePath: string, context: BuildConte
189198
* Collection of all the build tasks than need to run
190199
* Each task will only run if it's set with eacn BuildState.
191200
*/
192-
function buildUpdateTasks(event: string, filePath: string, context: BuildContext) {
201+
function buildUpdateTasks(changedFiles: ChangedFile[], context: BuildContext) {
193202
const resolveValue: BuildTaskResolveValue = {
194203
requiresAppReload: false,
195-
changedFile: filePath
204+
changedFiles: []
196205
};
197206

198207
return Promise.resolve()
208+
.then(() => {
209+
return loadFiles(changedFiles, context);
210+
})
199211
.then(() => {
200212
// TEMPLATE
201213
if (context.templateState === BuildState.RequiresUpdate) {
202214
resolveValue.requiresAppReload = true;
203-
return templateUpdate(event, filePath, context);
215+
return templateUpdate(changedFiles, context);
204216
}
205217
// no template updates required
206218
return Promise.resolve();
@@ -213,7 +225,7 @@ function buildUpdateTasks(event: string, filePath: string, context: BuildContext
213225
// we've already had a successful transpile once, only do an update
214226
// not that we've also already started a transpile diagnostics only
215227
// build that only needs to be completed by the end of buildUpdate
216-
return transpileUpdate(event, filePath, context);
228+
return transpileUpdate(changedFiles, context);
217229

218230
} else if (context.transpileState === BuildState.RequiresBuild) {
219231
// run the whole transpile
@@ -229,7 +241,7 @@ function buildUpdateTasks(event: string, filePath: string, context: BuildContext
229241
if (context.bundleState === BuildState.RequiresUpdate) {
230242
// we need to do a bundle update
231243
resolveValue.requiresAppReload = true;
232-
return bundleUpdate(event, filePath, context);
244+
return bundleUpdate(changedFiles, context);
233245

234246
} else if (context.bundleState === BuildState.RequiresBuild) {
235247
// we need to do a full bundle build
@@ -244,14 +256,30 @@ function buildUpdateTasks(event: string, filePath: string, context: BuildContext
244256
// SASS
245257
if (context.sassState === BuildState.RequiresUpdate) {
246258
// we need to do a sass update
247-
return sassUpdate(event, filePath, context).then(outputCssFile => {
248-
resolveValue.changedFile = outputCssFile;
259+
return sassUpdate(changedFiles, context).then(outputCssFile => {
260+
const changedFile: ChangedFile = {
261+
event: FILE_CHANGE_EVENT,
262+
ext: '.css',
263+
filePath: outputCssFile
264+
};
265+
266+
context.fileCache.set(outputCssFile, { path: outputCssFile, content: outputCssFile});
267+
268+
resolveValue.changedFiles.push(changedFile);
249269
});
250270

251271
} else if (context.sassState === BuildState.RequiresBuild) {
252272
// we need to do a full sass build
253273
return sass(context).then(outputCssFile => {
254-
resolveValue.changedFile = outputCssFile;
274+
const changedFile: ChangedFile = {
275+
event: FILE_CHANGE_EVENT,
276+
ext: '.css',
277+
filePath: outputCssFile
278+
};
279+
280+
context.fileCache.set(outputCssFile, { path: outputCssFile, content: outputCssFile});
281+
282+
resolveValue.changedFiles.push(changedFile);
255283
});
256284
}
257285
// no sass build required
@@ -262,17 +290,37 @@ function buildUpdateTasks(event: string, filePath: string, context: BuildContext
262290
});
263291
}
264292

293+
function loadFiles(changedFiles: ChangedFile[], context: BuildContext) {
294+
// UPDATE IN-MEMORY FILE CACHE
295+
let promises: Promise<any>[] = [];
296+
for (const changedFile of changedFiles) {
297+
if (changedFile.event === FILE_DELETE_EVENT) {
298+
// remove from the cache on delete
299+
context.fileCache.remove(changedFile.filePath);
300+
} else {
301+
// load the latest since the file changed
302+
const promise = readFileAsync(changedFile.filePath);
303+
promises.push(promise);
304+
promise.then((content: string) => {
305+
context.fileCache.set(changedFile.filePath, { path: changedFile.filePath, content: content});
306+
});
307+
}
308+
}
309+
310+
return Promise.all(promises);
311+
}
312+
265313
interface BuildTaskResolveValue {
266314
requiresAppReload: boolean;
267-
changedFile: string;
315+
changedFiles: ChangedFile[];
268316
}
269317

270318
/**
271319
* parallelTasks are for any tasks that can run parallel to the entire
272320
* build, but we still need to make sure they've completed before we're
273321
* all done, it's also possible there are no parallelTasks at all
274322
*/
275-
function buildUpdateParallelTasks(event: string, filePath: string, context: BuildContext) {
323+
function buildUpdateParallelTasks(changedFiles: ChangedFile[], context: BuildContext) {
276324
const parallelTasks: Promise<any>[] = [];
277325

278326
if (context.transpileState === BuildState.RequiresUpdate) {

src/bundle.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BuildContext } from './util/interfaces';
1+
import { BuildContext, ChangedFile } from './util/interfaces';
22
import { BuildError, IgnorableError } from './util/errors';
33
import { generateContext, BUNDLER_ROLLUP } from './util/config';
44
import { rollup, rollupUpdate, getRollupConfig, getOutputDest as rollupGetOutputDest } from './rollup';
@@ -24,15 +24,15 @@ function bundleWorker(context: BuildContext, configFile: string) {
2424
}
2525

2626

27-
export function bundleUpdate(event: string, filePath: string, context: BuildContext) {
27+
export function bundleUpdate(changedFiles: ChangedFile[], context: BuildContext) {
2828
if (context.bundler === BUNDLER_ROLLUP) {
29-
return rollupUpdate(event, filePath, context)
29+
return rollupUpdate(changedFiles, context)
3030
.catch(err => {
3131
throw new BuildError(err);
3232
});
3333
}
3434

35-
return webpackUpdate(event, filePath, context, null)
35+
return webpackUpdate(changedFiles, context, null)
3636
.catch(err => {
3737
if (err instanceof IgnorableError) {
3838
throw err;

src/dev-server/live-reload.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { ChangedFile } from '../util/interfaces';
12
import { hasDiagnostics } from '../logger/logger-diagnostics';
23
import * as path from 'path';
34
import * as tinylr from 'tiny-lr';
@@ -9,14 +10,13 @@ export function createLiveReloadServer(config: ServeConfig) {
910
const liveReloadServer = tinylr();
1011
liveReloadServer.listen(config.liveReloadPort, config.host);
1112

12-
function fileChange(filePath: string | string[]) {
13+
function fileChange(changedFiles: ChangedFile[]) {
1314
// only do a live reload if there are no diagnostics
1415
// the notification server takes care of showing diagnostics
1516
if (!hasDiagnostics(config.buildDir)) {
16-
const files = Array.isArray(filePath) ? filePath : [filePath];
1717
liveReloadServer.changed({
1818
body: {
19-
files: files.map(f => '/' + path.relative(config.wwwDir, f))
19+
files: changedFiles.map(changedFile => '/' + path.relative(config.wwwDir, changedFile.filePath))
2020
}
2121
});
2222
}
@@ -25,7 +25,7 @@ export function createLiveReloadServer(config: ServeConfig) {
2525
events.on(events.EventType.FileChange, fileChange);
2626

2727
events.on(events.EventType.ReloadApp, () => {
28-
fileChange('index.html');
28+
fileChange([{ event: 'change', ext: '.html', filePath: 'index.html'}]);
2929
});
3030
}
3131

src/lint.ts

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { access } from 'fs';
2-
import { BuildContext, TaskInfo } from './util/interfaces';
2+
import { BuildContext, ChangedFile, TaskInfo } from './util/interfaces';
33
import { BuildError } from './util/errors';
44
import { createProgram, findConfiguration, getFileNames } from 'tslint';
55
import { generateContext, getUserConfigFile } from './util/config';
@@ -31,12 +31,13 @@ export function lintWorker(context: BuildContext, configFile: string) {
3131
}
3232

3333

34-
export function lintUpdate(event: string, filePath: string, context: BuildContext) {
34+
export function lintUpdate(changedFiles: ChangedFile[], context: BuildContext) {
35+
const changedTypescriptFiles = changedFiles.filter(changedFile => changedFile.ext === '.ts');
3536
return new Promise(resolve => {
3637
// throw this in a promise for async fun, but don't let it hang anything up
3738
const workerConfig: LintWorkerConfig = {
3839
configFile: getUserConfigFile(context, taskInfo, null),
39-
filePath: filePath
40+
filePaths: changedTypescriptFiles.map(changedTypescriptFile => changedTypescriptFile.filePath)
4041
};
4142

4243
runWorker('lint', 'lintUpdateWorker', context, workerConfig);
@@ -49,7 +50,7 @@ export function lintUpdateWorker(context: BuildContext, workerConfig: LintWorker
4950
return getLintConfig(context, workerConfig.configFile).then(configFile => {
5051
// there's a valid tslint config, let's continue (but be quiet about it!)
5152
const program = createProgram(configFile, context.srcDir);
52-
return lintFile(context, program, workerConfig.filePath);
53+
return lintFiles(context, program, workerConfig.filePaths);
5354
}).catch(() => {
5455
});
5556
}
@@ -66,6 +67,13 @@ function lintApp(context: BuildContext, configFile: string) {
6667
return Promise.all(promises);
6768
}
6869

70+
function lintFiles(context: BuildContext, program: ts.Program, filePaths: string[]) {
71+
const promises: Promise<void>[] = [];
72+
for (const filePath of filePaths) {
73+
promises.push(lintFile(context, program, filePath));
74+
}
75+
return Promise.all(promises);
76+
}
6977

7078
function lintFile(context: BuildContext, program: ts.Program, filePath: string) {
7179
return new Promise((resolve) => {
@@ -162,5 +170,5 @@ const taskInfo: TaskInfo = {
162170

163171
export interface LintWorkerConfig {
164172
configFile: string;
165-
filePath: string;
173+
filePaths: string[];
166174
}

src/rollup.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BuildContext, BuildState, TaskInfo } from './util/interfaces';
1+
import { BuildContext, BuildState, ChangedFile, TaskInfo } from './util/interfaces';
22
import { BuildError } from './util/errors';
33
import { fillConfigDefaults, generateContext, getUserConfigFile, replacePathVars } from './util/config';
44
import { ionCompiler } from './plugins/ion-compiler';
@@ -25,7 +25,7 @@ export function rollup(context: BuildContext, configFile: string) {
2525
}
2626

2727

28-
export function rollupUpdate(event: string, filePath: string, context: BuildContext) {
28+
export function rollupUpdate(changedFiles: ChangedFile[], context: BuildContext) {
2929
const logger = new Logger('rollup update');
3030

3131
const configFile = getUserConfigFile(context, taskInfo, null);

src/sass.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { basename, dirname, join, sep } from 'path';
2-
import { BuildContext, BuildState, TaskInfo } from './util/interfaces';
2+
import { BuildContext, BuildState, ChangedFile, TaskInfo } from './util/interfaces';
33
import { BuildError } from './util/errors';
44
import { bundle } from './bundle';
55
import { ensureDirSync, readdirSync, writeFile } from 'fs-extra';
@@ -31,7 +31,7 @@ export function sass(context?: BuildContext, configFile?: string) {
3131
}
3232

3333

34-
export function sassUpdate(event: string, filePath: string, context: BuildContext) {
34+
export function sassUpdate(changedFiles: ChangedFile[], context: BuildContext) {
3535
const configFile = getUserConfigFile(context, taskInfo, null);
3636

3737
const logger = new Logger('sass update');
@@ -60,7 +60,7 @@ export function sassWorker(context: BuildContext, configFile: string) {
6060
return Promise.all(bundlePromise).then(() => {
6161
clearDiagnostics(context, DiagnosticsType.Sass);
6262

63-
const sassConfig: SassConfig = fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
63+
const sassConfig: SassConfig = getSassConfig(context, configFile);
6464

6565
// where the final css output file is saved
6666
if (!sassConfig.outFile) {
@@ -88,6 +88,10 @@ export function sassWorker(context: BuildContext, configFile: string) {
8888
});
8989
}
9090

91+
export function getSassConfig(context: BuildContext, configFile: string): SassConfig {
92+
configFile = getUserConfigFile(context, taskInfo, configFile);
93+
return fillConfigDefaults(configFile, taskInfo.defaultConfigFile);
94+
}
9195

9296
function generateSassData(context: BuildContext, sassConfig: SassConfig) {
9397
/**

0 commit comments

Comments
 (0)