/* * Copyright (c) 2016 Martin Donath * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ "use strict" /* ---------------------------------------------------------------------------- * Imports * ------------------------------------------------------------------------- */ var gulp = require("gulp") var args = require("yargs").argv var autoprefix = require("autoprefixer") var changed = require("gulp-changed") var child = require("child_process") var clean = require("del") var compact = require("gulp-remove-empty-lines") var concat = require("gulp-concat") var gulpif = require("gulp-if") var mincss = require("gulp-cssnano") var minhtml = require("gulp-htmlmin") var minsvg = require("gulp-svgmin") var modernizr = require("gulp-modernizr") var mqpacker = require("css-mqpacker") var notifier = require("node-notifier") var path = require("path") var plumber = require("gulp-plumber") var postcss = require("gulp-postcss") var replace = require("gulp-replace") var rev = require("gulp-rev") var sass = require("gulp-sass") var sasslint = require("gulp-sass-lint") var sourcemaps = require("gulp-sourcemaps") var stream = require("webpack-stream") var uglify = require("gulp-uglify") var util = require("gulp-util") var vinyl = require("vinyl-paths") var version = require("gulp-rev-replace") var webpack = require("webpack") /* ---------------------------------------------------------------------------- * Locals * ------------------------------------------------------------------------- */ /* MkDocs server */ var server = null /* Watching context */ var watch = false /* ---------------------------------------------------------------------------- * Overrides * ------------------------------------------------------------------------- */ /* * Override gulp.src() for nicer error handling. */ var src = gulp.src // TODO: do this via extension somehow... gulp.src = function() { return src.apply(gulp, arguments) .pipe( plumber(function(error) { util.log(util.colors.red( "Error (" + error.plugin + "): " + error.message )) /* Extract file where error happened, if existent */ var file = error.relativePath ? error.relativePath.split("/").pop() : "" /* Dispatch system-level notification */ notifier.notify({ title: "Error (" + error.plugin + "): " + file, message: error.messageOriginal }) this.emit("end") })) } /* ---------------------------------------------------------------------------- * Asset pipeline * ------------------------------------------------------------------------- */ /* * Clean stylesheets generated by build. */ gulp.task("assets:clean:stylesheets", () => { return gulp.src("material/assets/stylesheets/*") .pipe(vinyl(clean)) }) /* * Clean javascripts generated by build. */ gulp.task("assets:clean:javascripts", () => { return gulp.src("material/assets/javascripts/*") .pipe(vinyl(clean)) }) /* * Clean images generated by build. */ gulp.task("assets:clean:images", () => { return gulp.src("material/assets/images/*") .pipe(vinyl(clean)) }) /* * Clean files generated by build. */ gulp.task("assets:clean", [ "assets:clean:stylesheets", "assets:clean:javascripts", "assets:clean:images" ]) /* * Build stylesheets from SASS source. */ gulp.task("assets:build:stylesheets", args.production ? [ "assets:clean:stylesheets", "assets:build:images" ] : [], () => { return gulp.src("src/assets/stylesheets/*.scss") .pipe(gulpif(args.sourcemaps, sourcemaps.init())) .pipe( sass({ includePaths: [ "node_modules/modularscale-sass/stylesheets", "node_modules/material-design-color", "node_modules/material-shadows" ] })) .pipe( postcss([ autoprefix(), mqpacker ])) .pipe(gulpif(args.sourcemaps, sourcemaps.write())) .pipe(gulpif(args.production, mincss())) .pipe(gulpif(args.production, rev())) .pipe(gulpif(args.production, version({ manifest: gulp.src("manifest.json") }))) .pipe(gulp.dest("material/assets/stylesheets")) .pipe(gulpif(args.production, rev.manifest("manifest.json", { base: "material/assets", merge: true }))) .pipe(gulpif(args.production, gulp.dest("material/assets"))) }) /* * Build javascripts by transpiling ES6 with babel. */ gulp.task("assets:build:javascripts", args.production ? [ "assets:clean:javascripts" ] : [], () => { return gulp.src("src/assets/javascripts/**/*.js") .pipe( stream({ entry: "application.js", output: { filename: "application.js" }, module: { loaders: [{ loader: "babel-loader", test: path.join(__dirname, "src/assets/javascripts"), query: { presets: "es2015" } }, { test: /\.js$/, loader: "eslint-loader", exclude: /node_modules/ }] }, plugins: [ new webpack.NoErrorsPlugin() ].concat( args.production ? [ new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] : []), stats: { colors: true }, resolve: { modulesDirectories: [ "src/assets/javascripts", "node_modules" ], extensions: [ "", ".js" ] }, devtool: args.sourcemaps ? "source-map" : "" })) .pipe(gulpif(args.production, rev())) .pipe(gulp.dest("material/assets/javascripts")) .pipe(gulpif(args.production, rev.manifest("manifest.json", { base: "material/assets", merge: true }))) .pipe(gulpif(args.production, gulp.dest("material/assets"))) }) /* * Create a customized modernizr build. */ gulp.task("assets:build:modernizr", [ "assets:build:stylesheets", "assets:build:javascripts" ], () => { return gulp.src([ "material/assets/stylesheets/*.css", "material/assets/javascripts/*.js" ]).pipe( modernizr({ options: [ "addTest", /* Add custom tests */ "fnBind", /* Use function.bind */ "html5printshiv", /* HTML5 support for IE */ "setClasses", /* Add CSS classes to root tag */ "testProp" /* Test for properties */ ] })) .pipe(concat("modernizr.js")) .pipe(gulpif(args.production, uglify())) .pipe(gulpif(args.production, rev())) .pipe(gulp.dest("material/assets/javascripts")) .pipe(gulpif(args.production, rev.manifest("manifest.json", { base: "material/assets", merge: true }))) .pipe(gulpif(args.production, gulp.dest("material/assets"))) }) /* * Copy and minify vector graphics. */ gulp.task("assets:build:images:svg", () => { return gulp.src("src/assets/images/**/*.svg") .pipe(gulpif(watch, changed("material/assets/images"))) .pipe(gulpif(args.production, minsvg())) .pipe(gulpif(args.production, rev())) .pipe(gulp.dest("material/assets/images")) .pipe(gulpif(args.production, rev.manifest("manifest.json", { base: "material/assets", merge: true }))) .pipe(gulpif(args.production, gulp.dest("material/assets"))) }) /* * Copy favicon. */ gulp.task("assets:build:images:ico", () => { return gulp.src("src/assets/images/**/*.ico") .pipe(gulp.dest("material/assets/images")) }) /* * Copy images. */ gulp.task("assets:build:images", [ "assets:clean:images" ], () => { return gulp.start([ "assets:build:images:svg", "assets:build:images:ico" ]) }) /* * Minify views. */ gulp.task("assets:build:views", args.production ? [ "assets:build:stylesheets", "assets:build:modernizr", "assets:build:images" ] : [], () => { var metadata = require("./package.json") return gulp.src("src/**/*.html") .pipe(gulpif(watch, changed("material"))) .pipe( minhtml({ collapseBooleanAttributes: true, removeComments: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, customAttrCollapse: /(content)/ })) .pipe(replace("$theme-name$", metadata.name)) .pipe(replace("$theme-version$", metadata.version)) .pipe(compact()) .pipe(gulpif(args.production, version({ manifest: gulp.src("manifest.json") }))) .pipe(gulp.dest("material")) }) /* * Build assets. */ gulp.task("assets:build", [ "assets:build:stylesheets", "assets:build:javascripts", "assets:build:modernizr", "assets:build:images", "assets:build:views" ]) /* * Watch assets for changes and rebuild on the fly. */ gulp.task("assets:watch", () => { watch = true /* Rebuild stylesheets */ gulp.watch([ "src/assets/stylesheets/**/*.scss" ], ["assets:build:stylesheets"]) /* Rebuild javascripts */ gulp.watch([ "src/assets/javascripts/**/*.js" ], ["assets:build:javascripts"]) /* Copy images */ gulp.watch([ "src/assets/images/**/*" ], ["assets:build:images"]) /* Minify views */ gulp.watch([ "src/**/*.html" ], ["assets:build:views"]) }) /* ---------------------------------------------------------------------------- * Application server * ------------------------------------------------------------------------- */ /* * Build documentation. */ gulp.task("mkdocs:build", [ "assets:build" ], () => { return child.spawnSync("mkdocs", ["build"]) }) /* * Restart MkDocs server. */ gulp.task("mkdocs:serve", () => { if (server) server.kill() /* Spawn MkDocs server */ server = child.spawn("mkdocs", ["serve", "-a", "0.0.0.0:8000"]) /* Pretty print server log output */ server.stdout.on("data", data => { var lines = data.toString().split("\n") for (var l in lines) if (lines[l].length) util.log(lines[l]) }) /* Print errors to stdout */ server.stderr.on("data", data => { process.stdout.write(data.toString()) }) }) /* ---------------------------------------------------------------------------- * Interface * ------------------------------------------------------------------------- */ /* * Build assets and documentation. */ gulp.task("build", [ "assets:clean", "assets:build" ].concat(args.mkdocs ? "mkdocs:build" : [])) /* * Start asset and MkDocs watchdogs. */ gulp.task("watch", [ "assets:clean", "assets:build" ], () => { return gulp.start([ "assets:watch" ].concat(args.mkdocs ? "mkdocs:serve" : [])) }) /* * Build assets by default. */ gulp.task("default", ["build"])