From 29cc38f410dfa3a9c201df010d72723802232882 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Sat, 12 Aug 2017 11:30:47 -0400 Subject: [PATCH] Add --watch to build script --- build.js | 307 ++++++++++++++++++++++++++++++---------------- package-lock.json | 33 +++++ package.json | 4 +- 3 files changed, 240 insertions(+), 104 deletions(-) diff --git a/build.js b/build.js index 783821552..6a8bd4367 100644 --- a/build.js +++ b/build.js @@ -3,12 +3,13 @@ global.__version = require('./package.json').version; +const Promise = require('bluebird'); const AtImport = require('postcss-import'); const Autoprefixer = require('autoprefixer'); const Chalk = require('chalk'); const CSSnano = require('cssnano'); const Del = require('del'); -const FS = require('fs'); +const FS = Promise.promisifyAll(require('fs')); const Layouts = require('metalsmith-layouts'); const Markdown = require('metalsmith-markdown'); const Metalsmith = require('metalsmith'); @@ -16,107 +17,15 @@ const Path = require('path'); const PostCSS = require('postcss'); const Program = require('commander'); const UglifyJS = require('uglify-js'); +const Watch = require('watch'); -// Initialize CLI -Program - .version(__version) - .option('--build', 'Builds a release') - .option('--clean', 'Removes existing release') - .on('--help', () => { - console.log(Chalk.cyan('\n Version %s\n'), __version); - process.exit(1); - }) - .parse(process.argv); - -// Show help by default -if(!process.argv.slice(2).length) { - Program.outputHelp(); - process.exit(1); -} - -// Run build task -if(Program.build) { - Promise.resolve() - // Remove the dist folder - .then(() => Del(Path.join(__dirname, 'dist'))) - - // Create the dist folder - .then(() => FS.mkdirSync(Path.join(__dirname, 'dist'))) - - // Generate minified stylesheet - .then(() => new Promise((resolve, reject) => { - let shoelaceCSS = Path.join(__dirname, 'source/css/shoelace.css'); - let css = FS.readFileSync(shoelaceCSS, 'utf8'); - - PostCSS([ - AtImport, - Autoprefixer({ browsers: ['last 2 versions', '> 5%', 'ie >= 11', 'iOS >= 8'] }), - CSSnano({ safe: true }) - ]) - .process(css, { from: shoelaceCSS }) - .then((result) => resolve(result.css)) - .catch((err) => reject(err)); - })) - - // Write stylesheet to dist - .then((styles) => new Promise((resolve, reject) => { - let shoelaceCSS = Path.join(__dirname, 'dist/shoelace.css'); - - // Update {{version}} in CSS since it's not processed with Handlebars - styles = styles.replace(/\{\{version\}\}/g, __version); - - // Write output file - FS.writeFile(shoelaceCSS, styles, 'utf8', (err) => { - if(err) { - reject(err); - return; - } - console.log(Chalk.green('CSS Minified: %s! 💪'), Path.relative(__dirname, shoelaceCSS)); - - resolve(); - }); - })) - - // Minify scripts - .then(() => new Promise((resolve, reject) => { - let scripts = { - 'dropdowns.js': FS.readFileSync(Path.join(__dirname, 'source/js/dropdowns.js'), 'utf8'), - 'tabs.js': FS.readFileSync(Path.join(__dirname, 'source/js/tabs.js'), 'utf8') - }; - - let result = UglifyJS.minify(scripts, { - output: { - comments: /^!/ - } - }); - if(result.error) { - reject(result.error); - return; - } - - resolve(result.code); - })) - - // Write minified scripts to dist - .then((scripts) => new Promise((resolve, reject) => { - let shoelaceJS = Path.join(__dirname, 'dist/shoelace.js'); - - // Update {{version}} in JS since it's not processed with Handlebars - scripts = scripts.replace(/\{\{version\}\}/g, __version); - - // Write output file - FS.writeFile(shoelaceJS, scripts, 'utf8', (err) => { - if(err) { - reject(err); - return; - } - console.log(Chalk.green('JS Minified: %s! 💪'), Path.relative(__dirname, shoelaceJS)); - - resolve(); - }); - })) - - // Generate the docs +// +// Builds all doc pages. +// +// Returns a promise. +// +function buildDocs() { + return Promise.resolve() .then(() => new Promise((resolve, reject) => { Metalsmith(__dirname) .source('./source/docs') @@ -154,8 +63,164 @@ if(Program.build) { resolve(); }); + })); +} + +// +// Builds all scripts. +// +// Returns a promise. +// +function buildScripts() { + return Promise.resolve() + // Create the dist folder if it doesn't exist + .then(() => { + if(!FS.existsSync(Path.join(__dirname, 'dist'))) { + return FS.mkdirAsync(Path.join(__dirname, 'dist')); + } + }) + + // Generate minified scripts + .then(() => new Promise((resolve, reject) => { + let scripts = { + 'dropdowns.js': FS.readFileSync(Path.join(__dirname, 'source/js/dropdowns.js'), 'utf8'), + 'tabs.js': FS.readFileSync(Path.join(__dirname, 'source/js/tabs.js'), 'utf8') + }; + + let result = UglifyJS.minify(scripts, { + output: { + comments: /^!/ + } + }); + if(result.error) { + reject(result.error); + return; + } + + resolve(result.code); })) + // Write minified scripts to dist + .then((scripts) => { + let shoelaceJS = Path.join(__dirname, 'dist/shoelace.js'); + + // Update {{version}} in JS since it's not processed with Handlebars + scripts = scripts.replace(/\{\{version\}\}/g, __version); + + // Output a message + console.log(Chalk.green('JS Minified: %s! 💪'), Path.relative(__dirname, shoelaceJS)); + + // Write output file + return FS.writeFileAsync(shoelaceJS, scripts, 'utf8'); + }); +} + +// +// Builds all stylesheets. +// +// Returns a promise. +// +function buildStyles() { + return Promise.resolve() + // Create the dist folder if it doesn't exist + .then(() => { + if(!FS.existsSync(Path.join(__dirname, 'dist'))) { + return FS.mkdirAsync(Path.join(__dirname, 'dist')); + } + }) + + // Generate minified stylesheet + .then(() => new Promise((resolve, reject) => { + let shoelaceCSS = Path.join(__dirname, 'source/css/shoelace.css'); + let css = FS.readFileSync(shoelaceCSS, 'utf8'); + + PostCSS([ + AtImport, + Autoprefixer({ browsers: ['last 2 versions', '> 5%', 'ie >= 11', 'iOS >= 8'] }), + CSSnano({ safe: true }) + ]) + .process(css, { from: shoelaceCSS }) + .then((result) => resolve(result.css)) + .catch((err) => reject(err)); + })) + + // Write stylesheet to dist + .then((styles) => { + let shoelaceCSS = Path.join(__dirname, 'dist/shoelace.css'); + + // Update {{version}} in CSS since it's not processed with Handlebars + styles = styles.replace(/\{\{version\}\}/g, __version); + + // Output a message + console.log(Chalk.green('CSS Minified: %s! 💪'), Path.relative(__dirname, shoelaceCSS)); + + // Write output file + return FS.writeFileAsync(shoelaceCSS, styles, 'utf8'); + }); +} + +// +// Watches a directory for changes +// +// - options (object) +// - path (string) - the path of the directory to watch. +// - ready (function) - callback to execute after initializing. +// - change (function(event, file)) - callback to execute when a file is changed. +// +// No return value. +// +function watch(options) { + options = options || {}; + + Watch.watchTree(options.path, { + ignoreDotFiles: true, + interval: 1 + }, (file, current, previous) => { + if(typeof file === 'object' && previous === null && current === null) { + if(typeof options.ready === 'function') options.ready(); + } else if(previous === null) { + if(typeof options.change === 'function') options.change({ type: 'created' }, file); + } else if(current.nlink === 0) { + if(typeof options.change === 'function') options.change({ type: 'deleted' }, file); + } else { + if(typeof options.change === 'function') options.change({ type: 'modified' }, file); + } + }); +} + +// Initialize CLI +Program + .version(__version) + .option('--build', 'Builds a release') + .option('--clean', 'Removes existing release') + .option('--watch', 'Watch for changes and build automatically') + .on('--help', () => { + console.log(Chalk.cyan('\n Version %s\n'), __version); + process.exit(1); + }) + .parse(process.argv); + +// Show help by default +if(!process.argv.slice(2).length) { + Program.outputHelp(); + process.exit(1); +} + +// Build +if(Program.build) { + Promise.resolve() + // Remove the dist folder + .then(() => Del(Path.join(__dirname, 'dist'))) + + // Build styles + .then(() => buildStyles()) + + // Minify scripts + .then(() => buildScripts()) + + // Generate docs + .then(() => buildDocs()) + // Exit with success .then(() => process.exit(1)) @@ -166,7 +231,7 @@ if(Program.build) { }); } -// Clean task +// Clean if(Program.clean) { Promise.resolve() // Delete /dist @@ -186,7 +251,43 @@ if(Program.clean) { // Handle errors .catch((err) => { - console.error(Chalk.red('Unable to delete dist directory: ' + err)); + console.error(Chalk.red(err)); process.exit(-1); }); } + +// Watch +if(Program.watch) { + // Watch styles + watch({ + path: Path.join(__dirname, 'source/css'), + ready: () => console.log(Chalk.cyan('Watching for style changes...')), + change: (event) => { + if(event.type === 'created' || event.type === 'modified') { + buildStyles(); + } + } + }); + + // Watch scripts + watch({ + path: Path.join(__dirname, 'source/js'), + ready: () => console.log(Chalk.cyan('Watching for scripts changes...')), + change: (event) => { + if(event.type === 'created' || event.type === 'modified') { + buildScripts(); + } + } + }); + + // Watch docs + watch({ + path: Path.join(__dirname, 'source/docs'), + ready: () => console.log(Chalk.cyan('Watching for docs changes...')), + change: (event) => { + if(event.type === 'created' || event.type === 'modified') { + buildDocs(); + } + } + }); +} diff --git a/package-lock.json b/package-lock.json index 6ac5a8fe3..f1b9d47c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1420,6 +1420,15 @@ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "dev": true }, + "exec-sh": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.0.tgz", + "integrity": "sha1-FPdd4/INKG75MwmbLOUKkDWc7xA=", + "dev": true, + "requires": { + "merge": "1.2.0" + } + }, "expand-brackets": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", @@ -2973,6 +2982,12 @@ "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", "dev": true }, + "merge": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.0.tgz", + "integrity": "sha1-dTHjnUlJwoGma4xabgJl6LBYlNo=", + "dev": true + }, "metalsmith": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/metalsmith/-/metalsmith-2.3.0.tgz", @@ -5579,6 +5594,24 @@ "wrap-fn": "0.1.5" } }, + "watch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz", + "integrity": "sha1-NApxe952Vyb6CqB9ch4BR6VR3ww=", + "dev": true, + "requires": { + "exec-sh": "0.2.0", + "minimist": "1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } + } + }, "whet.extend": { "version": "0.9.9", "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", diff --git a/package.json b/package.json index 38edeaf83..106f32873 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "devDependencies": { "autoprefixer": "^7.1.2", + "bluebird": "^3.5.0", "chalk": "^2.1.0", "commander": "^2.11.0", "cssnano": "^3.10.0", @@ -23,7 +24,8 @@ "metalsmith-markdown": "^0.2.1", "postcss": "^6.0.8", "postcss-import": "^10.0.0", - "uglify-js": "^3.0.27" + "uglify-js": "^3.0.27", + "watch": "^1.0.2" }, "dependencies": {} }