marksmall/node-build-web-app

View on GitHub
src/gulp/tasks/serve.js

Summary

Maintainability
C
7 hrs
Test Coverage
"use strict";

import AbstractTaskLoader from "../abstractTaskLoader";
import config from "../config";
import utils from "../utils";

let browserSync = require("browser-sync").create(config.webServerNames.dev);
let proxyMiddleware = require("http-proxy-middleware");
let runSequence = require("run-sequence");

import historyApiFallback from "connect-history-api-fallback"; // fix for SPAs w/ BrowserSync & others: https://github.com/BrowserSync/browser-sync/issues/204
//import debug from "gulp-debug";

class ServeTaskLoader extends AbstractTaskLoader {
    registerTask(gulp){
        super.registerTask(gulp);

        runSequence = runSequence.use(gulp); // needed to bind to the correct gulp object (alternative is to pass gulp to runSequence as first argument)

        // TypeScript
        gulp.task("serve-scripts-typescript", "Transpile TypeScript to ES5 and reload the browser (this task should only be called during serve)", [ "prepare-serve-scripts-typescript" ], () =>{
            return browserSync.reload();
        });  // reload BrowserSync once everything is ready

        gulp.task("prepare-serve-scripts-typescript", "Transpile TypeScript to ES5 and generate sourcemaps", [
            "ts-lint"
        ], (callback) =>{
            return runSequence(
                "scripts-typescript",
                callback);
        });

        // JavaScript
        gulp.task("serve-scripts-javascript", "Transpile JavaScript to ES5 and reload the browser (this task should only be called during serve)", [ "prepare-serve-scripts-javascript" ], () =>{
            return browserSync.reload();
        }); // reload BrowserSync once everything is ready
        gulp.task("prepare-serve-scripts-javascript", "Transpile JavaScript to ES5 and generate sourcemaps", [
            "check-js-style",
            "check-js-quality"
        ], (callback) =>{
            return runSequence(
                "scripts-javascript",
                callback);
        });

        // configure proxy middleware
        // context: '/' will proxy all requests
        //     use: '/api' to proxy request when path starts with '/api'
        let proxy = null;
        let middleware = [
            historyApiFallback(), // not necessary if the app uses hash based routing
            function(req, res, next){
                res.setHeader("Access-Control-Allow-Origin", "*"); // add CORS to the response headers (for resources served by BrowserSync)
                next();
            }
        ];

        if(gulp.options.proxy){
            proxy = proxyMiddleware(gulp.options.proxy.api, {
                target: gulp.options.proxy.target + ":" + gulp.options.proxy.port,
                changeOrigin: true   // for vhosted sites, changes host header to match to target's host
            });

            middleware.unshift(proxy);
        }

        // let proxy = proxyMiddleware(gulp.options.proxy.api, {
        //     target: gulp.options.proxy.target + ":" + gulp.options.proxy.port,
        //     changeOrigin: true   // for vhosted sites, changes host header to match to target's host
        // });

        // If the app src folder is overridden, then append it to the watch list, otherwise use default.
        let baseDir = null;

        if(gulp.options.folders){
            baseDir = config.webServerFolders.dev.concat([ gulp.options.folders.app ]);
        } else{
            baseDir = config.webServerFolders.dev;
        }

        let browserSyncOptions = { // http://www.browsersync.io/docs/options/
            notify: false,
            //port: 8000,

            // Customize the BrowserSync console logging prefix
            logPrefix: "MWD", // Modern Web Dev

            // Run w/ https by uncommenting "https: true"
            // Note: this uses an unsigned certificate which on first access
            //       will present a certificate warning in the browser.
            // https: true,
            ghostMode: { // replicate actions in all clients
                clicks: false,
                forms: false,
                scroll: false
            },
            server: {
                baseDir: baseDir,
                //routes: alternative way to map content that is above the base dir
                // fix for SPAs w/ BrowserSync & others: https://github.com/BrowserSync/browser-sync/issues/204
                // reference: https://github.com/BrowserSync/browser-sync/issues/204
                // middleware: proxyMiddleware('/api', {target: 'http://localhost:8000'})
                middleware: middleware
            }//,
            //reloadDebounce: 500 // restrict the frequency in which browser reload events can be emitted to connected clients
        };

        // If the app src folder is overridden, then append it to the watch list, otherwise use default.
        let html = null;
        let styles = utils.getCssFolder(gulp, config);
        let typescript = null;
        let javascript = null;
        let images = null;

        if(gulp.options.folders){
            html = [ gulp.options.folders.app + config.globs.html ];
            typescript = [ gulp.options.folders.app + config.globs.scripts.typescript ];
            javascript = [ gulp.options.folders.app + config.globs.scripts.javascript ];
            images = [ gulp.options.folders.app + config.globs.images ];
        } else{
            html = config.html.src;
            typescript = config.typescript.srcAppOnly;
            javascript = config.javascript.src;
            images = config.images.src;
        }

        let startBrowserSync = () =>{
            browserSync.init(utils.mergeOptions(browserSyncOptions, gulp.options.browserSync));

            gulp.watch(html).on("change", browserSync.reload); // force a reload when html changes
            gulp.watch(styles, [ "sass-lint", "styles" ]); // stylesheet changes will be streamed if possible or will force a reload
            gulp.watch(typescript, [ "serve-scripts-typescript" ]); // TypeScript changes will force a reload
            gulp.watch(javascript, [ "serve-scripts-javascript" ]); // JavaScript changes will force a reload
            gulp.watch(images).on("change", browserSync.reload); // force a reload when images change
        };

        gulp.task("serve", "Watch files for changes and rebuild/reload automagically", () =>{
            runSequence("prepare-serve", startBrowserSync); // here we need to ensure that all the other tasks are done before we start BrowserSync
        });

        gulp.task("prepare-serve", "Do all the necessary preparatory work for the serve task", () =>{
            // Only run the proxy task if there is configuration for it, the proxy task is mandatory if
            // the proxy configuration is present.
            if(gulp.options.proxy){
                return runSequence([
                    "clean",
                    "ts-lint",
                    "check-js-style",
                    "check-js-quality",
                    "proxy"
                ], [
                    "scripts-typescript",
                    "scripts-javascript",
                    "sass-lint",
                    "styles",
                    "validate-package-json"
                ]);
            } else{
                return runSequence([
                    "clean",
                    "ts-lint",
                    "check-js-style",
                    "check-js-quality"
                ], [
                    "scripts-typescript",
                    "scripts-javascript",
                    "sass-lint",
                    "styles",
                    "validate-package-json"
                ]);
            }
        });
    }
}

module.exports = new ServeTaskLoader();