From 1935514ca8736472256f40bebca55bc26e599b89 Mon Sep 17 00:00:00 2001 From: Vladimir Lugovsky Date: Thu, 10 Dec 2015 19:22:20 +0300 Subject: [PATCH] feat(build): added build tasks --- gulp/build.js | 98 +++++++++++++++++++++++ gulp/conf.js | 41 ++++++++++ gulp/inject.js | 36 +++++++++ gulp/scripts.js | 17 ++++ gulp/server.js | 55 +++++++++++++ gulp/styles.js | 46 +++++++++++ gulp/watch.js | 39 ++++++++++ gulpfile.js | 203 ++++-------------------------------------------- package.json | 18 ++++- src/index.html | 30 ++++++- 10 files changed, 392 insertions(+), 191 deletions(-) create mode 100644 gulp/build.js create mode 100644 gulp/conf.js create mode 100644 gulp/inject.js create mode 100644 gulp/scripts.js create mode 100644 gulp/server.js create mode 100644 gulp/styles.js create mode 100644 gulp/watch.js diff --git a/gulp/build.js b/gulp/build.js new file mode 100644 index 000000000..cf4c833ac --- /dev/null +++ b/gulp/build.js @@ -0,0 +1,98 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var $ = require('gulp-load-plugins')({ + pattern: ['gulp-*', 'main-bower-files', 'uglify-save-license', 'del'] +}); + +gulp.task('partials', function () { + return gulp.src([ + path.join(conf.paths.src, '/app/**/*.html'), + path.join(conf.paths.tmp, '/serve/app/**/*.html') + ]) + .pipe($.minifyHtml({ + empty: true, + spare: true, + quotes: true + })) + .pipe($.angularTemplatecache('templateCacheHtml.js', { + module: 'angularBestPractices', + root: 'app' + })) + .pipe(gulp.dest(conf.paths.tmp + '/partials/')); +}); + +gulp.task('html', ['inject', 'partials'], function () { + var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false }); + var partialsInjectOptions = { + starttag: '', + ignorePath: path.join(conf.paths.tmp, '/partials'), + addRootSlash: false + }; + + var htmlFilter = $.filter('*.html', { restore: true }); + var jsFilter = $.filter('**/*.js', { restore: true }); + var cssFilter = $.filter('**/*.css', { restore: true }); + var assets; + + return gulp.src(path.join(conf.paths.tmp, '/serve/*.html')) + .pipe($.inject(partialsInjectFile, partialsInjectOptions)) + .pipe(assets = $.useref.assets()) + .pipe($.rev()) + .pipe(jsFilter) + .pipe($.sourcemaps.init()) + .pipe($.ngAnnotate()) + .pipe($.uglify({ preserveComments: $.uglifySaveLicense })).on('error', conf.errorHandler('Uglify')) + .pipe($.sourcemaps.write('maps')) + .pipe(jsFilter.restore) + .pipe(cssFilter) + .pipe($.sourcemaps.init()) + .pipe($.replace('../../bower_components/bootstrap-sass/assets/fonts/bootstrap/', '../fonts/')) + .pipe($.minifyCss({ processImport: false })) + .pipe($.sourcemaps.write('maps')) + .pipe(cssFilter.restore) + .pipe(assets.restore()) + .pipe($.useref()) + .pipe($.revReplace()) + .pipe(htmlFilter) + .pipe($.minifyHtml({ + empty: true, + spare: true, + quotes: true, + conditionals: true + })) + .pipe(htmlFilter.restore) + .pipe(gulp.dest(path.join(conf.paths.dist, '/'))) + .pipe($.size({ title: path.join(conf.paths.dist, '/'), showFiles: true })); + }); + +// Only applies for fonts from bower dependencies +// Custom fonts are handled by the "other" task +gulp.task('fonts', function () { + return gulp.src($.mainBowerFiles()) + .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) + .pipe($.flatten()) + .pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/'))); +}); + +gulp.task('other', function () { + var fileFilter = $.filter(function (file) { + return file.stat.isFile(); + }); + + return gulp.src([ + path.join(conf.paths.src, '/**/*'), + path.join('!' + conf.paths.src, '/**/*.{html,css,js,scss}') + ]) + .pipe(fileFilter) + .pipe(gulp.dest(path.join(conf.paths.dist, '/'))); +}); + +gulp.task('clean', function () { + return $.del([path.join(conf.paths.dist, '/'), path.join(conf.paths.tmp, '/')]); +}); + +gulp.task('build', ['html', 'fonts', 'other']); diff --git a/gulp/conf.js b/gulp/conf.js new file mode 100644 index 000000000..061528a7d --- /dev/null +++ b/gulp/conf.js @@ -0,0 +1,41 @@ +/** + * This file contains the variables used in other gulp files + * which defines tasks + * By design, we only put there very generic config values + * which are used in several places to keep good readability + * of the tasks + */ + +var gutil = require('gulp-util'); + +/** + * The main paths of your project handle these with care + */ +exports.paths = { + src: 'src', + dist: 'release', + tmp: '.tmp', + e2e: 'e2e' +}; + +/** + * Wiredep is the lib which inject bower dependencies in your project + * Mainly used to inject script tags in the index.html but also used + * to inject css preprocessor deps and js files in karma + */ +exports.wiredep = { + exclude: [/jquery/, /\/bootstrap\.js$/, /\/bootstrap-sass\/.*\.js/, /\/bootstrap\.css/], + directory: 'bower_components' +}; + +/** + * Common implementation for an error handler of a Gulp plugin + */ +exports.errorHandler = function(title) { + 'use strict'; + + return function(err) { + gutil.log(gutil.colors.red('[' + title + ']'), err.toString()); + this.emit('end'); + }; +}; diff --git a/gulp/inject.js b/gulp/inject.js new file mode 100644 index 000000000..c296052d6 --- /dev/null +++ b/gulp/inject.js @@ -0,0 +1,36 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var $ = require('gulp-load-plugins')(); + +var wiredep = require('wiredep').stream; +var _ = require('lodash'); + +gulp.task('inject', ['scripts', 'styles'], function () { + var injectStyles = gulp.src([ + path.join(conf.paths.tmp, '/serve/app/**/*.css'), + path.join('!' + conf.paths.tmp, '/serve/app/vendor.css') + ], { read: false }); + + var injectScripts = gulp.src([ + path.join(conf.paths.src, '/app/**/*.module.js'), + path.join(conf.paths.src, '/app/**/*.js'), + path.join('!' + conf.paths.src, '/app/**/*.spec.js'), + path.join('!' + conf.paths.src, '/app/**/*.mock.js'), + ]) + .pipe($.angularFilesort()).on('error', conf.errorHandler('AngularFilesort')); + + var injectOptions = { + ignorePath: [conf.paths.src, path.join(conf.paths.tmp, '/serve')], + addRootSlash: false + }; + + return gulp.src(path.join(conf.paths.src, '/*.html')) + .pipe($.inject(injectStyles, injectOptions)) + .pipe($.inject(injectScripts, injectOptions)) + .pipe(wiredep(_.extend({}, conf.wiredep))) + .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve'))); +}); diff --git a/gulp/scripts.js b/gulp/scripts.js new file mode 100644 index 000000000..57b5267e7 --- /dev/null +++ b/gulp/scripts.js @@ -0,0 +1,17 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +var $ = require('gulp-load-plugins')(); + +gulp.task('scripts', function () { + return gulp.src(path.join(conf.paths.src, '/app/**/*.js')) + .pipe($.eslint()) + .pipe($.eslint.format()) + .pipe(browserSync.reload({ stream: true })) + .pipe($.size()) +}); diff --git a/gulp/server.js b/gulp/server.js new file mode 100644 index 000000000..88b8b8213 --- /dev/null +++ b/gulp/server.js @@ -0,0 +1,55 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); +var browserSyncSpa = require('browser-sync-spa'); + +var util = require('util'); + +var proxyMiddleware = require('http-proxy-middleware'); + +function browserSyncInit(baseDir, browser) { + browser = browser === undefined ? 'default' : browser; + + var routes = null; + if(baseDir === conf.paths.src || (util.isArray(baseDir) && baseDir.indexOf(conf.paths.src) !== -1)) { + routes = { + '/bower_components': 'bower_components' + }; + } + + var server = { + baseDir: baseDir, + routes: routes + }; + + /* + * You can add a proxy to your backend by uncommenting the line below. + * You just have to configure a context which will we redirected and the target url. + * Example: $http.get('/users') requests will be automatically proxified. + * + * For more details and option, https://github.com/chimurai/http-proxy-middleware/blob/v0.0.5/README.md + */ + // server.middleware = proxyMiddleware('/users', {target: 'http://jsonplaceholder.typicode.com', proxyHost: 'jsonplaceholder.typicode.com'}); + + browserSync.instance = browserSync.init({ + startPath: '/', + server: server, + browser: browser + }); +} + +browserSync.use(browserSyncSpa({ + selector: '[ng-app]'// Only needed for angular apps +})); + +gulp.task('serve', ['watch'], function () { + browserSyncInit([path.join(conf.paths.tmp, '/serve'), conf.paths.src]); +}); + +gulp.task('serve:dist', ['build'], function () { + browserSyncInit(conf.paths.dist); +}); diff --git a/gulp/styles.js b/gulp/styles.js new file mode 100644 index 000000000..bf17202e0 --- /dev/null +++ b/gulp/styles.js @@ -0,0 +1,46 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +var $ = require('gulp-load-plugins')(); + +var wiredep = require('wiredep').stream; +var _ = require('lodash'); + +gulp.task('styles', function () { + var sassOptions = { + style: 'expanded' + }; + + var injectFiles = gulp.src([ + path.join(conf.paths.src, '/app/**/*.scss'), + path.join('!' + conf.paths.src, '/app/index.scss') + ], { read: false }); + + var injectOptions = { + transform: function(filePath) { + filePath = filePath.replace(conf.paths.src + '/app/', ''); + return '@import "' + filePath + '";'; + }, + starttag: '// injector', + endtag: '// endinjector', + addRootSlash: false + }; + + + return gulp.src([ + path.join(conf.paths.src, '/app/index.scss') + ]) + .pipe($.inject(injectFiles, injectOptions)) + .pipe(wiredep(_.extend({}, conf.wiredep))) + .pipe($.sourcemaps.init()) + .pipe($.sass(sassOptions)).on('error', conf.errorHandler('Sass')) + .pipe($.autoprefixer()).on('error', conf.errorHandler('Autoprefixer')) + .pipe($.sourcemaps.write()) + .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve/app/'))) + .pipe(browserSync.reload({ stream: true })); +}); diff --git a/gulp/watch.js b/gulp/watch.js new file mode 100644 index 000000000..16eb3247b --- /dev/null +++ b/gulp/watch.js @@ -0,0 +1,39 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +function isOnlyChange(event) { + return event.type === 'changed'; +} + +gulp.task('watch', ['inject'], function () { + + gulp.watch([path.join(conf.paths.src, '/*.html'), 'bower.json'], ['inject']); + + gulp.watch([ + path.join(conf.paths.src, '/app/**/*.css'), + path.join(conf.paths.src, '/app/**/*.scss') + ], function(event) { + if(isOnlyChange(event)) { + gulp.start('styles'); + } else { + gulp.start('inject'); + } + }); + + gulp.watch(path.join(conf.paths.src, '/app/**/*.js'), function(event) { + if(isOnlyChange(event)) { + gulp.start('scripts'); + } else { + gulp.start('inject'); + } + }); + + gulp.watch(path.join(conf.paths.src, '/app/**/*.html'), function(event) { + browserSync.reload(event.path); + }); +}); diff --git a/gulpfile.js b/gulpfile.js index 7c42c6eff..6b58f6b64 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,192 +1,23 @@ -var gulp = require('gulp'); - -var sass = require('gulp-sass'); -var autoprefix = require('gulp-autoprefixer'); -var minifyCSS = require('gulp-minify-css'); -var concat = require('gulp-concat'); -var changed = require('gulp-changed'); -var imagemin = require('gulp-imagemin'); -var stripDebug = require('gulp-strip-debug'); -var uglify = require('gulp-uglify'); -var eventStream = require('event-stream'); -var templateCache = require('gulp-angular-templatecache'); -var minifyHTML = require('gulp-minify-html'); -var zip = require('gulp-zip'); -var prompt = require('gulp-prompt'); -var rename = require('gulp-rename'); - -var bootstrapCssSrc = 'bower_components/bootstrap/dist/css/bootstrap.min.css'; - -gulp.task('minify-404-css', function () { - var vendorFiles = gulp.src(bootstrapCssSrc); - var appFiles = gulp.src('src/assets/css/404.scss').pipe(sass({ style: 'compressed' }).on('error', sass.logError)); - - return eventStream.concat(vendorFiles, appFiles) - .pipe(concat('404.min.css')) - .pipe(autoprefix('last 4 version')) - .pipe(minifyCSS()) - .pipe(gulp.dest('release/css/')); -}); - -gulp.task('minify-auth-css', function () { - var vendorFiles = gulp.src(bootstrapCssSrc); - var appFiles = gulp.src('src/assets/css/auth.scss').pipe(sass({ style: 'compressed' }).on('error', sass.logError)); - - return eventStream.concat(vendorFiles, appFiles) - .pipe(concat('auth.min.css')) - .pipe(autoprefix('last 4 versions')) - .pipe(minifyCSS()) - .pipe(gulp.dest('release/css/')) -}); - -gulp.task('minify-css', ['minify-404-css', 'minify-auth-css'], function () { - var vendorFiles = gulp.src([ - bootstrapCssSrc, - 'bower_components/bootstrap-select/dist/css/bootstrap-select.min.css', - 'bower_components/bootstrap-switch/dist/css/bootstrap3/bootstrap-switch.min.css', - 'bower_components/bootstrap-tagsinput/dist/bootstrap-tagsinput.css', - 'bower_components/Ionicons/css/ionicons.min.css', - 'bower_components/font-awesome/css/font-awesome.min.css', - 'bower_components/animate.css/animate.min.css', - 'bower_components/angular-progress-button-styles/dist/angular-progress-button-styles.min.css' - ]); - var appFiles = gulp.src('src/assets/css/main.scss').pipe(sass({ style: 'compressed' }).on('error', sass.logError)); - - return eventStream.concat(vendorFiles, appFiles) - .pipe(concat('index.min.css')) - .pipe(autoprefix('last 2 versions')) - .pipe(minifyCSS()) - .pipe(gulp.dest('release/css/')) -}); - -var imgSrc = [ - 'src/assets/img/*', - 'src/assets/pictures/*', - 'src/app/tplSkin/thumbs/*', - 'src/app/pages/dashboard/widgets/timeline/img/*', - 'src/app/pages/profile/img/*', - 'src/app/pages/icons/widgets/kameleon-img/*', - 'bower_components/amcharts/dist/amcharts/images/*', - 'bower_components/ammap/dist/ammap/images/*', - 'bower_components/leaflet/dist/images/*' -]; - -gulp.task('imagemin', function () { - var imgDst = 'release/img/'; - return gulp - .src(imgSrc) - .pipe(changed(imgDst)) - .pipe(imagemin()) - .pipe(gulp.dest(imgDst)); -}); +'use strict'; -gulp.task('js-lib', function(){ - var libSrc = [ - 'bower_components/jquery/dist/jquery.min.js', - 'bower_components/angular/angular.min.js', - 'bower_components/angular-route/angular-route.min.js', - 'bower_components/angular-touch/angular-touch.min.js', - 'bower_components/jquery-ui/jquery-ui.min.js', - 'bower_components/bootstrap/dist/js/bootstrap.min.js', - 'bower_components/highlight/src/highlight.js', - 'bower_components/bootstrap-switch/dist/js/bootstrap-switch.min.js', - 'bower_components/bootstrap-select/dist/js/bootstrap-select.min.js', - 'bower_components/bootstrap-tagsinput/dist/bootstrap-tagsinput.min.js', - 'bower_components/moment/min/moment.min.js', - 'bower_components/amcharts/dist/amcharts/amcharts.js', - 'bower_components/amcharts/dist/amcharts/plugins/responsive/responsive.min.js', - 'bower_components/amcharts/dist/amcharts/serial.js', - 'bower_components/amcharts/dist/amcharts/funnel.js', - 'bower_components/amcharts/dist/amcharts/pie.js', - 'bower_components/amcharts-stock/dist/amcharts/amstock.js', - 'bower_components/ammap/dist/ammap/ammap.js', - 'bower_components/ammap/dist/ammap/maps/js/worldLow.js', - 'bower_components/jquery.easing/js/jquery.easing.min.js', - 'bower_components/angular-ui-sortable/sortable.js', - 'bower_components/jquery.easy-pie-chart/dist/jquery.easypiechart.min.js', - 'bower_components/fullcalendar/dist/fullcalendar.min.js', - 'bower_components/Chart.js/Chart.min.js', - 'bower_components/leaflet/dist/leaflet.js', - 'bower_components/angular-toastr/dist/angular-toastr.tpls.min.js', - 'bower_components/angular-smart-table/dist/smart-table.min.js', - 'bower_components/slimScroll/jquery.slimscroll.min.js', - 'bower_components/angular-slimscroll/angular-slimscroll.js', - 'bower_components/hammerjs/hammer.min.js', - 'bower_components/AngularHammer/angular.hammer.min.js', - 'bower_components/angular-progress-button-styles/dist/angular-progress-button-styles.min.js', - 'src/app/components/backTop/lib/jquery.backTop.min.js' - ]; - - return gulp.src(libSrc) - .pipe(concat('lib.min.js')) - .pipe(stripDebug()) - .pipe(uglify()) - .pipe(gulp.dest('release/js/')); -}); - -gulp.task('js', function () { - var src = [ - 'src/assets/js/global-variables.js', - 'src/assets/js/amcharts-blur-theme.js', - 'src/app/**/*.js', - '!src/app/**/lib/**/*.js' - ]; - - gulp.src(src).pipe(concat('bundle.min.js')).pipe(uglify()).pipe(gulp.dest('release/js/')); -}); - -gulp.task('font', function () { - var fontSrc = [ - 'src/assets/css/fonts/*', - 'bower_components/bootstrap/fonts/*', - 'bower_components/font-awesome/fonts/*', - 'bower_components/Ionicons/fonts/*', - ]; - var fontDst = 'release/fonts/'; - - gulp.src(fontSrc).pipe(gulp.dest(fontDst)); -}); - -gulp.task('templateCache', function () { - return gulp.src('src/app/**/*.html') - .pipe(minifyHTML({ conditionals: true, spare: true, empty: true })) - .pipe(templateCache({ root: 'app/', module: 'BlurAdmin' })) - .pipe(gulp.dest('release/js')); -}); - -gulp.task('html', function () { - return gulp.src('src/*.html') - .pipe(minifyHTML({ conditionals: true, spare: true, empty: true })) - .pipe(gulp.dest('release/')); -}); +var gulp = require('gulp'); +var wrench = require('wrench'); -gulp.task('watch', function () { - gulp.watch(['src/app/**/*.css', 'src/assets/**/*.css', './**/*.scss '], ['minify-css']); - gulp.watch(imgSrc, ['imagemin']); - gulp.watch(['src/app/**/*.js', 'src/assets/**/*.js'], ['js']); - gulp.watch(['src/app/**/*.html'], ['templateCache']); - gulp.watch(['src/*.html'], ['html']); +/** + * This will load all js or coffee files in the gulp directory + * in order to load all gulp tasks + */ +wrench.readdirSyncRecursive('./gulp').filter(function(file) { + return (/\.(js|coffee)$/i).test(file); +}).map(function(file) { + require('./gulp/' + file); }); -gulp.task('init', ['minify-css', 'imagemin', 'js-lib', 'js', 'font', 'templateCache', 'html']); - -gulp.task('marketplace-release', ['init'], function() { - return gulp.src('') - .pipe(prompt.prompt({ - type: 'input', - name: 'version', - message: 'Please enter release version (x.x.x)' - }, function(res){ - var nameAndVersion = 'blur-admin-' + res.version; - return gulp - .src(['src/**', 'release/**', 'bower.json', 'package.json', 'README.md', '.gitignore'], { base : "." }) - .pipe(rename(function (path) { - path.dirname = nameAndVersion + '/' + path.dirname; - })) - .pipe(zip(nameAndVersion + '.zip')) - .pipe(gulp.dest('.')); - })); +/** + * Default task clean temporaries directories and launch the + * main optimization build task + */ +gulp.task('default', ['clean'], function () { + gulp.start('build'); }); - -gulp.task('default', ['init']); \ No newline at end of file diff --git a/package.json b/package.json index bebb1f585..a05aef4ff 100644 --- a/package.json +++ b/package.json @@ -3,21 +3,37 @@ "version": "0.0.1", "devDependencies": { "bower": "^1.5.2", + "browser-sync": "^2.10.0", + "browser-sync-spa": "^1.0.3", "event-stream": "^3.3.1", "gulp": "^3.9.0", + "gulp-angular-filesort": "^1.1.1", "gulp-angular-templatecache": "^1.7.0", "gulp-autoprefixer": "^2.3.1", "gulp-changed": "^1.3.0", "gulp-concat": "^2.6.0", + "gulp-eslint": "^1.1.1", + "gulp-filter": "^3.0.1", + "gulp-flatten": "^0.2.0", "gulp-imagemin": "^2.3.0", + "gulp-inject": "^3.0.0", + "gulp-load-plugins": "^1.1.0", "gulp-minify-css": "^1.2.1", "gulp-minify-html": "^1.0.4", "gulp-prompt": "^0.1.2", "gulp-rename": "^1.2.2", "gulp-sass": "^2.0.4", + "gulp-size": "^2.0.0", + "gulp-sourcemaps": "^1.6.0", "gulp-strip-debug": "^1.0.2", "gulp-uglify": "^1.4.0", - "gulp-zip": "^3.0.2" + "gulp-useref": "^3.0.3", + "gulp-zip": "^3.0.2", + "http-proxy-middleware": "^0.9.0", + "lodash": "^3.10.1", + "main-bower-files": "^2.9.0", + "wiredep": "^2.2.2", + "wrench": "^1.5.8" }, "scripts": { "postinstall": "bower install" diff --git a/src/index.html b/src/index.html index fad1d1e95..5306bab73 100644 --- a/src/index.html +++ b/src/index.html @@ -12,7 +12,17 @@ - + + + + + + + + + + + @@ -48,10 +58,22 @@
- + + + + + - - + + + + + + + + + + \ No newline at end of file