Skip to content

Commit

Permalink
feat(tools): do not invoke tasks if not required
Browse files Browse the repository at this point in the history
In dev executes tasks only when a specific file type has changed.
Fix #1432.
  • Loading branch information
mgechev committed Oct 9, 2016
1 parent 8c11045 commit e558d18
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 77 deletions.
10 changes: 10 additions & 0 deletions tools/tasks/assets_task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Task } from './task';

export abstract class AssetsTask extends Task {
shallRun(files: String[]) {
return files.reduce((a, f) => {
return a || (!f.endsWith('.css') && !f.endsWith('.sass') &&
!f.endsWith('.scss') && !f.endsWith('.ts'));
}, false);
}
}
11 changes: 11 additions & 0 deletions tools/tasks/css_task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Task } from './task';

export abstract class CssTask extends Task {

shallRun(files: String[]) {
return files.some(f =>
f.endsWith('.css') || f.endsWith('.sass') || f.endsWith('.scss'));
}

}

25 changes: 15 additions & 10 deletions tools/tasks/seed/build.assets.dev.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import * as gulp from 'gulp';
import { join } from 'path';

import { AssetsTask } from '../assets_task';
import Config from '../../config';

/**
* Executes the build process, copying the assets located in `src/client` over to the appropriate
* `dist/dev` directory.
*/
export = () => {
let paths: string[] = [
join(Config.APP_SRC, '**'),
'!' + join(Config.APP_SRC, '**', '*.ts'),
'!' + join(Config.APP_SRC, '**', '*.scss'),
'!' + join(Config.APP_SRC, '**', '*.sass')
].concat(Config.TEMP_FILES.map((p) => { return '!' + p; }));
export =
class BuildAssetsTask extends AssetsTask {
run() {
let paths: string[] = [
join(Config.APP_SRC, '**'),
'!' + join(Config.APP_SRC, '**', '*.ts'),
'!' + join(Config.APP_SRC, '**', '*.scss'),
'!' + join(Config.APP_SRC, '**', '*.sass')
].concat(Config.TEMP_FILES.map((p) => { return '!' + p; }));

return gulp.src(paths)
.pipe(gulp.dest(Config.APP_DEST));
}
};

return gulp.src(paths)
.pipe(gulp.dest(Config.APP_DEST));
};
2 changes: 1 addition & 1 deletion tools/tasks/seed/build.bundle.rxjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

const Builder = require('systemjs-builder');

export = (done:any) => {
export = (done: any) => {
const options = {
normalize: true,
runtime: false,
Expand Down
13 changes: 12 additions & 1 deletion tools/tasks/seed/build.html_css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as util from 'gulp-util';
import { join } from 'path';

import Config from '../../config';
import { CssTask } from '../css_task';

const plugins = <any>gulpLoadPlugins();
const cleanCss = require('gulp-clean-css');
Expand Down Expand Up @@ -147,4 +148,14 @@ function processExternalCss() {
/**
* Executes the build process, processing the HTML and CSS files.
*/
export = () => merge(processComponentStylesheets(), prepareTemplates(), processExternalStylesheets());
export =
class BuildHtmlCss extends CssTask {

shallRun(files: String[]) {
return super.shallRun(files) || files.some(f => f.endsWith('.html'));
}

run() {
return merge(processComponentStylesheets(), prepareTemplates(), processExternalStylesheets());
}
};
107 changes: 56 additions & 51 deletions tools/tasks/seed/build.js.dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { join/*, sep, relative*/ } from 'path';

import Config from '../../config';
import { makeTsProject, templateLocals } from '../../utils';
import { TypeScriptTask } from '../typescript_task';

const plugins = <any>gulpLoadPlugins();

Expand All @@ -17,60 +18,64 @@ let typedBuildCounter = Config.TYPED_COMPILE_INTERVAL; // Always start with the
* Executes the build process, transpiling the TypeScript files (except the spec and e2e-spec files) for the development
* environment.
*/
export = () => {
let tsProject: any;
let typings = gulp.src([
Config.TOOLS_DIR + '/manual_typings/**/*.d.ts'
]);
let src = [
join(Config.APP_SRC, '**/*.ts'),
'!' + join(Config.APP_SRC, '**/*.spec.ts'),
'!' + join(Config.APP_SRC, '**/*.e2e-spec.ts'),
'!' + join(Config.TMP_DIR, `**/${Config.NG_FACTORY_FILE}.ts`)
];
export =
class BuildJsDev extends TypeScriptTask {
run() {
let tsProject: any;
let typings = gulp.src([
Config.TOOLS_DIR + '/manual_typings/**/*.d.ts'
]);
let src = [
join(Config.APP_SRC, '**/*.ts'),
'!' + join(Config.APP_SRC, '**/*.spec.ts'),
'!' + join(Config.APP_SRC, '**/*.e2e-spec.ts'),
'!' + join(Config.TMP_DIR, `**/${Config.NG_FACTORY_FILE}.ts`)
];

let projectFiles = gulp.src(src);
let result: any;
let isFullCompile = true;
let projectFiles = gulp.src(src);
let result: any;
let isFullCompile = true;

// Only do a typed build every X builds, otherwise do a typeless build to speed things up
if (typedBuildCounter < Config.TYPED_COMPILE_INTERVAL) {
isFullCompile = false;
tsProject = makeTsProject({isolatedModules: true});
projectFiles = projectFiles.pipe(plugins.cached());
util.log('Performing typeless TypeScript compile.');
} else {
tsProject = makeTsProject();
projectFiles = merge(typings, projectFiles);
}
// Only do a typed build every X builds, otherwise do a typeless build to speed things up
if (typedBuildCounter < Config.TYPED_COMPILE_INTERVAL) {
isFullCompile = false;
tsProject = makeTsProject({isolatedModules: true});
projectFiles = projectFiles.pipe(plugins.cached());
util.log('Performing typeless TypeScript compile.');
} else {
tsProject = makeTsProject();
projectFiles = merge(typings, projectFiles);
}

result = projectFiles
.pipe(plugins.plumber())
.pipe(plugins.sourcemaps.init())
.pipe(tsProject())
.on('error', () => {
typedBuildCounter = Config.TYPED_COMPILE_INTERVAL;
});
result = projectFiles
.pipe(plugins.plumber())
.pipe(plugins.sourcemaps.init())
.pipe(tsProject())
.on('error', () => {
typedBuildCounter = Config.TYPED_COMPILE_INTERVAL;
});

if (isFullCompile) {
typedBuildCounter = 0;
} else {
typedBuildCounter++;
}
if (isFullCompile) {
typedBuildCounter = 0;
} else {
typedBuildCounter++;
}

return result.js
.pipe(plugins.sourcemaps.write())
// Use for debugging with Webstorm/IntelliJ
// https://github.com/mgechev/angular2-seed/issues/1220
// .pipe(plugins.sourcemaps.write('.', {
// includeContent: false,
// sourceRoot: (file: any) =>
// relative(file.path, PROJECT_ROOT + '/' + APP_SRC).replace(sep, '/') + '/' + APP_SRC
// }))
.pipe(plugins.template(Object.assign(
templateLocals(), {
SYSTEM_CONFIG_DEV: jsonSystemConfig
return result.js
.pipe(plugins.sourcemaps.write())
// Use for debugging with Webstorm/IntelliJ
// https://github.com/mgechev/angular2-seed/issues/1220
// .pipe(plugins.sourcemaps.write('.', {
// includeContent: false,
// sourceRoot: (file: any) =>
// relative(file.path, PROJECT_ROOT + '/' + APP_SRC).replace(sep, '/') + '/' + APP_SRC
// }))
.pipe(plugins.template(Object.assign(
templateLocals(), {
SYSTEM_CONFIG_DEV: jsonSystemConfig
}
)))
.pipe(gulp.dest(Config.APP_DEST));
}
)))
.pipe(gulp.dest(Config.APP_DEST));
};
};

25 changes: 25 additions & 0 deletions tools/tasks/task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Base class for all tasks.
*/
export abstract class Task {
/**
* Override this task if you want to implement some custom
* task activation mechanism. By default each task will be always executed.
*
* @param {string[]} files A list of files changed since the previous watch.
*/
shallRun(files: string[]): boolean {
return true;
}

/**
* Implements your task behavior.
*
* @param {any} done A function which should be activated once your task completes.
* @return {ReadWriteStream | Promise<any> | void} This method can either return a promise
* which should be resolved once your task execution completes, a stream
* which should throw an end event once your task execution completes
* or nothing in case you will manually invoke the `done` method.
*/
abstract run(done?: any): any | Promise<any> | void;
}
9 changes: 9 additions & 0 deletions tools/tasks/typescript_task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Task } from './task';

export abstract class TypeScriptTask extends Task {
shallRun(files: String[]) {
return files.reduce((a, f) => {
return a || f.endsWith('.ts');
}, false);
}
}
28 changes: 28 additions & 0 deletions tools/utils/seed/code_change_tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,34 @@ import * as browserSync from 'browser-sync';

import Config from '../../config';

class ChangeFileManager {
private _files: string[] = [];
private _pristine = true;

get lastChangedFiles() {
return this._files.slice();
}

get pristine() {
return this._pristine;
}

addFile(file: string) {
this._pristine = false;
this._files.push(file);
}

addFiles(files: string[]) {
files.forEach(f => this.addFile(f));
}

clear() {
this._files = [];
}
}

export let changeFileManager = new ChangeFileManager();

/**
* Initialises BrowserSync with the configuration defined in seed.config.ts (or if overriden: project.config.ts).
*/
Expand Down
46 changes: 35 additions & 11 deletions tools/utils/seed/tasks_tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import * as isstream from 'isstream';
import { join } from 'path';
import * as tildify from 'tildify';

import { changeFileManager } from './code_change_tools';
import { Task } from '../../tasks/task';

/**
* Loads the tasks within the given path.
* @param {string} path - The path to load the tasks from.
Expand All @@ -14,6 +17,33 @@ export function loadTasks(path: string): void {
readDir(path, taskname => registerTask(taskname, path));
}

function normalizeTask(task: any, taskName: string) {
if (task instanceof Task) {
return task;
}
if (task.prototype && task.prototype instanceof Task) {
return new task();
}
if (typeof task === 'function') {
return new class AnonTask extends Task {
run(done: any) {
if (task.length > 0) {
return task(done);
}

const taskReturnedValue = task();
if (isstream(taskReturnedValue)) {
return taskReturnedValue;
}

done();
}
};
}
throw new Error(`${taskName} should be instance of the class
Task, a function or a class which extends Task`);
}

/**
* Registers the task by the given taskname and path.
* @param {string} taskname - The name of the task.
Expand All @@ -24,19 +54,13 @@ function registerTask(taskname: string, path: string): void {
util.log('Registering task', util.colors.yellow(tildify(TASK)));

gulp.task(taskname, (done: any) => {
const task = require(TASK);
if (task.length > 0) {
return task(done);
}
const task = normalizeTask(require(TASK), TASK);

const taskReturnedValue = task();
if (isstream(taskReturnedValue)) {
return taskReturnedValue;
if (changeFileManager.pristine || task.shallRun(changeFileManager.lastChangedFiles)) {
return task.run(done);
} else {
done();
}

// TODO: add promise handling if needed at some point.

done();
});
}

Expand Down
12 changes: 9 additions & 3 deletions tools/utils/seed/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { join } from 'path';
import * as runSequence from 'run-sequence';

import Config from '../../config';
import { changeFileManager } from './code_change_tools';
import { notifyLiveReload } from '../../utils';

const plugins = <any>gulpLoadPlugins();
Expand All @@ -17,8 +18,13 @@ export function watch(taskname: string) {
join(Config.APP_SRC,'**')
].concat(Config.TEMP_FILES.map((p) => { return '!'+p; }));

plugins.watch(paths, (e:any) =>
runSequence(taskname, () => notifyLiveReload(e))
);
plugins.watch(paths, (e: any) => {
changeFileManager.addFile(e.path);

runSequence(taskname, () => {
changeFileManager.clear();
notifyLiveReload(e);
});
});
};
}

0 comments on commit e558d18

Please sign in to comment.