aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAlex Tatiyants <atatiyan@gmail.com>2016-01-03 17:17:48 -0800
committerAlex Tatiyants <atatiyan@gmail.com>2016-01-03 17:17:48 -0800
commit5310ac7d8eb1838a6297117bc7f9fca70291f46a (patch)
tree28f54b184cb85f04e6d6720dd03258f3728fedde /tools
initial commit
Diffstat (limited to 'tools')
-rw-r--r--tools/config.ts101
-rw-r--r--tools/tasks/build.bundles.ts26
-rw-r--r--tools/tasks/build.deps.ts20
-rw-r--r--tools/tasks/build.docs.ts27
-rw-r--r--tools/tasks/build.fonts.dev.ts15
-rw-r--r--tools/tasks/build.html_css.prod.ts24
-rw-r--r--tools/tasks/build.img.dev.ts14
-rw-r--r--tools/tasks/build.index.ts34
-rw-r--r--tools/tasks/build.js.dev.ts25
-rw-r--r--tools/tasks/build.js.prod.ts22
-rw-r--r--tools/tasks/build.sass.dev.ts22
-rw-r--r--tools/tasks/build.test.ts21
-rw-r--r--tools/tasks/check.versions.ts35
-rw-r--r--tools/tasks/clean.ts34
-rw-r--r--tools/tasks/karma.start.ts11
-rw-r--r--tools/tasks/npm.ts5
-rw-r--r--tools/tasks/serve.docs.ts7
-rw-r--r--tools/tasks/server.start.ts7
-rw-r--r--tools/tasks/tsd.ts7
-rw-r--r--tools/tasks/tslint.ts21
-rw-r--r--tools/tasks/watch.dev.ts8
-rw-r--r--tools/tasks/watch.serve.ts12
-rw-r--r--tools/tasks/watch.test.ts8
-rw-r--r--tools/typings/connect-livereload.d.ts5
-rw-r--r--tools/typings/gulp-load-plugins.d.ts42
-rw-r--r--tools/typings/karma.d.ts12
-rw-r--r--tools/typings/merge-stream.d.ts8
-rw-r--r--tools/typings/open.d.ts8
-rw-r--r--tools/typings/run-sequence.d.ts5
-rw-r--r--tools/typings/slash.d.ts5
-rw-r--r--tools/typings/systemjs-builder.d.ts10
-rw-r--r--tools/typings/tiny-lr.d.ts10
-rw-r--r--tools/typings/yargs.d.ts9
-rw-r--r--tools/utils.ts11
-rw-r--r--tools/utils/server.ts45
-rw-r--r--tools/utils/tasks_tools.ts64
-rw-r--r--tools/utils/template-injectables.ts25
-rw-r--r--tools/utils/template-locals.ts13
38 files changed, 778 insertions, 0 deletions
diff --git a/tools/config.ts b/tools/config.ts
new file mode 100644
index 0000000..9c5559a
--- /dev/null
+++ b/tools/config.ts
@@ -0,0 +1,101 @@
+import {readFileSync} from 'fs';
+import {argv} from 'yargs';
+
+
+// --------------
+// Configuration.
+export const ENV = argv['env'] || 'dev';
+export const DEBUG = argv['debug'] || false;
+export const PORT = argv['port'] || 5555;
+export const LIVE_RELOAD_PORT = argv['reload-port'] || 4002;
+export const DOCS_PORT = argv['docs-port'] || 4003;
+export const APP_BASE = argv['base'] || '/';
+
+export const APP_TITLE = 'My Angular2 App';
+
+export const APP_SRC = 'app';
+export const ASSETS_SRC = `${APP_SRC}/assets`;
+
+export const TOOLS_DIR = 'tools';
+export const TMP_DIR = 'tmp';
+export const TEST_DEST = 'test';
+export const DOCS_DEST = 'docs';
+export const APP_DEST = `dist/${ENV}`;
+export const ASSETS_DEST = `${APP_DEST}/assets`;
+export const BUNDLES_DEST = `${APP_DEST}/bundles`;
+export const CSS_DEST = `${APP_DEST}/css`;
+export const FONTS_DEST = `${APP_DEST}/fonts`;
+export const LIB_DEST = `${APP_DEST}/lib`;
+export const APP_ROOT = ENV === 'dev' ? `${APP_BASE}${APP_DEST}/` : `${APP_BASE}`;
+export const VERSION = appVersion();
+
+export const VERSION_NPM = '2.14.7';
+export const VERSION_NODE = '4.0.0';
+
+// Declare NPM dependencies (Note that globs should not be injected).
+export const NPM_DEPENDENCIES = [
+ { src: 'systemjs/dist/system-polyfills.js', dest: LIB_DEST },
+
+ { src: 'es6-shim/es6-shim.min.js', inject: 'shims', dest: LIB_DEST },
+ { src: 'reflect-metadata/Reflect.js', inject: 'shims', dest: LIB_DEST },
+ { src: 'systemjs/dist/system.src.js', inject: 'shims', dest: LIB_DEST },
+ { src: 'angular2/bundles/angular2-polyfills.js', inject: 'shims', dest: LIB_DEST },
+
+ // Faster dev page load
+ { src: 'rxjs/bundles/Rx.min.js', inject: 'libs', dest: LIB_DEST },
+ { src: 'angular2/bundles/angular2.min.js', inject: 'libs', dest: LIB_DEST },
+ { src: 'angular2/bundles/router.js', inject: 'libs', dest: LIB_DEST }, // use router.min.js with alpha47
+ { src: 'angular2/bundles/http.min.js', inject: 'libs', dest: LIB_DEST },
+
+ { src: 'lodash/index.js', inject: 'libs', dest: LIB_DEST },
+ { src: 'moment/moment.js', inject: 'libs', dest: LIB_DEST }
+];
+
+// Declare local files that needs to be injected
+export const APP_ASSETS = [
+ { src: `${ASSETS_SRC}/css/styles.css`, inject: true, dest: CSS_DEST}
+];
+
+NPM_DEPENDENCIES
+ .filter(d => !/\*/.test(d.src)) // Skip globs
+ .forEach(d => d.src = require.resolve(d.src));
+
+export const DEPENDENCIES = NPM_DEPENDENCIES.concat(APP_ASSETS);
+
+
+// ----------------
+// SystemsJS Configuration.
+const SYSTEM_CONFIG_DEV = {
+ defaultJSExtensions: true,
+ paths: {
+ 'bootstrap': `${APP_ROOT}bootstrap`,
+ '*': `${APP_BASE}node_modules/*`
+ }
+};
+
+const SYSTEM_CONFIG_PROD = {
+ defaultJSExtensions: true,
+ bundles: {
+ 'bundles/app': ['bootstrap']
+ }
+};
+
+export const SYSTEM_CONFIG = ENV === 'dev' ? SYSTEM_CONFIG_DEV : SYSTEM_CONFIG_PROD;
+
+// This is important to keep clean module names as 'module name == module uri'.
+export const SYSTEM_CONFIG_BUILDER = {
+ defaultJSExtensions: true,
+ paths: {
+ '*': `${TMP_DIR}/*`,
+ 'angular2/*': 'node_modules/angular2/*',
+ 'rxjs/*': 'node_modules/rxjs/*'
+ }
+};
+
+
+// --------------
+// Private.
+function appVersion(): number|string {
+ var pkg = JSON.parse(readFileSync('package.json').toString());
+ return pkg.version;
+}
diff --git a/tools/tasks/build.bundles.ts b/tools/tasks/build.bundles.ts
new file mode 100644
index 0000000..52eb381
--- /dev/null
+++ b/tools/tasks/build.bundles.ts
@@ -0,0 +1,26 @@
+import {parallel} from 'async';
+import {join} from 'path';
+import * as Builder from 'systemjs-builder';
+import {BUNDLES_DEST, SYSTEM_CONFIG_BUILDER} from '../config';
+
+const BUNDLE_OPTS = {
+ minify: true,
+ sourceMaps: true,
+ format: 'cjs'
+};
+
+export = function bundles(gulp, plugins) {
+ return function (done) {
+ let builder = new Builder(SYSTEM_CONFIG_BUILDER);
+
+ parallel([
+ bundleApp
+ ], () => done());
+
+ function bundleApp(done) {
+ builder.bundle(
+ 'bootstrap - angular2/*',
+ join(BUNDLES_DEST, 'app.js'), BUNDLE_OPTS).then(done);
+ }
+ };
+};
diff --git a/tools/tasks/build.deps.ts b/tools/tasks/build.deps.ts
new file mode 100644
index 0000000..071f4cd
--- /dev/null
+++ b/tools/tasks/build.deps.ts
@@ -0,0 +1,20 @@
+import * as merge from 'merge-stream';
+import {DEPENDENCIES} from '../config';
+
+export = function buildDepsProd(gulp, plugins) {
+ return function () {
+ let stream = merge();
+
+ DEPENDENCIES.forEach(dep => {
+ stream.add(addStream(dep));
+ });
+
+ return stream;
+
+ function addStream(dep) {
+ let stream = gulp.src(dep.src);
+ stream.pipe(gulp.dest(dep.dest));
+ return stream;
+ }
+ };
+};
diff --git a/tools/tasks/build.docs.ts b/tools/tasks/build.docs.ts
new file mode 100644
index 0000000..a464c67
--- /dev/null
+++ b/tools/tasks/build.docs.ts
@@ -0,0 +1,27 @@
+import {join} from 'path';
+import {APP_SRC, APP_TITLE, DOCS_DEST} from '../config';
+
+export = function buildDocs(gulp, plugins, option) {
+ return function() {
+
+ let src = [
+ join(APP_SRC, '**/*.ts'),
+ '!' + join(APP_SRC, '**/*_spec.ts')
+ ];
+
+ return gulp.src(src)
+ .pipe(plugins.typedoc({
+ // TypeScript options (see typescript docs)
+ module: 'commonjs',
+ target: 'es5',
+ includeDeclarations: true,
+ // Output options (see typedoc docs)
+ out: DOCS_DEST,
+ json: join(DOCS_DEST , 'data/docs.json' ),
+ name: APP_TITLE,
+ ignoreCompilerErrors: false,
+ experimentalDecorators: true,
+ version: true
+ }));
+ };
+}
diff --git a/tools/tasks/build.fonts.dev.ts b/tools/tasks/build.fonts.dev.ts
new file mode 100644
index 0000000..6aa0b92
--- /dev/null
+++ b/tools/tasks/build.fonts.dev.ts
@@ -0,0 +1,15 @@
+import {join} from 'path';
+import {APP_SRC, APP_DEST} from '../config';
+
+export = function buildFontsDev(gulp, plugins) {
+ return function () {
+ return gulp.src([
+ join(APP_SRC, '**/*.eot'),
+ join(APP_SRC, '**/*.ttf'),
+ join(APP_SRC, '**/*.woff'),
+ join(APP_SRC, '**/*.woff2'),
+ join(APP_SRC, '**/*.otf')
+ ])
+ .pipe(gulp.dest(APP_DEST));
+ };
+}
diff --git a/tools/tasks/build.html_css.prod.ts b/tools/tasks/build.html_css.prod.ts
new file mode 100644
index 0000000..93d0aa9
--- /dev/null
+++ b/tools/tasks/build.html_css.prod.ts
@@ -0,0 +1,24 @@
+import * as merge from 'merge-stream';
+import {join} from 'path';
+import {APP_SRC, TMP_DIR} from '../config';
+
+// const HTML_MINIFIER_OPTS = { empty: true };
+
+export = function buildJSDev(gulp, plugins) {
+ return function () {
+
+ return merge(minifyHtml(), minifyCss());
+
+ function minifyHtml() {
+ return gulp.src(join(APP_SRC, '**/*.html'))
+ // .pipe(plugins.minifyHtml(HTML_MINIFIER_OPTS))
+ .pipe(gulp.dest(TMP_DIR));
+ }
+
+ function minifyCss() {
+ return gulp.src(join(APP_SRC, '**/*.css'))
+ .pipe(plugins.minifyCss())
+ .pipe(gulp.dest(TMP_DIR));
+ }
+ };
+};
diff --git a/tools/tasks/build.img.dev.ts b/tools/tasks/build.img.dev.ts
new file mode 100644
index 0000000..28a7897
--- /dev/null
+++ b/tools/tasks/build.img.dev.ts
@@ -0,0 +1,14 @@
+import {join} from 'path';
+import {APP_SRC, APP_DEST} from '../config';
+
+export = function buildImagesDev(gulp, plugins) {
+ return function () {
+ return gulp.src([
+ join(APP_SRC, '**/*.gif'),
+ join(APP_SRC, '**/*.jpg'),
+ join(APP_SRC, '**/*.png'),
+ join(APP_SRC, '**/*.svg')
+ ])
+ .pipe(gulp.dest(APP_DEST));
+ };
+}
diff --git a/tools/tasks/build.index.ts b/tools/tasks/build.index.ts
new file mode 100644
index 0000000..bbf98ee
--- /dev/null
+++ b/tools/tasks/build.index.ts
@@ -0,0 +1,34 @@
+import {join, sep} from 'path';
+import {APP_SRC, APP_DEST, DEPENDENCIES, ENV} from '../config';
+import {transformPath, templateLocals} from '../utils';
+
+export = function buildIndexDev(gulp, plugins) {
+ return function () {
+ return gulp.src(join(APP_SRC, 'index.html'))
+ // NOTE: There might be a way to pipe in loop.
+ .pipe(inject('shims'))
+ .pipe(inject('libs'))
+ .pipe(inject())
+ .pipe(plugins.template(templateLocals()))
+ .pipe(gulp.dest(APP_DEST));
+ };
+
+
+ function inject(name?: string) {
+ return plugins.inject(gulp.src(getInjectablesDependenciesRef(name), { read: false }), {
+ name,
+ transform: transformPath(plugins, 'dev')
+ });
+ }
+
+ function getInjectablesDependenciesRef(name?: string) {
+ return DEPENDENCIES
+ .filter(dep => dep['inject'] && dep['inject'] === (name || true))
+ .map(mapPath);
+ }
+
+ function mapPath(dep) {
+ let prodPath = join(dep.dest, dep.src.split(sep).pop());
+ return ('prod' === ENV ? prodPath : dep.src );
+ }
+};
diff --git a/tools/tasks/build.js.dev.ts b/tools/tasks/build.js.dev.ts
new file mode 100644
index 0000000..dfe9539
--- /dev/null
+++ b/tools/tasks/build.js.dev.ts
@@ -0,0 +1,25 @@
+import {join} from 'path';
+import {APP_SRC, APP_DEST} from '../config';
+import {templateLocals, tsProjectFn} from '../utils';
+
+export = function buildJSDev(gulp, plugins) {
+ let tsProject = tsProjectFn(plugins);
+ return function () {
+ let src = [
+ join(APP_SRC, '**/*.ts'),
+ '!' + join(APP_SRC, '**/*_spec.ts')
+ ];
+
+ let result = gulp.src(src)
+ .pipe(plugins.plumber())
+ // Won't be required for non-production build after the change
+ .pipe(plugins.inlineNg2Template({ base: APP_SRC }))
+ .pipe(plugins.sourcemaps.init())
+ .pipe(plugins.typescript(tsProject));
+
+ return result.js
+ .pipe(plugins.sourcemaps.write())
+ .pipe(plugins.template(templateLocals()))
+ .pipe(gulp.dest(APP_DEST));
+ };
+};
diff --git a/tools/tasks/build.js.prod.ts b/tools/tasks/build.js.prod.ts
new file mode 100644
index 0000000..54ed21a
--- /dev/null
+++ b/tools/tasks/build.js.prod.ts
@@ -0,0 +1,22 @@
+import {join} from 'path';
+import {APP_SRC, TMP_DIR} from '../config';
+import {templateLocals, tsProjectFn} from '../utils';
+
+export = function buildJSDev(gulp, plugins) {
+ return function () {
+ let tsProject = tsProjectFn(plugins);
+ let src = [
+ join(APP_SRC, '**/*.ts'),
+ '!' + join(APP_SRC, '**/*_spec.ts')
+ ];
+
+ let result = gulp.src(src)
+ .pipe(plugins.plumber())
+ .pipe(plugins.inlineNg2Template({ base: TMP_DIR }))
+ .pipe(plugins.typescript(tsProject));
+
+ return result.js
+ .pipe(plugins.template(templateLocals()))
+ .pipe(gulp.dest(TMP_DIR));
+ };
+};
diff --git a/tools/tasks/build.sass.dev.ts b/tools/tasks/build.sass.dev.ts
new file mode 100644
index 0000000..a2127be
--- /dev/null
+++ b/tools/tasks/build.sass.dev.ts
@@ -0,0 +1,22 @@
+import {join} from 'path';
+import {APP_SRC} from '../config';
+
+export = function buildSassDev(gulp, plugins, option) {
+ return function() {
+ return gulp.src(join(APP_SRC, '**', '*.scss'))
+ .pipe(plugins.plumber({
+ // this allows gulp not to crash on sass compilation errors
+ errorHandler: function(error) {
+ console.log(error.message);
+ this.emit('end');
+ }
+ }))
+ .pipe(plugins.compass({
+ // config_file: './config.rb',
+ style: 'compressed',
+ css: 'app/assets/css',
+ sass: join(APP_SRC, 'assets/sass'),
+ }))
+ .pipe(gulp.dest(join(APP_SRC, 'assets')));
+ };
+}
diff --git a/tools/tasks/build.test.ts b/tools/tasks/build.test.ts
new file mode 100644
index 0000000..3e089cd
--- /dev/null
+++ b/tools/tasks/build.test.ts
@@ -0,0 +1,21 @@
+import {join} from 'path';
+import {APP_SRC, TEST_DEST} from '../config';
+import {tsProjectFn} from '../utils';
+
+export = function buildTest(gulp, plugins) {
+ return function () {
+ let tsProject = tsProjectFn(plugins);
+ let src = [
+ join(APP_SRC, '**/*.ts'),
+ '!' + join(APP_SRC, 'bootstrap.ts')
+ ];
+
+ let result = gulp.src(src)
+ .pipe(plugins.plumber())
+ .pipe(plugins.inlineNg2Template({ base: APP_SRC }))
+ .pipe(plugins.typescript(tsProject));
+
+ return result.js
+ .pipe(gulp.dest(TEST_DEST));
+ };
+};
diff --git a/tools/tasks/check.versions.ts b/tools/tasks/check.versions.ts
new file mode 100644
index 0000000..211d5ed
--- /dev/null
+++ b/tools/tasks/check.versions.ts
@@ -0,0 +1,35 @@
+import {VERSION_NPM, VERSION_NODE} from '../config';
+
+function reportError(message: string) {
+ console.error(require('chalk').white.bgRed.bold(message));
+ process.exit(1);
+}
+
+module.exports = function check(gulp, plugins) {
+ return function () {
+ let exec = require('child_process').exec;
+ let semver = require('semver');
+
+ exec('npm --version',
+ function (error, stdout, stderr) {
+ if (error !== null) {
+ reportError('npm preinstall error: ' + error + stderr);
+ }
+
+ if (!semver.gte(stdout, VERSION_NPM)) {
+ reportError('NPM is not in required version! Required is ' + VERSION_NPM + ' and you\'re using ' + stdout);
+ }
+ });
+
+ exec('node --version',
+ function (error, stdout, stderr) {
+ if (error !== null) {
+ reportError('npm preinstall error: ' + error + stderr);
+ }
+
+ if (!semver.gte(stdout, VERSION_NODE)) {
+ reportError('NODE is not in required version! Required is ' + VERSION_NODE + ' and you\'re using ' + stdout);
+ }
+ });
+ };
+};
diff --git a/tools/tasks/clean.ts b/tools/tasks/clean.ts
new file mode 100644
index 0000000..9f0ebf2
--- /dev/null
+++ b/tools/tasks/clean.ts
@@ -0,0 +1,34 @@
+import * as async from 'async';
+import * as del from 'del';
+import {APP_DEST, TEST_DEST, TMP_DIR} from '../config';
+
+export = function clean(gulp, plugins, option) {
+ return function (done) {
+
+ switch(option) {
+ case 'all' : cleanAll(done); break;
+ case 'dist' : cleanDist(done); break;
+ case 'test' : cleanTest(done); break;
+ case 'tmp' : cleanTmp(done); break;
+ default: done();
+ }
+
+ };
+};
+
+function cleanAll(done) {
+ async.parallel([
+ cleanDist,
+ cleanTest,
+ cleanTmp
+ ], done);
+}
+function cleanDist(done) {
+ del(APP_DEST, done);
+}
+function cleanTest(done) {
+ del(TEST_DEST, done);
+}
+function cleanTmp(done) {
+ del(TMP_DIR, done);
+}
diff --git a/tools/tasks/karma.start.ts b/tools/tasks/karma.start.ts
new file mode 100644
index 0000000..313aacd
--- /dev/null
+++ b/tools/tasks/karma.start.ts
@@ -0,0 +1,11 @@
+import * as karma from 'karma';
+import {join} from 'path';
+
+export = function karmaStart() {
+ return function (done) {
+ new (<any>karma).Server({
+ configFile: join(process.cwd(), 'karma.conf.js'),
+ singleRun: true
+ }).start(done);
+ };
+};
diff --git a/tools/tasks/npm.ts b/tools/tasks/npm.ts
new file mode 100644
index 0000000..2ab5e76
--- /dev/null
+++ b/tools/tasks/npm.ts
@@ -0,0 +1,5 @@
+export = function npm(gulp, plugins) {
+ return plugins.shell.task([
+ 'npm prune'
+ ]);
+};
diff --git a/tools/tasks/serve.docs.ts b/tools/tasks/serve.docs.ts
new file mode 100644
index 0000000..2aa5f05
--- /dev/null
+++ b/tools/tasks/serve.docs.ts
@@ -0,0 +1,7 @@
+import {serveDocs} from '../utils';
+
+export = function serverStart(gulp, plugins) {
+ return function () {
+ serveDocs();
+ };
+};
diff --git a/tools/tasks/server.start.ts b/tools/tasks/server.start.ts
new file mode 100644
index 0000000..e733e92
--- /dev/null
+++ b/tools/tasks/server.start.ts
@@ -0,0 +1,7 @@
+import {serveSPA} from '../utils';
+
+export = function serverStart(gulp, plugins) {
+ return function () {
+ serveSPA();
+ };
+};
diff --git a/tools/tasks/tsd.ts b/tools/tasks/tsd.ts
new file mode 100644
index 0000000..bf38ad0
--- /dev/null
+++ b/tools/tasks/tsd.ts
@@ -0,0 +1,7 @@
+export = function tsd(gulp, plugins) {
+ return plugins.shell.task([
+ 'tsd reinstall --clean',
+ 'tsd link',
+ 'tsd rebundle'
+ ]);
+};
diff --git a/tools/tasks/tslint.ts b/tools/tasks/tslint.ts
new file mode 100644
index 0000000..ccea264
--- /dev/null
+++ b/tools/tasks/tslint.ts
@@ -0,0 +1,21 @@
+import {join} from 'path';
+import {APP_SRC, TOOLS_DIR} from '../config';
+
+export = function tslint(gulp, plugins) {
+ return function () {
+ let src = [
+ join(APP_SRC, '**/*.ts'),
+ '!' + join(APP_SRC, '**/*.d.ts'),
+ join(TOOLS_DIR, '**/*.ts'),
+ '!' + join(TOOLS_DIR, '**/*.d.ts')
+ ];
+
+ return gulp.src(src)
+ .pipe(plugins.tslint())
+ .pipe(plugins.tslint.report(plugins.tslintStylish, {
+ emitError: false,
+ sort: true,
+ bell: true
+ }));
+ };
+};
diff --git a/tools/tasks/watch.dev.ts b/tools/tasks/watch.dev.ts
new file mode 100644
index 0000000..bfa37a3
--- /dev/null
+++ b/tools/tasks/watch.dev.ts
@@ -0,0 +1,8 @@
+import {join} from 'path';
+import {APP_SRC} from '../config';
+
+export = function watchDev(gulp, plugins) {
+ return function () {
+ plugins.watch(join(APP_SRC, '**/*'), () => gulp.start('build.dev'));
+ };
+};
diff --git a/tools/tasks/watch.serve.ts b/tools/tasks/watch.serve.ts
new file mode 100644
index 0000000..2bb18f1
--- /dev/null
+++ b/tools/tasks/watch.serve.ts
@@ -0,0 +1,12 @@
+import * as runSequence from 'run-sequence';
+import {join} from 'path';
+import {APP_SRC} from '../config';
+import {notifyLiveReload} from '../utils';
+
+export = function watchServe(gulp, plugins) {
+ return function () {
+ plugins.watch(join(APP_SRC, '**'), e =>
+ runSequence('build.dev', () => notifyLiveReload(e))
+ );
+ };
+};
diff --git a/tools/tasks/watch.test.ts b/tools/tasks/watch.test.ts
new file mode 100644
index 0000000..2a8b55b
--- /dev/null
+++ b/tools/tasks/watch.test.ts
@@ -0,0 +1,8 @@
+import {join} from 'path';
+import {APP_SRC} from '../config';
+
+export = function watchTest(gulp, plugins) {
+ return function () {
+ plugins.watch(join(APP_SRC, '**/*.ts'), () => gulp.start('build.test'));
+ };
+};
diff --git a/tools/typings/connect-livereload.d.ts b/tools/typings/connect-livereload.d.ts
new file mode 100644
index 0000000..1b1ef21
--- /dev/null
+++ b/tools/typings/connect-livereload.d.ts
@@ -0,0 +1,5 @@
+declare module 'connect-livereload' {
+ function connectLivereload(options?: any): any;
+ module connectLivereload {}
+ export = connectLivereload;
+}
diff --git a/tools/typings/gulp-load-plugins.d.ts b/tools/typings/gulp-load-plugins.d.ts
new file mode 100644
index 0000000..809c305
--- /dev/null
+++ b/tools/typings/gulp-load-plugins.d.ts
@@ -0,0 +1,42 @@
+// Type definitions for gulp-load-plugins
+// Project: https://github.com/jackfranklin/gulp-load-plugins
+// Definitions by: Joe Skeen <http://github.com/joeskeen>
+// Definitions: https://github.com/borisyankov/DefinitelyTyped
+
+// Does not support ES2015 import (import * as open from 'open').
+
+/** Loads in any gulp plugins and attaches them to an object, freeing you up from having to manually require each gulp plugin. */
+declare module 'gulp-load-plugins' {
+
+ interface IOptions {
+ /** the glob(s) to search for, default ['gulp-*', 'gulp.*'] */
+ pattern?: string[];
+ /** where to find the plugins, searched up from process.cwd(), default 'package.json' */
+ config?: string;
+ /** which keys in the config to look within, default ['dependencies', 'devDependencies', 'peerDependencies'] */
+ scope?: string[];
+ /** what to remove from the name of the module when adding it to the context, default /^gulp(-|\.)/ */
+ replaceString?: RegExp;
+ /** if true, transforms hyphenated plugin names to camel case, default true */
+ camelize?: boolean;
+ /** whether the plugins should be lazy loaded on demand, default true */
+ lazy?: boolean;
+ /** a mapping of plugins to rename, the key being the NPM name of the package, and the value being an alias you define */
+ rename?: IPluginNameMappings;
+ }
+
+ interface IPluginNameMappings {
+ [npmPackageName: string]: string;
+ }
+
+ /** Loads in any gulp plugins and attaches them to an object, freeing you up from having to manually require each gulp plugin. */
+ function gulpLoadPlugins<T extends IGulpPlugins>(options?: IOptions): T;
+ module gulpLoadPlugins {}
+ export = gulpLoadPlugins;
+}
+
+/**
+ * Extend this interface to use Gulp plugins in your gulpfile.js
+ */
+interface IGulpPlugins {
+}
diff --git a/tools/typings/karma.d.ts b/tools/typings/karma.d.ts
new file mode 100644
index 0000000..5d2cd42
--- /dev/null
+++ b/tools/typings/karma.d.ts
@@ -0,0 +1,12 @@
+// Use this minimalistic definition file as bluebird dependency
+// generate a lot of errors.
+
+declare module 'karma' {
+ var karma: IKarma;
+ export = karma;
+ interface IKarma {
+ server: {
+ start(options: any, callback: Function): void
+ };
+ }
+}
diff --git a/tools/typings/merge-stream.d.ts b/tools/typings/merge-stream.d.ts
new file mode 100644
index 0000000..7425d35
--- /dev/null
+++ b/tools/typings/merge-stream.d.ts
@@ -0,0 +1,8 @@
+declare module 'merge-stream' {
+ function mergeStream(...streams: NodeJS.ReadWriteStream[]): MergeStream;
+ interface MergeStream extends NodeJS.ReadWriteStream {
+ add(stream: NodeJS.ReadWriteStream): MergeStream;
+ }
+ module mergeStream {}
+ export = mergeStream;
+} \ No newline at end of file
diff --git a/tools/typings/open.d.ts b/tools/typings/open.d.ts
new file mode 100644
index 0000000..89d4b76
--- /dev/null
+++ b/tools/typings/open.d.ts
@@ -0,0 +1,8 @@
+// https://github.com/borisyankov/DefinitelyTyped/tree/master/open
+// Does not support ES2015 import (import * as open from 'open').
+
+declare module 'open' {
+ function open(target: string, app?: string): void;
+ module open {}
+ export = open;
+}
diff --git a/tools/typings/run-sequence.d.ts b/tools/typings/run-sequence.d.ts
new file mode 100644
index 0000000..c4fada3
--- /dev/null
+++ b/tools/typings/run-sequence.d.ts
@@ -0,0 +1,5 @@
+declare module 'run-sequence' {
+ function runSequence(...args: any[]): void;
+ module runSequence {}
+ export = runSequence;
+}
diff --git a/tools/typings/slash.d.ts b/tools/typings/slash.d.ts
new file mode 100644
index 0000000..10ec0b5
--- /dev/null
+++ b/tools/typings/slash.d.ts
@@ -0,0 +1,5 @@
+declare module 'slash' {
+ function slash(path: string): string;
+ module slash {}
+ export = slash;
+}
diff --git a/tools/typings/systemjs-builder.d.ts b/tools/typings/systemjs-builder.d.ts
new file mode 100644
index 0000000..4644816
--- /dev/null
+++ b/tools/typings/systemjs-builder.d.ts
@@ -0,0 +1,10 @@
+declare module 'systemjs-builder' {
+ class Builder {
+ constructor(configObject?: any, baseUrl?: string, configPath?: string);
+ bundle(source: string, target: string, options?: any): Promise<any>;
+ buildStatic(source: string, target: string, options?: any): Promise<any>;
+ }
+
+ module Builder {}
+ export = Builder;
+}
diff --git a/tools/typings/tiny-lr.d.ts b/tools/typings/tiny-lr.d.ts
new file mode 100644
index 0000000..8a492d5
--- /dev/null
+++ b/tools/typings/tiny-lr.d.ts
@@ -0,0 +1,10 @@
+declare module 'tiny-lr' {
+ function tinylr(): ITinylr;
+ module tinylr {}
+ export = tinylr;
+
+ interface ITinylr {
+ changed(options: any): void;
+ listen(port: number): void;
+ }
+}
diff --git a/tools/typings/yargs.d.ts b/tools/typings/yargs.d.ts
new file mode 100644
index 0000000..8351942
--- /dev/null
+++ b/tools/typings/yargs.d.ts
@@ -0,0 +1,9 @@
+declare module 'yargs' {
+ var yargs: IYargs;
+ export = yargs;
+
+ // Minimalistic but serves the usage in the seed.
+ interface IYargs {
+ argv: any;
+ }
+}
diff --git a/tools/utils.ts b/tools/utils.ts
new file mode 100644
index 0000000..81e3bb2
--- /dev/null
+++ b/tools/utils.ts
@@ -0,0 +1,11 @@
+export * from './utils/template-injectables';
+export * from './utils/template-locals';
+export * from './utils/server';
+export * from './utils/tasks_tools';
+
+
+export function tsProjectFn(plugins) {
+ return plugins.typescript.createProject('tsconfig.json', {
+ typescript: require('typescript')
+ });
+}
diff --git a/tools/utils/server.ts b/tools/utils/server.ts
new file mode 100644
index 0000000..9e185d3
--- /dev/null
+++ b/tools/utils/server.ts
@@ -0,0 +1,45 @@
+import * as connectLivereload from 'connect-livereload';
+import * as express from 'express';
+import * as tinylrFn from 'tiny-lr';
+import * as openResource from 'open';
+import * as serveStatic from 'serve-static';
+import {resolve} from 'path';
+import {APP_BASE, APP_DEST, DOCS_DEST, LIVE_RELOAD_PORT, DOCS_PORT, PORT} from '../config';
+
+let tinylr = tinylrFn();
+
+
+export function serveSPA() {
+ let server = express();
+ tinylr.listen(LIVE_RELOAD_PORT);
+
+ server.use(
+ APP_BASE,
+ connectLivereload({ port: LIVE_RELOAD_PORT }),
+ express.static(process.cwd())
+ );
+
+ server.listen(PORT, () =>
+ openResource('http://localhost:' + PORT + APP_BASE + APP_DEST)
+ );
+}
+
+export function notifyLiveReload(e) {
+ let fileName = e.path;
+ tinylr.changed({
+ body: { files: [fileName] }
+ });
+}
+
+export function serveDocs() {
+ let server = express();
+
+ server.use(
+ APP_BASE,
+ serveStatic(resolve(process.cwd(), DOCS_DEST))
+ );
+
+ server.listen(DOCS_PORT, () =>
+ openResource('http://localhost:' + DOCS_PORT + APP_BASE)
+ );
+}
diff --git a/tools/utils/tasks_tools.ts b/tools/utils/tasks_tools.ts
new file mode 100644
index 0000000..af1a069
--- /dev/null
+++ b/tools/utils/tasks_tools.ts
@@ -0,0 +1,64 @@
+import * as gulp from 'gulp';
+import * as util from 'gulp-util';
+import * as chalk from 'chalk';
+import * as gulpLoadPlugins from 'gulp-load-plugins';
+import * as _runSequence from 'run-sequence';
+import {readdirSync, existsSync, lstatSync} from 'fs';
+import {join} from 'path';
+import {TOOLS_DIR} from '../config';
+
+const TASKS_PATH = join(TOOLS_DIR, 'tasks');
+
+// NOTE: Remove if no issues with runSequence function below.
+// export function loadTasks(): void {
+// scanDir(TASKS_PATH, (taskname) => registerTask(taskname));
+// }
+
+export function task(taskname: string, option?: string) {
+ util.log('Loading task', chalk.yellow(taskname, option || ''));
+ return require(join('..', 'tasks', taskname))(gulp, gulpLoadPlugins(), option);
+}
+
+export function runSequence(...sequence: any[]) {
+ let tasks = [];
+ let _sequence = sequence.slice(0);
+ sequence.pop();
+
+ scanDir(TASKS_PATH, taskname => tasks.push(taskname));
+
+ sequence.forEach(task => {
+ if (tasks.indexOf(task) > -1) { registerTask(task); }
+ });
+
+ return _runSequence(..._sequence);
+}
+
+// ----------
+// Private.
+
+function registerTask(taskname: string, filename?: string, option: string = ''): void {
+ gulp.task(taskname, task(filename || taskname, option));
+}
+
+// TODO: add recursive lookup ? or enforce pattern file + folder (ie ./tools/utils & ./tools/utils.ts )
+function scanDir(root: string, cb: (taskname: string) => void) {
+ if (!existsSync(root)) return;
+
+ walk(root);
+
+ function walk(path) {
+ let files = readdirSync(path);
+ for (let i = 0; i < files.length; i += 1) {
+ let file = files[i];
+ let curPath = join(path, file);
+ // if (lstatSync(curPath).isDirectory()) { // recurse
+ // path = file;
+ // walk(curPath);
+ // }
+ if (lstatSync(curPath).isFile() && /\.ts$/.test(file)) {
+ let taskname = file.replace(/(\.ts)/, '');
+ cb(taskname);
+ }
+ }
+ }
+}
diff --git a/tools/utils/template-injectables.ts b/tools/utils/template-injectables.ts
new file mode 100644
index 0000000..83ac315
--- /dev/null
+++ b/tools/utils/template-injectables.ts
@@ -0,0 +1,25 @@
+import * as slash from 'slash';
+import {join} from 'path';
+import {APP_BASE, APP_DEST, ENV} from '../config';
+
+let injectables: string[] = [];
+
+export function injectableAssetsRef() {
+ return injectables;
+}
+
+export function registerInjectableAssetsRef(paths: string[], target: string = '') {
+ injectables = injectables.concat(
+ paths
+ .filter(path => !/(\.map)$/.test(path))
+ .map(path => join(target, slash(path).split('/').pop()))
+ );
+}
+
+export function transformPath(plugins, env) {
+ return function (filepath) {
+ filepath = ENV === 'prod' ? filepath.replace(`/${APP_DEST}`, '') : filepath;
+ arguments[0] = join(APP_BASE, filepath);
+ return slash(plugins.inject.transform.apply(plugins.inject.transform, arguments));
+ };
+}
diff --git a/tools/utils/template-locals.ts b/tools/utils/template-locals.ts
new file mode 100644
index 0000000..be6b669
--- /dev/null
+++ b/tools/utils/template-locals.ts
@@ -0,0 +1,13 @@
+import {APP_BASE, APP_DEST, APP_ROOT, APP_TITLE, SYSTEM_CONFIG, VERSION} from '../config';
+
+// TODO: Add an interface to register more template locals.
+export function templateLocals() {
+ return {
+ APP_BASE,
+ APP_DEST,
+ APP_ROOT,
+ APP_TITLE,
+ SYSTEM_CONFIG,
+ VERSION
+ };
+}