Guest User

Untitled

a guest
Feb 2nd, 2016
343
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. 'use strict';
  2. var gulp = require('gulp');
  3. var sass = require('gulp-sass');
  4. var imagemin = require('gulp-imagemin');
  5. var uglify = require('gulp-uglify');
  6. var del = require('del');
  7. var merge = require('merge-stream');
  8. var htmlmin = require('gulp-htmlmin');
  9. var inlinesource = require('gulp-inline-source');
  10. var gettext = require('gulp-angular-gettext');
  11. var sourcemaps = require('gulp-sourcemaps');
  12. var rename = require('gulp-rename');
  13. var htmlify = require('gulp-angular-htmlify');
  14. var ngAnnotate = require('gulp-ng-annotate');
  15. var debug = require('gulp-debug');
  16. var changed = require('gulp-changed');
  17. var RevAll = require('gulp-rev-all');
  18. var karma = require('karma').server;
  19. var gulpFilter = require('gulp-filter');
  20. var minifyCSS = require('gulp-minify-css');
  21. var fs = require('fs');
  22. var notify = require('gulp-notify');
  23. var templateCache = require('gulp-angular-templatecache');
  24. var postcss = require('gulp-postcss');
  25. var autoprefixer = require('autoprefixer-core');
  26. var connect = require('gulp-connect');
  27. var replace = require('gulp-replace');
  28. var rollbar = require('gulp-rollbar');
  29. var argv = require('yargs').argv;
  30. var Q = require('q');
  31. var map = require('map-stream');
  32. var chmod = require('gulp-chmod');
  33. var globExpand = require('glob-expand');
  34.  
  35. var es = require('event-stream');
  36. var path = require('path');
  37. var gutil = require('gulp-util');
  38.  
  39. var DEST = 'build';
  40. var revisionFiles = true;
  41. var minificationEnabled = true;
  42. var sourceMapsEnabled = true;
  43. var rollbarUpload = argv.upload;
  44. minificationEnabled = rollbarUpload || minificationEnabled;
  45. sourceMapsEnabled = rollbarUpload || sourceMapsEnabled;
  46.  
  47. var cachedTemplatesGlobs = [
  48.     './src/app/partials/*.html',
  49.     './src/app/modals/*.html',
  50. ];
  51.  
  52. var uglifySettings = {
  53.     preserveLicenseComments: false,
  54.     inline_script: true,
  55.     warnings: false,
  56.     mangle: { except:
  57.         [
  58.             '__require',
  59.             'require',
  60.             'define',
  61.             'System.import',
  62.             'System.require',
  63.             'System',
  64.             'FB',
  65.             'ga',
  66.             'Rollbar'
  67.         ]},
  68.     beautify: {
  69.         quote_keys: true
  70.     },
  71.     compress: {
  72.         sequences: true,
  73.         dead_code: true,
  74.         conditionals: true,
  75.         booleans: true,
  76.         unused: true,
  77.         if_return: true,
  78.         join_vars: true,
  79.         drop_console: true
  80.     }
  81. };
  82.  
  83. var base = path.join(__dirname, 'src');
  84. var jspm = require('jspm');
  85. jspm.setPackagePath('.');
  86. var builder = new jspm.Builder();
  87.  
  88. function getSystemJSPath(mod, path) {
  89.     var parts = builder.loader.map[mod].split(':');
  90.     var dir = builder.loader.paths[parts[0] + ':*'].split('*')[0] + parts[1];
  91.     return base + '/' + dir + '/' + path || '';
  92. }
  93.  
  94. function getSystemJSEntryPoint(mod) {
  95.     var parts = builder.loader.map[mod].split(':');
  96.     var dir = builder.loader.paths[parts[0] + ':*'].split('*')[0] + parts[1];
  97.     return base + '/' + dir + '.js';
  98. }
  99.  
  100. gulp.task('default', ['clean', 'dev'], function() {
  101.   gulp.watch([
  102.       './src/scss/**/*',
  103.       './src/app/partials/*',
  104.       './src/app/modals/*.html'],
  105.     ['dev']);
  106. });
  107.  
  108. function styles() {
  109.     var stream = gulp.src('./src/scss/app.scss', {base: base})
  110.         .pipe(sass({includePaths: [
  111.             getSystemJSPath('foundation', 'scss'),
  112.             getSystemJSPath('fontawesome', 'scss'),
  113.             getSystemJSPath('spinkit', 'scss')
  114.         ]}))
  115.         .pipe(postcss([
  116.             autoprefixer({
  117.                 browsers: ['last 2 version'],
  118.                 cascade: false
  119.             })
  120.         ]))
  121.         .pipe(rename(function(path) {
  122.             path.dirname = './css/';
  123.         }));
  124.  
  125.     if (minificationEnabled) {
  126.         stream = stream.pipe(minifyCSS({
  127.             advanced: false,
  128.             aggressiveMerging: false
  129.         }));
  130.     }
  131.  
  132.     return stream;
  133. }
  134. gulp.task('styles', styles);
  135.  
  136. function errFn(e){
  137.     console.log('Error on', e, e.stack);
  138. }
  139.  
  140. function scripts() {
  141.     var sourceGlob = [
  142.         'src/**/*.js',
  143.         '!src/version.json',
  144.         '!src/lib/**/*',
  145.     ];
  146.  
  147.     var bundleStream = es.pause();
  148.     bundleStream.pause();
  149.  
  150.     var commonBundlePromise = commonBundle(4);
  151.  
  152.     var primaryBundlesPromise = commonBundlePromise.then(proccessBundles, errFn);
  153.  
  154.     var secondaryBundlesPromise = commonBundlePromise
  155.         .then(miniBundleTrees, errFn)
  156.         .then(proccessBundles, errFn);
  157.  
  158.     Promise.all([primaryBundlesPromise, secondaryBundlesPromise]).then(function(result){
  159.         var a = result[0];
  160.         var b = result[1];
  161.  
  162.         function popOnStream(data){
  163.             if(!data.fileName) {
  164.                 return;
  165.             }
  166.             var file = new gutil.File({
  167.                 path: path.join(base, data.fileName),
  168.                 base: base,
  169.                 contents: new Buffer(data.contents),
  170.                 sourceMap: data.sourceMap
  171.             });
  172.             bundleStream.write(file);
  173.         }
  174.  
  175.         a.map(popOnStream);
  176.         b.map(popOnStream);
  177.         bundleStream.resume();
  178.         bundleStream.emit('end');
  179.     });
  180.  
  181.     var stream = merge(bundleStream, thirdPartySources());
  182.  
  183.     if (sourceMapsEnabled) {
  184.         stream = stream.pipe(sourcemaps.init());
  185.     }
  186.  
  187.     stream = stream.pipe(rename(function(sourcePath) {
  188.         sourcePath.dirname = sourcePath.dirname.replace(__dirname, '');
  189.     }));
  190.  
  191.     return stream;
  192.  
  193.     function commonBundle(minCount){
  194.         var files = globExpand(sourceGlob);
  195.         var counts = {};
  196.  
  197.         var agregateTree = {};
  198.  
  199.         var promises = files.map(function(filename_){
  200.             var filename = filename_.replace(/^src\//, '');
  201.             return builder.trace(filename).then(function(tree){
  202.                 agregateTree = builder.addTrees(agregateTree, tree);
  203.                 for(var key in tree){
  204.                     counts[key] = (counts[key] || 0) + 1;
  205.                 }
  206.             }, errFn);
  207.         });
  208.  
  209.         return Promise.all(promises).then(function(){
  210.             for(var key in counts){
  211.                 if(counts[key] < minCount){
  212.                     delete agregateTree[key];
  213.                 }
  214.             }
  215.  
  216.             // console.log('commonTree', agregateTree);
  217.  
  218.             // return cTree;
  219.             return [{
  220.                 tree: agregateTree,
  221.                 fileName: 'shared-bundle.js'
  222.             }];
  223.         });
  224.     }
  225.  
  226.     function proccessBundles(bundles) {
  227.         var bstream = es.pause();
  228.         bstream.pause;
  229.  
  230.         var bundlePromises = bundles.map(function(args) {
  231.             return builder.bundle(args.tree, { sourceMaps: sourceMapsEnabled })
  232.             .then(processBundle, errFn);
  233.  
  234.             function processBundle(bundle) {
  235.                 var data = {
  236.                     fileName: args.fileName,
  237.                     contents: bundle.source,
  238.                 };
  239.  
  240.                 if (sourceMapsEnabled && bundle.sourceMap){
  241.                     var sourceMap = JSON.parse(bundle.sourceMap);
  242.                     sourceMap.sourcesContent = [];
  243.                     sourceMap.file = data.fileName;
  244.                     sourceMap.sourceRoot = '.';
  245.  
  246.                     for (var j = 0; j < sourceMap.sources.length; j++) {
  247.                         var content = fs.readFileSync(path.join(base, sourceMap.sources[j]), 'utf8');
  248.                         sourceMap.sourcesContent.push(content);
  249.                     }
  250.  
  251.                     data.sourceMap = sourceMap;
  252.                 }
  253.                 return data;
  254.             }
  255.         });
  256.         return Promise.all(bundlePromises);
  257.     }
  258.  
  259.  
  260.     function miniBundleTrees(cBundles){
  261.  
  262.         var bundledFiles = [];
  263.         for (var key in cBundles[0].tree) {
  264.             var p = cBundles[0].tree[key].path;
  265.             bundledFiles.push('src/' + p);
  266.         }
  267.  
  268.         var sourceGlob_ = JSON.parse(JSON.stringify(sourceGlob));
  269.  
  270.         for (var j = 0; j < bundledFiles.length; j++) {
  271.             sourceGlob_.push('!' + bundledFiles[j]);
  272.         }
  273.  
  274.         var files = globExpand(sourceGlob_);
  275.  
  276.         var promises = files.map(function(filename_){
  277.             var filename = filename_.replace(/^src\//, '');
  278.             return builder.trace(filename).then(function(tree){
  279.                 // console.log("filename", filename, filename_);
  280.                 var cTree = JSON.parse(JSON.stringify(cBundles[0].tree));
  281.                 // Always keep the file itself
  282.                 delete cTree[filename];
  283.                 var prunedTree = builder.subtractTrees(tree, cTree);
  284.                 // console.log('commonTree', Object.keys(cTree));
  285.                 // console.log('trace', Object.keys(prunedTree));
  286.                 return {
  287.                     tree: prunedTree,
  288.                     fileName: filename
  289.                 };
  290.             }, errFn);
  291.         });
  292.  
  293.         return Promise.all(promises);
  294.     }
  295.  
  296.  
  297.     function thirdPartySources(){
  298.  
  299.         var combined = merge();
  300.  
  301.         ['en', 'fr', 'de', 'af'].map(function mpr(lang) {
  302.             combined.add(gulp.src(
  303.                 getSystemJSPath('angular-i18n', 'angular-locale_' + lang + '.js'),
  304.                 {base: base}));
  305.         });
  306.  
  307.         combined.add(gulp.src(
  308.             [getSystemJSPath('leaflet', '**/*.js'), '!**/src/**'],
  309.             {base: base})
  310.         );
  311.  
  312.         combined.add(gulp.src(
  313.             [
  314.                 'src/lib/system-polyfills.js',
  315.                 getSystemJSPath('papaparse', '**/*.js'),
  316.                 getSystemJSEntryPoint('papaparse'),
  317.             ],
  318.             {base: base})
  319.         );
  320.  
  321.         return combined;
  322.     }
  323. }
  324.  
  325. // Fonts
  326. function fonts() {
  327.     return merge(
  328.         gulp.src([getSystemJSPath('fontawesome', 'fonts/*')])
  329.             .pipe(rename({dirname: 'fonts'})),
  330.         gulp.src(['./src/fonts/*'], {base: base})
  331.     );
  332. }
  333.  
  334. // Error pages
  335. function errorPages() {
  336.     return gulp.src(['./src/error_pages/*'], {base: base})
  337.         .pipe(changed(DEST));
  338. }
  339.  
  340. // Images
  341. function images() {
  342.     var filter = gulpFilter(['*', '!.svg']);
  343.  
  344.     return merge(
  345.         gulp.src([
  346.             './src/*.{png,jpg,gif}',
  347.             './src/img/*.{png,jpg,gif,svg}',
  348.             getSystemJSPath('leaflet', 'dist/images/*.png'),
  349.         ], {base: base}),
  350.         gulp.src([getSystemJSPath('leaflet', '')])
  351.             .pipe(rename({dirname: 'css/images'}))
  352.     )
  353.     .pipe(filter)
  354.     .pipe(imagemin({ optimizationLevel: 7, progressive: true, interlaced: true, pngquant: true }))
  355.     .pipe(filter.restore());
  356. }
  357.  
  358. // HTML
  359. function html() {
  360.     var sourceGlob = [
  361.         './src/**/*.html',
  362.         '!./src/lib/**/*.html'
  363.     ];
  364.  
  365.     for (var j = 0; j < cachedTemplatesGlobs.length; j++) {
  366.         sourceGlob.push('!' + cachedTemplatesGlobs[j]);
  367.     }
  368.  
  369.     return gulp.src(sourceGlob, {base: base})
  370.     .pipe(inlinesource({rootpath: 'src'}))
  371.     .pipe(replace(
  372.         'system-polyfills.js"></script>',
  373.         '/lib/system-polyfills.js"><\' + \'/script>'
  374.         ), {skipBinary: true})
  375.     .pipe(htmlmin({
  376.         minifyCSS: minificationEnabled,
  377.         minifyJS: minificationEnabled && uglifySettings,
  378.         collapseWhitespace: true,
  379.         conservativeCollapse: true,
  380.         keepClosingSlash: true,
  381.         removeComments: true,
  382.         processScripts: ['text/ng-template'],
  383.     }))
  384.     .pipe(htmlify({
  385.         customPrefixes: ['ui-', 'bkm-']
  386.     }))
  387.     .pipe(rename(function(path) {
  388.         path.dirname = path.dirname.replace(__dirname, '');
  389.     }));
  390. }
  391.  
  392. function templates() {
  393.     return gulp.src(cachedTemplatesGlobs, {base: base})
  394.         .pipe(templateCache(
  395.             'app/templates.js',
  396.             {
  397.                 moduleSystem: 'RequireJS',
  398.                 module: 'templates',
  399.                 standalone: true
  400.             }
  401.         ));
  402. }
  403.  
  404. // Extract translations
  405. function pot() {
  406.     return gulp.src(['src/**/*.html', 'src/**/*.js', '!src/lib/**/*'])
  407.         .pipe(gettext.extract('template.pot', {}));
  408. }
  409.  
  410. // Compile translations
  411. function translations() {
  412.     return gulp.src('po/**/*.po')
  413.         .pipe(gettext.compile({
  414.             format: 'json'
  415.         }))
  416.         .pipe(rename({dirname: 'translations'}));
  417. }
  418.  
  419. function other() {
  420.     return gulp.src([
  421.         'src/**/*.pdf',
  422.         'src/**/*.ico',
  423.         'src/**/*.csv',
  424.         'src/**/*.txt',
  425.         'src/**/*.manifest'
  426.     ]);
  427. }
  428.  
  429. gulp.task('buildTemplates', function(cb) {
  430.     return templates().pipe(chmod(755)).pipe(gulp.dest('src'));
  431. });
  432.  
  433. // Clean
  434. gulp.task('clean', function(cb) {
  435.     del([DEST], cb);
  436. });
  437.  
  438. // Build
  439. gulp.task('build', ['clean', 'buildTemplates'], function() {
  440.  
  441.     function annotator(contents, path){
  442.  
  443.         var moduleLoaderRegex = /(?:define|require|System\.register|System\.import|System\["require"\]|System\["import"\])\s*\(\s*(?:(?:['"][^'"]*['"]\s?,\s?)?(?:\[[^\]]*|(?:function))|(?:['"][^'"]*['"]\s?))/g;
  444.         var safeShortSubList = [];
  445.         var result;
  446.  
  447.         while (result = moduleLoaderRegex.exec(contents)) {
  448.             if (!result) {
  449.                 continue;
  450.             }
  451.             safeShortSubList.push(result[0]);
  452.         }
  453.  
  454.         var fragments = [{'contents': contents, path: path}];
  455.  
  456.         for (var i = 0; i < fragments.length; i++) {
  457.             for (var j = 0; j < safeShortSubList.length; j++) {
  458.                 var parts = fragments[i].contents.split(safeShortSubList[j]);
  459.  
  460.                 if (parts.length === 1) {
  461.                     continue;
  462.                 }
  463.  
  464.                 var args = [i, 1];
  465.  
  466.                 for (var idx = 0; idx < parts.length; idx++) {
  467.                     if (parts[idx]) {
  468.                         args.push({'contents': parts[idx], path: path});
  469.                     }
  470.  
  471.                     var isLast = (idx === parts.length - 1);
  472.                     if (!isLast) {
  473.                         args.push({'contents': safeShortSubList[j], path: path, 'safeShort': true});
  474.                     }
  475.                 }
  476.  
  477.                 // Nothing changed
  478.                 if (args.length < 4) {
  479.                     continue;
  480.                 }
  481.  
  482.                 Array.prototype.splice.apply(fragments, args);
  483.             }
  484.         }
  485.  
  486.         return fragments;
  487.     }
  488.  
  489.     function replacer(fragment, replaceRegExp, newReference, referencedFile) {
  490.  
  491.         // If not a js reference or its safe to do extentionless subs
  492.         if (!referencedFile.path.match(/.js$/) || fragment.safeShort) {
  493.             // ok
  494.         } else if (replaceRegExp.toString().match('\\(\\)')) {
  495.             return;
  496.         }
  497.  
  498.         fragment.contents = fragment.contents.replace(replaceRegExp, '$1' + newReference + '$3$4');
  499.     }
  500.  
  501.     var revAll = new RevAll({
  502.         fileNameVersion: 'version.json',
  503.         dontRenameFile: [
  504.             /fb\.html/,
  505.             /^\/?home\.html/,
  506.             /^\/?business-home\.html/,
  507.             /app\/app\.html/,
  508.             /redirect\.html/,
  509.             /app\/app\.manifest/,
  510.             /img\/email-footer-logo.png/,
  511.             /img\/email-logo.png/,
  512.         ],
  513.         dontUpdateReference: [
  514.             /fb\.html/,
  515.             /^\/?home\.html/,
  516.             /^\/?business-home\.html/,
  517.             /app\/app\.html/,
  518.             /redirect\.html/,
  519.             /app\/app\.manifest/
  520.         ],
  521.         dontSearchFile: [
  522.             /.*\.pdf/
  523.         ],
  524.         dontGlobal: [
  525.             /lib\/.*\.(jpg|jpeg|gif|png)/,
  526.             /lib\/angular-i18n\/.*/,
  527.             /lib\/.*/,
  528.             /fonts\/.*/,
  529.             /img\/bookem-120.png/,
  530.             /version\.json/,
  531.             /favicon\.ico/,
  532.             /robots\.txt/,
  533.             /humans\.txt/,
  534.             /css\/images\/*/,
  535.         ],
  536.         annotator: annotator,
  537.         replacer: replacer
  538.     });
  539.  
  540.     var script = scripts().pipe(ngAnnotate({
  541.         add: true,
  542.         single_quotes: true,
  543.     }));
  544.  
  545.     if (minificationEnabled) {
  546.         script = script.pipe(uglify(uglifySettings));
  547.     }
  548.  
  549.     var revisioned = merge(
  550.         images(),
  551.         html(),
  552.         fonts(),
  553.         script,
  554.         styles(),
  555.         other()
  556.         );
  557.  
  558.     if (revisionFiles) {
  559.         revisioned = revisioned.pipe(revAll.revision());
  560.     }
  561.  
  562.     var nonrevisoned = merge(
  563.         errorPages(),
  564.         translations(),
  565.         pot()
  566.     );
  567.  
  568.     var stream;
  569.  
  570.     if (revisionFiles) {
  571.         stream = merge(
  572.             revisioned.pipe(revAll.versionFile()),
  573.             revisioned,
  574.             nonrevisoned
  575.         );
  576.     } else {
  577.         stream = merge(
  578.             revisioned,
  579.             nonrevisoned
  580.         );
  581.     }
  582.  
  583.     if (sourceMapsEnabled && !rollbarUpload) {
  584.         stream = stream.pipe(sourcemaps.write('./maps'));
  585.     }
  586.  
  587.     var ps = es.pause();
  588.     ps.pause();
  589.     var versionHash;
  590.     var deferred = Q.defer();
  591.  
  592.     var mapStream = map(function(file, cb) {
  593.  
  594.         deferred.promise.then(function() {
  595.             cb(null, file);
  596.         });
  597.  
  598.         if (!versionHash && !revisionFiles) {
  599.             versionHash = 'BOOKEM_VERSION_HASH_123456789123';
  600.             mapStream.pipe(ps);
  601.             ps.resume();
  602.             deferred.resolve();
  603.         } else if (!versionHash && revAll.revisioner.hashCombined){
  604.             console.log('Version hash:', revAll.revisioner.hashCombined);
  605.             versionHash = revAll.revisioner.hashCombined;
  606.  
  607.             var versionReplace = replace(
  608.                 'BOOKEM_VERSION_HASH_123456789123',
  609.                 versionHash,
  610.                 {skipBinary: true}
  611.             );
  612.  
  613.             if (rollbarUpload) {
  614.                 var rb = rollbar({
  615.                     accessToken: '625baa078f76474d8201da0bb41c4682',
  616.                     version: versionHash,
  617.                     sourceMappingURLPrefix: 'https://bookem.co.za'
  618.                 });
  619.  
  620.                 mapStream
  621.                     .pipe(versionReplace)
  622.                     .pipe(rb)
  623.                     .pipe(ps);
  624.             } else {
  625.                 mapStream
  626.                     .pipe(versionReplace)
  627.                     .pipe(ps);
  628.             }
  629.  
  630.             ps.resume();
  631.             deferred.resolve();
  632.         }
  633.     });
  634.  
  635.     stream.pipe(mapStream);
  636.     stream = ps;
  637.  
  638.     // stream = stream.pipe(debug({verbose: true}));
  639.  
  640.     stream = stream.pipe(chmod(755))
  641.     .pipe(gulp.dest(DEST))
  642.     .pipe(notify({
  643.         onLast: true,
  644.         message: 'Build complete'
  645.     }));
  646.  
  647.     return stream.on('error', errFn);
  648. });
  649.  
  650. // Develop
  651. gulp.task('dev', function() {
  652.     return merge(
  653.         templates(),
  654.         styles(),
  655.         translations(),
  656.         pot().pipe(rename({dirname: '../po'})),
  657.         gulp.src([getSystemJSPath('fontawesome', 'fonts/*')]).pipe(rename({dirname: 'fonts'}))
  658.     )
  659.     // .pipe(debug({verbose: true}))
  660.     .pipe(gulp.dest('src'))
  661.     .pipe(notify({
  662.         onLast: true,
  663.         message: 'Build complete'
  664.     }));
  665. });
  666.  
  667.  
  668. // Test
  669. gulp.task('test', function (done) {
  670.     karma.start({
  671.         configFile: __dirname + '/test/configs/unit.conf.js',
  672.         singleRun: true
  673.     }, done);
  674. });
  675.  
  676.  
  677. gulp.task('connect', function() {
  678.     connect.server({
  679.         root: './src',
  680.         livereload: true,
  681.         fallback: './src/home.html'
  682.     });
  683. });
Advertisement
Add Comment
Please, Sign In to add comment