diff --git a/.eslintrc.js b/.eslintrc.js index 0effe44..1b33b68 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,13 +4,15 @@ module.exports = { commonjs: true, es6: true }, - extends: "standard", + extends: ['standard', 'prettier'], globals: { - Atomics: "readonly", - SharedArrayBuffer: "readonly" + Atomics: 'readonly', + SharedArrayBuffer: 'readonly' }, parserOptions: { ecmaVersion: 2018 }, - rules: {} + rules: { + semi: 0 + } }; diff --git a/.gitignore b/.gitignore index 85c9916..06c3bb3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /node_modules/ /tmp/ .DS_Store -/dist \ No newline at end of file +/dist +IDEAS.md diff --git a/gulpfile.js b/gulpfile.js index c577410..5d70224 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,13 +1,18 @@ -'use strict'; +const gulp = require('gulp'); +const plumber = require('gulp-plumber'); -const gulp = require('gulp'), - fs = require('fs'), - plumber = require('gulp-plumber'); +const { + SOURCE, + DIST, + WORKING_DIR, + CONFIGURATION_FILE +} = require('./constants'); const options = { - source: 'templates', - dist: 'dist', - workingDir: 'tmp', + source: SOURCE, + dist: DIST, + workingDir: WORKING_DIR, + configurationFile: CONFIGURATION_FILE, src: function plumbedSrc() { return gulp.src.apply(gulp, arguments).pipe(plumber()); } @@ -16,35 +21,35 @@ const options = { /** * Load tasks from the '/tasks' directory. */ -const build = require('./tasks/build')(options); -const checkDeps = require('./tasks/check-deps')(options); -const dupe = require('./tasks/dupe')(options); -const less = require('./tasks/less')(options); -const lint = require('./tasks/lint')(options); -const postcss = require('./tasks/postcss')(options); -const sass = require('./tasks/sass')(options); +require('./tasks/build')(options); +require('./tasks/dupe')(options); +require('./tasks/less')(options); +require('./tasks/lint')(options); +require('./tasks/postcss')(options); +require('./tasks/sass')(options); +require('./tasks/check-deps')(options); /* Runs the entire pipeline once. */ -gulp.task('run-pipeline', gulp.series('dupe', 'less', 'sass', 'postcss', 'lint', 'build')); +gulp.task( + 'run-pipeline', + gulp.series('dupe', 'less', 'sass', 'postcss', 'lint', 'build') +); /* By default templates will be built into '/dist'. */ gulp.task( 'default', - gulp.series( - 'run-pipeline', - () => { - /* gulp will watch for changes in '/templates'. */ - gulp.watch( - [ - options.source + '/**/*.html', - options.source + '/**/*.css', - options.source + '/**/*.scss', - options.source + '/**/*.less', - options.source + '/**/conf.json' - ], - { delay: 500 }, - gulp.series('run-pipeline') - ) - } - ) + gulp.series('run-pipeline', () => { + /* gulp will watch for changes in '/templates'. */ + gulp.watch( + [ + options.source + '/**/*.html', + options.source + '/**/*.css', + options.source + '/**/*.scss', + options.source + '/**/*.less', + options.source + '/**/conf.json' + ], + { delay: 500 }, + gulp.series('run-pipeline') + ); + }) ); diff --git a/package-lock.json b/package-lock.json index aecca8c..265d254 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3082,6 +3082,23 @@ } } }, + "eslint-config-prettier": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.2.0.tgz", + "integrity": "sha512-VLsgK/D+S/FEsda7Um1+N8FThec6LqE3vhcMyp8mlmto97y3fGf3DX7byJexGuOb1QY0Z/zz222U5t+xSfcZDQ==", + "dev": true, + "requires": { + "get-stdin": "^6.0.0" + }, + "dependencies": { + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + } + } + }, "eslint-config-standard": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-14.1.0.tgz", diff --git a/tasks/build.js b/tasks/build.js index 0704e68..3d27646 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -1,69 +1,67 @@ -'use strict'; - -const gulp = require('gulp'), - inlineCss = require('gulp-inline-css'), - minifyHTML = require('gulp-minify-html'), - minifyInline = require('gulp-minify-inline'), - preprocess = require('gulp-preprocess'), - rename = require('gulp-rename'), - klaw = require('klaw'), - fs = require('fs'), - del = require('del'), - jsonlint = require('jsonlint'), - inlineimg = require('gulp-inline-images-no-http'), - path = require('path'); +const gulp = require('gulp'); +const inlineCss = require('gulp-inline-css'); +const minifyHTML = require('gulp-minify-html'); +const minifyInline = require('gulp-minify-inline'); +const preprocess = require('gulp-preprocess'); +const rename = require('gulp-rename'); +const klaw = require('klaw'); +const fs = require('fs'); +const del = require('del'); +const inlineimg = require('gulp-inline-images-no-http'); +const path = require('path'); function buildTask(options) { - // Requires: 'dupe', 'less', 'sass', 'postcss', 'lint'. - gulp.task( - 'build', - function build(done) { - /** - * Makes templates for a given directory & its configurations. - * - * @function makeTemplates - * @param {String} dir Directory to make templates from. - * @param {Array} confItems A list of configurations objects (usually persons) to make templates from. - */ - function makeTemplates(dir, confItems) { - confItems.forEach(function handleConf(conf) { - var cwd = options.workingDir + '/' + dir; - var stylesheets = []; + const { configurationFile } = options; - /** - * Find stylesheets relative to the CWD & generate tags. - * This way we can automagically inject them into . - */ + // Requires: 'dupe', 'less', 'sass', 'postcss', 'lint'. + gulp.task('build', function build(done) { + /** + * Makes templates for a given directory & its configurations. + * + * @function makeTemplates + * @param {String} dir Directory to make templates from. + * @param {Array} confItems A list of configurations objects (usually persons) to make templates from. + */ + function makeTemplates(dir, confItems) { + return confItems.map(conf => { + const cwd = options.workingDir + '/' + dir; + const files = []; + + /** + * Find stylesheets relative to the CWD & generate tags. + * This way we can automagically inject them into . + */ + return new Promise(resolve => { klaw(cwd) .on('readable', function walkTemplateDir() { - var stylesheet; + let file; - while ((stylesheet = this.read())) { - var relativePath = __dirname.substring(0, __dirname.lastIndexOf('/')) + '/tmp/' + dir; - stylesheets.push(stylesheet.path.replace(relativePath, '')); + while ((file = this.read())) { + const relativePath = + __dirname.substring(0, __dirname.lastIndexOf('/')) + + '/tmp/' + + dir; + files.push(file.path.replace(relativePath, '')); } }) .on('end', function finishedTemplateDirWalk() { - const context = Object.assign( - conf, - { - stylesheets: stylesheets - .filter(function filterFiles (file) { - /* Read only CSS files. */ - return file.match(/.*\.css/) ? true : false; - }) - .reduce(function (prev, current, index, acc) { - var cssPath = path.win32.basename(current); - return (prev += ''); + /** + * Creates a context of stylesheets to inject them into HTML files later. + * Generates a list of tags with the found stylesheets. + */ + const context = Object.assign(conf, { + stylesheets: files + .filter(file => !!file.match(/.*\.css/)) // Read only CSS files. + .reduce((acc, cur) => { + const cssPath = path.win32.basename(cur); + return (acc += + ''); }, '') - } - ); + }); options .src([cwd + '/**/*.html', '!' + cwd + '/**/*.inc.html']) - .pipe( - preprocess({ context }) - ) + .pipe(preprocess({ context })) .pipe(inlineimg()) .pipe( inlineCss({ @@ -83,43 +81,58 @@ function buildTask(options) { }) ) .pipe(gulp.dest(options.dist)); + + resolve(); }); }); - } - - /* Clean up & then read 'src' to generate templates (build entry point). */ - return del(options.dist) - .then(function buildStart() { - /** - * Loop through dirs and load their conf files. - * Promisify all 'makeTemplate' calls and when resolved, let gulp know we're done. - */ - var files = []; - var promises = []; - - fs.readdirSync('./' + options.workingDir).forEach(function readConfigurations(dir) { - /** NB: For 'watch' to properly work, the cache needs to be deleted before each require. */ - var confPath = '../tmp/' + dir + '/conf.json'; - var current = null; - var confItems; - - delete require.cache[require.resolve(confPath)]; - current = require(confPath); - if (current && current.length) { - confItems = [...current]; - } else { - confItems = [current]; - } - - promises.push(makeTemplates(dir, confItems)); - }); - - Promise.all(promises); - }) - .then(() => done()) - .catch((err) => console.log(err)); + }); } - ); + + /* + * Clean up & then read from workingDir to generate templates. + * For each found config, a template group will be generated through `makeTemplates`. + */ + return del(options.dist) + .then(function buildStart() { + /** + * Loop through dirs and load their conf files. + * Promisify all 'makeTemplate' calls and when resolved, let gulp know we're done. + */ + const promises = []; + + fs.readdirSync(options.workingDir).forEach(function readConfigurations( + dir + ) { + const confPath = `../tmp/${dir}/${configurationFile}`; + + /** Exit with warn if no configuration file found. */ + if (!fs.existsSync(path.resolve(options.workingDir, confPath))) { + return console.warn( + `Missing configuration in "${dir}". Did you remember to create "${dir}/${configurationFile}"?` + ); + } + + let current = null; + let confItems; + + delete require.cache[require.resolve(confPath)]; // NB: For 'watch' to properly work, the cache needs to be deleted before each require. + current = require(confPath); + + // Handle single objects or arrays of configs. + if (current && current.length) { + confItems = [...current]; + } else { + confItems = [current]; + } + + promises.push(makeTemplates(dir, confItems)); + }); + + Promise.all(promises); + }) + .then(() => done()) + .catch(err => console.log(err)); + }); } module.exports = buildTask; diff --git a/tasks/check-deps.js b/tasks/check-deps.js index d5a206a..d3417e1 100644 --- a/tasks/check-deps.js +++ b/tasks/check-deps.js @@ -1,13 +1,9 @@ -'use strict'; +const gulp = require('gulp'); +const david = require('gulp-david'); -const gulp = require('gulp'), - david = require('gulp-david'); - -function checkDepsTask(){ - gulp.task('check-deps', function checkDeps(){ - gulp - .src('package.json') - .pipe(david()); +function checkDepsTask() { + gulp.task('check-deps', function checkDeps() { + gulp.src('package.json').pipe(david()); }); } diff --git a/tasks/dupe.js b/tasks/dupe.js index 610d236..ac02b9c 100644 --- a/tasks/dupe.js +++ b/tasks/dupe.js @@ -1,10 +1,8 @@ -'use strict'; +const gulp = require('gulp'); +const del = require('del'); -const gulp = require('gulp'), - del = require('del'); - -function dupeTask(options){ - gulp.task('dupe', function(){ +function dupeTask(options) { + gulp.task('dupe', function() { del.sync([options.workingDir]); return options diff --git a/tasks/less.js b/tasks/less.js index 528870d..f2e9259 100644 --- a/tasks/less.js +++ b/tasks/less.js @@ -1,23 +1,18 @@ -'use strict'; +const gulp = require('gulp'); +const less = require('gulp-less'); +const autoprefixer = require('gulp-autoprefixer'); +const rename = require('gulp-rename'); -const gulp = require('gulp'), - less = require('gulp-less'), - autoprefixer = require('gulp-autoprefixer'), - rename = require('gulp-rename'); - -function lessTask(options){ +function lessTask(options) { // Requires: dupe. - gulp.task( - 'less', - function() { - return options - .src(options.workingDir + '/**/*.less') - .pipe(less()) - .pipe(autoprefixer()) - .pipe(rename({ extname: '.css' })) - .pipe(gulp.dest(options.workingDir)); - } - ); + gulp.task('less', function() { + return options + .src(options.workingDir + '/**/*.less') + .pipe(less()) + .pipe(autoprefixer()) + .pipe(rename({ extname: '.css' })) + .pipe(gulp.dest(options.workingDir)); + }); } module.exports = lessTask; diff --git a/tasks/lint.js b/tasks/lint.js index 6ea959a..9975b91 100644 --- a/tasks/lint.js +++ b/tasks/lint.js @@ -1,19 +1,14 @@ -'use strict'; +const gulp = require('gulp'); +const jsonlint = require('gulp-jsonlint'); -const gulp = require('gulp'), - jsonlint = require("gulp-jsonlint"); - -function lintTask(options){ +function lintTask(options) { // Requiers: dupe. - gulp.task( - 'lint', - function() { - return options - .src(options.workingDir + '/**/conf.json') - .pipe(jsonlint()) - .pipe(jsonlint.reporter()); - } - ); + gulp.task('lint', function() { + return options + .src(options.workingDir + '/**/conf.json') + .pipe(jsonlint()) + .pipe(jsonlint.reporter()); + }); } module.exports = lintTask; diff --git a/tasks/postcss.js b/tasks/postcss.js index 488958d..98a83de 100644 --- a/tasks/postcss.js +++ b/tasks/postcss.js @@ -1,24 +1,17 @@ -'use strict'; +const gulp = require('gulp'); +const postcss = require('gulp-postcss'); +const autoprefixer = require('autoprefixer'); -const gulp = require('gulp'), - postcss = require('gulp-postcss'), - autoprefixer = require('autoprefixer'); - -function postcssTask(options){ +function postcssTask(options) { // Requires: dupe. - gulp.task( - 'postcss', - function() { - var processors = [ - autoprefixer() - ]; + gulp.task('postcss', function() { + var processors = [autoprefixer()]; - return options - .src(options.workingDir + '/**/*.css') - .pipe(postcss(processors)) - .pipe(gulp.dest(options.workingDir)); - } - ); + return options + .src(options.workingDir + '/**/*.css') + .pipe(postcss(processors)) + .pipe(gulp.dest(options.workingDir)); + }); } module.exports = postcssTask; diff --git a/tasks/sass.js b/tasks/sass.js index fb989f1..9f31b2e 100644 --- a/tasks/sass.js +++ b/tasks/sass.js @@ -1,9 +1,7 @@ -'use strict'; - -const gulp = require('gulp'), - autoprefixer = require('gulp-autoprefixer'), - sass = require('gulp-sass'), - rename = require('gulp-rename'); +const gulp = require('gulp'); +const autoprefixer = require('gulp-autoprefixer'); +const sass = require('gulp-sass'); +const rename = require('gulp-rename'); function sassTask(options) { // Requires: dupe. @@ -13,9 +11,7 @@ function sassTask(options) { return options .src(options.workingDir + '/**/*.scss') .pipe(sass()) - .pipe( - autoprefixer() - ) + .pipe(autoprefixer()) .pipe(rename({ extname: '.css' })) .pipe(gulp.dest(options.workingDir)); }) diff --git a/tasks/util/check-for-missing..js b/tasks/util/check-for-missing..js new file mode 100644 index 0000000..e69de29 diff --git a/tasks/util/check-for-unused.js b/tasks/util/check-for-unused.js new file mode 100644 index 0000000..e69de29 diff --git a/tests/end-to-end.test.js b/tests/end-to-end.test.js index 3f25032..b797a53 100644 --- a/tests/end-to-end.test.js +++ b/tests/end-to-end.test.js @@ -2,37 +2,55 @@ const test = require('ava'); const fs = require('fs'); const path = require('path'); -test('dark signature output', async (t) => { - const expected = fs.readFileSync(path.resolve('tests/sample/dark/signature-dark.html')); +test('dark signature output', async t => { + const expected = fs.readFileSync( + path.resolve('tests/sample/dark/signature-dark.html') + ); const built = fs.readFileSync(path.resolve('dist/dark/signature-dark.html')); t.deepEqual(expected, built); }); -test('dark signature reply output', async (t) => { - const expected = fs.readFileSync(path.resolve('tests/sample/dark/signature-reply-dark.html')); - const built = fs.readFileSync(path.resolve('dist/dark/signature-reply-dark.html')); +test('dark signature reply output', async t => { + const expected = fs.readFileSync( + path.resolve('tests/sample/dark/signature-reply-dark.html') + ); + const built = fs.readFileSync( + path.resolve('dist/dark/signature-reply-dark.html') + ); t.deepEqual(expected, built); }); -test('light signature output', async (t) => { - const expected = fs.readFileSync(path.resolve('tests/sample/light/signature-light.html')); - const built = fs.readFileSync(path.resolve('dist/light/signature-light.html')); +test('light signature output', async t => { + const expected = fs.readFileSync( + path.resolve('tests/sample/light/signature-light.html') + ); + const built = fs.readFileSync( + path.resolve('dist/light/signature-light.html') + ); t.deepEqual(expected, built); }); -test('light signature reply output', async (t) => { - const expected = fs.readFileSync(path.resolve('tests/sample/light/signature-reply-light.html')); - const built = fs.readFileSync(path.resolve('dist/light/signature-reply-light.html')); +test('light signature reply output', async t => { + const expected = fs.readFileSync( + path.resolve('tests/sample/light/signature-reply-light.html') + ); + const built = fs.readFileSync( + path.resolve('dist/light/signature-reply-light.html') + ); t.deepEqual(expected, built); }); -test('light full mail output', async (t) => { - const expected = fs.readFileSync(path.resolve('tests/sample/light/full-mail-light.html')); - const built = fs.readFileSync(path.resolve('dist/light/full-mail-light.html')); +test('light full mail output', async t => { + const expected = fs.readFileSync( + path.resolve('tests/sample/light/full-mail-light.html') + ); + const built = fs.readFileSync( + path.resolve('dist/light/full-mail-light.html') + ); t.deepEqual(expected, built); });