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 = { module.exports = {
SOURCE: 'templates', SOURCE_DIR: 'templates',
DIST: 'dist', DIST_DIR: 'dist',
WORKING_DIR: 'tmp', WORKING_DIR: 'tmp',
CONFIGURATION_FILE: 'conf.json' CONFIGURATION_FILE: 'conf.json'
} }

View File

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

6
package-lock.json generated
View File

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

View File

@ -26,13 +26,13 @@
"start": "node ./node_modules/.bin/gulp", "start": "node ./node_modules/.bin/gulp",
"once": "node ./node_modules/.bin/gulp run-pipeline", "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", "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", "e2e-test": "npm run once && node ./node_modules/.bin/ava",
"test:watch": "npm run once && node ./node_modules/.bin/ava --watch", "format": "prettier {tasks,tests}/**/*.js gulpfile.js .eslintrc.js --write",
"format": "node ./node_modules/.bin/prettier {tasks,tests}/**/*.js gulpfile.js .eslintrc.js --write", "lint": "eslint ./**/*.js gulpfile.js"
"lint": "node ./node_modules/.bin/eslint ./**/*.js gulpfile.js"
}, },
"dependencies": { "dependencies": {
"autoprefixer": "^9.7.4", "autoprefixer": "^9.6.1",
"chalk": "^2.4.2",
"del": "^5.1.0", "del": "^5.1.0",
"graceful-fs": "^4.2.3", "graceful-fs": "^4.2.3",
"gulp": "^4.0.2", "gulp": "^4.0.2",

View File

@ -58,7 +58,7 @@ function buildTask(options) {
return path; 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. * Clean up & then read from workingDir to generate templates.
* For each found config, a template group will be generated through `makeTemplates`. * For each found config, a template group will be generated through `makeTemplates`.
*/ */
return del(options.dist) return del(options.distDir)
.then(() => { .then(() => {
/** /**
* Loop through dirs and load their conf files. * Loop through dirs and load their conf files.

View File

@ -1,28 +1,9 @@
const gulp = require('gulp'); const gulp = require('gulp');
const { getConfigsForDir, getFilePathsForDir } = require('./util/util');
function checkForMissingTask(options) { function checkForUnusedTask(options) {
gulp.task('check-for-missing', done => { gulp.task('check-for-unused', async done => {
const configs = getConfigsForDir(options.workingDir, options.configurationFile); done();
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();
});
}); });
} }
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() { gulp.task('dupe', function() {
del.sync([options.workingDir]); 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 fs = require('fs');
const path = require('path'); const path = require('path');
const klaw = require('klaw'); 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. * 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. */ /** Exit with warn if no configuration file found. */
if (!fs.existsSync(path.resolve(rootDir, confPath))) { 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; return false;
} }
@ -41,8 +43,11 @@ const getConfigsForDir = (rootDir, configFileName) => {
.filter(config => config); .filter(config => config);
}; };
// todo test
/** /**
* Given a directory, gets all file paths in it. * Given a directory, gets all file paths in it.
*
* @param { string } dir Dir to get files paths for.
*/ */
const getFilePathsForDir = dir => { const getFilePathsForDir = dir => {
const files = []; 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, getConfigsForDir,
getFilePathsForDir 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