From 5310ac7d8eb1838a6297117bc7f9fca70291f46a Mon Sep 17 00:00:00 2001 From: Alex Tatiyants Date: Sun, 3 Jan 2016 17:17:48 -0800 Subject: initial commit --- tools/config.ts | 101 ++++++++++++++++++++++++++++++++++ tools/tasks/build.bundles.ts | 26 +++++++++ tools/tasks/build.deps.ts | 20 +++++++ tools/tasks/build.docs.ts | 27 +++++++++ tools/tasks/build.fonts.dev.ts | 15 +++++ tools/tasks/build.html_css.prod.ts | 24 ++++++++ tools/tasks/build.img.dev.ts | 14 +++++ tools/tasks/build.index.ts | 34 ++++++++++++ tools/tasks/build.js.dev.ts | 25 +++++++++ tools/tasks/build.js.prod.ts | 22 ++++++++ tools/tasks/build.sass.dev.ts | 22 ++++++++ tools/tasks/build.test.ts | 21 +++++++ tools/tasks/check.versions.ts | 35 ++++++++++++ tools/tasks/clean.ts | 34 ++++++++++++ tools/tasks/karma.start.ts | 11 ++++ tools/tasks/npm.ts | 5 ++ tools/tasks/serve.docs.ts | 7 +++ tools/tasks/server.start.ts | 7 +++ tools/tasks/tsd.ts | 7 +++ tools/tasks/tslint.ts | 21 +++++++ tools/tasks/watch.dev.ts | 8 +++ tools/tasks/watch.serve.ts | 12 ++++ tools/tasks/watch.test.ts | 8 +++ tools/typings/connect-livereload.d.ts | 5 ++ tools/typings/gulp-load-plugins.d.ts | 42 ++++++++++++++ tools/typings/karma.d.ts | 12 ++++ tools/typings/merge-stream.d.ts | 8 +++ tools/typings/open.d.ts | 8 +++ tools/typings/run-sequence.d.ts | 5 ++ tools/typings/slash.d.ts | 5 ++ tools/typings/systemjs-builder.d.ts | 10 ++++ tools/typings/tiny-lr.d.ts | 10 ++++ tools/typings/yargs.d.ts | 9 +++ tools/utils.ts | 11 ++++ tools/utils/server.ts | 45 +++++++++++++++ tools/utils/tasks_tools.ts | 64 +++++++++++++++++++++ tools/utils/template-injectables.ts | 25 +++++++++ tools/utils/template-locals.ts | 13 +++++ 38 files changed, 778 insertions(+) create mode 100644 tools/config.ts create mode 100644 tools/tasks/build.bundles.ts create mode 100644 tools/tasks/build.deps.ts create mode 100644 tools/tasks/build.docs.ts create mode 100644 tools/tasks/build.fonts.dev.ts create mode 100644 tools/tasks/build.html_css.prod.ts create mode 100644 tools/tasks/build.img.dev.ts create mode 100644 tools/tasks/build.index.ts create mode 100644 tools/tasks/build.js.dev.ts create mode 100644 tools/tasks/build.js.prod.ts create mode 100644 tools/tasks/build.sass.dev.ts create mode 100644 tools/tasks/build.test.ts create mode 100644 tools/tasks/check.versions.ts create mode 100644 tools/tasks/clean.ts create mode 100644 tools/tasks/karma.start.ts create mode 100644 tools/tasks/npm.ts create mode 100644 tools/tasks/serve.docs.ts create mode 100644 tools/tasks/server.start.ts create mode 100644 tools/tasks/tsd.ts create mode 100644 tools/tasks/tslint.ts create mode 100644 tools/tasks/watch.dev.ts create mode 100644 tools/tasks/watch.serve.ts create mode 100644 tools/tasks/watch.test.ts create mode 100644 tools/typings/connect-livereload.d.ts create mode 100644 tools/typings/gulp-load-plugins.d.ts create mode 100644 tools/typings/karma.d.ts create mode 100644 tools/typings/merge-stream.d.ts create mode 100644 tools/typings/open.d.ts create mode 100644 tools/typings/run-sequence.d.ts create mode 100644 tools/typings/slash.d.ts create mode 100644 tools/typings/systemjs-builder.d.ts create mode 100644 tools/typings/tiny-lr.d.ts create mode 100644 tools/typings/yargs.d.ts create mode 100644 tools/utils.ts create mode 100644 tools/utils/server.ts create mode 100644 tools/utils/tasks_tools.ts create mode 100644 tools/utils/template-injectables.ts create mode 100644 tools/utils/template-locals.ts (limited to 'tools') 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 (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 +// 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(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; + buildStatic(source: string, target: string, options?: any): Promise; + } + + 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 + }; +} -- cgit v1.2.3