Check for unused initial (untested) implementation

This commit is contained in:
Dan Mindru 2019-09-05 09:56:41 +02:00 committed by Dan Mindru
parent e599bef72e
commit d55786f744
13 changed files with 175 additions and 86 deletions

View File

@ -1,6 +1,6 @@
module.exports = {
SOURCE: 'templates',
DIST: 'dist',
SOURCE_DIR: 'templates',
DIST_DIR: 'dist',
WORKING_DIR: 'tmp',
CONFIGURATION_FILE: 'conf.json'
}

View File

@ -1,11 +1,11 @@
const gulp = require('gulp');
const plumber = require('gulp-plumber');
const { SOURCE, DIST, WORKING_DIR, CONFIGURATION_FILE } = require('./constants');
const { SOURCE_DIR, DIST_DIR, WORKING_DIR, CONFIGURATION_FILE } = require('./constants');
const options = {
source: SOURCE,
dist: DIST,
sourceDir: SOURCE_DIR,
distDir: DIST_DIR,
workingDir: WORKING_DIR,
configurationFile: CONFIGURATION_FILE,
src: function plumbedSrc() {
@ -22,11 +22,14 @@ require('./tasks/less')(options);
require('./tasks/lint')(options);
require('./tasks/postcss')(options);
require('./tasks/sass')(options);
require('./tasks/check-for-missing')(options);
require('./tasks/check-for-unused').checkForUnusedTask(options);
require('./tasks/check-deps')(options);
/* Runs the entire pipeline once. */
gulp.task('run-pipeline', gulp.series('dupe', 'less', 'sass', 'postcss', 'lint', 'build', 'check-for-missing'));
gulp.task(
'run-pipeline',
gulp.series('dupe', 'less', 'sass', 'postcss', 'lint', 'build', gulp.parallel('check-for-unused'))
);
/* By default templates will be built into '/dist'. */
gulp.task(
@ -35,11 +38,11 @@ gulp.task(
/* 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'
options.sourceDir + '/**/*.html',
options.sourceDir + '/**/*.css',
options.sourceDir + '/**/*.scss',
options.sourceDir + '/**/*.less',
options.sourceDir + '/**/conf.json'
],
{ delay: 500 },
gulp.series('run-pipeline')

6
package-lock.json generated
View File

@ -1635,9 +1635,9 @@
}
},
"chalk": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
"integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",

View File

@ -26,13 +26,13 @@
"start": "node ./node_modules/.bin/gulp",
"once": "node ./node_modules/.bin/gulp run-pipeline",
"deploy": "npm run test && cp -r dist demo && git push origin `git subtree split --prefix demo develop`:gh-pages --force",
"test": "npm run once && node ./node_modules/.bin/ava",
"test:watch": "npm run once && node ./node_modules/.bin/ava --watch",
"format": "node ./node_modules/.bin/prettier {tasks,tests}/**/*.js gulpfile.js .eslintrc.js --write",
"lint": "node ./node_modules/.bin/eslint ./**/*.js gulpfile.js"
"e2e-test": "npm run once && node ./node_modules/.bin/ava",
"format": "prettier {tasks,tests}/**/*.js gulpfile.js .eslintrc.js --write",
"lint": "eslint ./**/*.js gulpfile.js"
},
"dependencies": {
"autoprefixer": "^9.7.4",
"autoprefixer": "^9.6.1",
"chalk": "^2.4.2",
"del": "^5.1.0",
"graceful-fs": "^4.2.3",
"gulp": "^4.0.2",

View File

@ -58,7 +58,7 @@ function buildTask(options) {
return path;
})
)
.pipe(gulp.dest(options.dist));
.pipe(gulp.dest(options.distDir));
});
}
@ -66,7 +66,7 @@ function buildTask(options) {
* 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)
return del(options.distDir)
.then(() => {
/**
* Loop through dirs and load their conf files.

View File

@ -1,28 +1,9 @@
const gulp = require('gulp');
const { getConfigsForDir, getFilePathsForDir } = require('./util/util');
function checkForMissingTask(options) {
gulp.task('check-for-missing', done => {
const configs = getConfigsForDir(options.workingDir, options.configurationFile);
configs.map(({ dir, confItems }) => {
confItems.forEach(async confItem => {
const definedStrings = Object.keys(confItem).map(key => {
return {
src: `@echo ${key}`,
used: false
};
});
const cwd = `${options.workingDir}/${dir}`;
const files = await getFilePathsForDir(cwd);
const htmlTemplates = files.filter(file => !!file.match(/.*\.html/) && !file.match(/.*\.inc*\.html/)); // Read only CSS files.
console.log(definedStrings, htmlTemplates);
});
done();
});
function checkForUnusedTask(options) {
gulp.task('check-for-unused', async done => {
done();
});
}
module.exports = checkForMissingTask;
module.exports = checkForUnusedTask;

View File

@ -0,0 +1,81 @@
const gulp = require('gulp');
const fs = require('fs');
const chalk = require('chalk');
const { getConfigsForDir, getFilePathsForDir, log } = require('./util/util');
const OUTPUT_KEYWORD = '@echo';
// todo: needs a proper refactor.
function checkForUnusedTask(options) {
gulp.task('check-for-unused', async done => {
const configs = getConfigsForDir(options.workingDir, options.configurationFile);
const unusedItems = await checkForUnusedItemsInConfigs(options.workingDir, configs);
outputWarningsForUnusedItems(unusedItems, configs);
done();
});
}
// todo: find configs by id instead of using the index?
const outputWarningsForUnusedItems = (unusedItems, configs) => {
const find = OUTPUT_KEYWORD;
const regex = new RegExp(find, 'g');
unusedItems.forEach((unusedInConfigs, index) => {
const { dir } = configs[index];
unusedInConfigs.forEach((unusedInConfItems, index) => {
log.warn(
`${unusedInConfItems.length} unused properties in ${dir}: ${unusedInConfItems
.reduce((acc, cur) => (acc ? `${acc}, ${chalk.white(cur)}` : chalk.white(cur)), '')
.replace(regex, '')}`
);
});
});
};
const checkForUnusedItemsInConfigs = (rootDir, configs) => {
return Promise.all(
configs.map(async ({ dir, confItems }) => {
return Promise.all(
confItems.map(async confItem => {
const definedStrings = Object.keys(confItem).map(key => `${OUTPUT_KEYWORD} ${key}`);
const cwd = `${rootDir}/${dir}`;
const files = await getFilePathsForDir(cwd);
const htmlTemplates = await self.getHtmlTemplatesFromFilelist(files);
const concatenatedTemplates = htmlTemplates.join('');
return definedStrings.filter(str => concatenatedTemplates.includes(str));
})
);
})
);
};
// todo: should be util, so should the one in build.js
const getHtmlTemplatesFromFilelist = filelist => {
return Promise.all(
filelist
.filter(file => !!file.match(/.*\.html/) && !file.match(/.*\.inc*\.html/))
.map(
htmlTemplate =>
new Promise((resolve, reject) => {
fs.readFile(htmlTemplate, 'utf8', (error, data) => {
if (error) {
reject(error);
}
resolve(data);
});
})
)
);
};
const self = {
checkForUnusedTask,
outputWarningsForUnusedItems,
checkForUnusedItemsInConfigs,
getHtmlTemplatesFromFilelist
};
module.exports = self;

View File

@ -5,7 +5,7 @@ function dupeTask(options) {
gulp.task('dupe', function() {
del.sync([options.workingDir]);
return options.src([options.source + '/**/*']).pipe(gulp.dest('./' + options.workingDir));
return options.src([options.sourceDir + '/**/*']).pipe(gulp.dest('./' + options.workingDir));
});
}

View File

@ -1,7 +1,9 @@
const fs = require('fs');
const path = require('path');
const klaw = require('klaw');
const chalk = require('chalk');
// todo test
/**
* Given a directory, scans all directories in it (not deep) and returns found config items.
*
@ -16,7 +18,7 @@ const getConfigsForDir = (rootDir, configFileName) => {
/** Exit with warn if no configuration file found. */
if (!fs.existsSync(path.resolve(rootDir, confPath))) {
console.warn(`Missing configuration in "${dir}". Did you remember to create "${dir}/${configFileName}"?`);
self.log.warn(`Missing configuration in "${dir}". Did you remember to create "${dir}/${configFileName}"?`);
return false;
}
@ -41,8 +43,11 @@ const getConfigsForDir = (rootDir, configFileName) => {
.filter(config => config);
};
// todo test
/**
* Given a directory, gets all file paths in it.
*
* @param { string } dir Dir to get files paths for.
*/
const getFilePathsForDir = dir => {
const files = [];
@ -63,7 +68,24 @@ const getFilePathsForDir = dir => {
});
};
module.exports = {
const log = {
warn: (...messages) => {
console.warn('🔵 ', chalk.yellow(messages));
},
log: (...messages) => {
console.log('🔘 ', chalk.gray(messages));
},
error: (...messages) => {
console.error('🔴 ', chalk.red(messages));
}
};
const self = {
log,
getConfigsForDir,
getFilePathsForDir
};
module.exports = self;

View File

@ -0,0 +1,40 @@
const test = require('ava');
const fs = require('fs');
const path = require('path');
const readFileSync = path => fs.readFileSync(('../', path), 'utf8');
test('dark signature output', async t => {
const expected = readFileSync('tests/sample/dark/signature-dark.html');
const built = readFileSync('dist/dark/signature-dark.html');
t.deepEqual(expected, built);
});
test('dark signature reply output', async t => {
const expected = readFileSync('tests/sample/dark/signature-reply-dark.html');
const built = readFileSync('dist/dark/signature-reply-dark.html');
t.deepEqual(expected, built);
});
test('light signature output', async t => {
const expected = readFileSync('tests/sample/light/signature-light.html');
const built = readFileSync('dist/light/signature-light.html');
t.deepEqual(expected, built);
});
test('light signature reply output', async t => {
const expected = readFileSync('tests/sample/light/signature-reply-light.html');
const built = readFileSync('dist/light/signature-reply-light.html');
t.deepEqual(expected, built);
});
test('light full mail output', async t => {
const expected = readFileSync('tests/sample/light/full-mail-light.html');
const built = readFileSync('dist/light/full-mail-light.html');
t.deepEqual(expected, built);
});

View File

@ -1,38 +0,0 @@
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'), 'utf8');
const built = fs.readFileSync(path.resolve('dist/dark/signature-dark.html'), 'utf8');
t.deepEqual(expected, built);
});
test('dark signature reply output', async t => {
const expected = fs.readFileSync(path.resolve('tests/sample/dark/signature-reply-dark.html'), 'utf8');
const built = fs.readFileSync(path.resolve('dist/dark/signature-reply-dark.html'), 'utf8');
t.deepEqual(expected, built);
});
test('light signature output', async t => {
const expected = fs.readFileSync(path.resolve('tests/sample/light/signature-light.html'), 'utf8');
const built = fs.readFileSync(path.resolve('dist/light/signature-light.html'), 'utf8');
t.deepEqual(expected, built);
});
test('light signature reply output', async t => {
const expected = fs.readFileSync(path.resolve('tests/sample/light/signature-reply-light.html'), 'utf8');
const built = fs.readFileSync(path.resolve('dist/light/signature-reply-light.html'), 'utf8');
t.deepEqual(expected, built);
});
test('light full mail output', async t => {
const expected = fs.readFileSync(path.resolve('tests/sample/light/full-mail-light.html'), 'utf8');
const built = fs.readFileSync(path.resolve('dist/light/full-mail-light.html'), 'utf8');
t.deepEqual(expected, built);
});

View File

0
tests/unit/util.js Normal file
View File