From e5ad76e0d69781c977108e87d5aa4b55fbfc756e Mon Sep 17 00:00:00 2001 From: Wolfr Date: Tue, 19 Feb 2019 17:47:33 +0100 Subject: [PATCH] Add linter --- content/linter/buttons/buttons.js | 18 +++++ core/paths.js | 5 ++ core/tasks/linter.js | 50 ++++++++++++ gulpfile.js | 3 +- package-lock.json | 124 ++++++++++++++++++++++++++++++ package.json | 1 + 6 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 content/linter/buttons/buttons.js create mode 100644 core/tasks/linter.js diff --git a/content/linter/buttons/buttons.js b/content/linter/buttons/buttons.js new file mode 100644 index 00000000..d3a03d22 --- /dev/null +++ b/content/linter/buttons/buttons.js @@ -0,0 +1,18 @@ +module.exports = function ($) { + const errors = []; + let allButtonsHaveType = true; + + $('button').each(function (index, el) { + const $el = $(el); + + if (!$el.attr('type')) { + allButtonsHaveType = false; + } + }); + + if (!allButtonsHaveType) { + errors.push('Buttons need a `type` attribute.'); + } + + return errors; +}; \ No newline at end of file diff --git a/core/paths.js b/core/paths.js index 73670c85..8cae1fa3 100644 --- a/core/paths.js +++ b/core/paths.js @@ -35,8 +35,13 @@ module.exports = { moduleTemplates: path.join(contentPath, 'templates/modules/**/*.pug'), patterns: path.join(contentPath, 'templates/_patterns/'), components: path.join(contentPath, 'templates/_components/'), + allComponents: path.join(contentPath, 'templates/_components/**/*.pug'), data: path.join(contentPath, 'data/*') }, + linter: { + path: path.join(contentPath, 'linter/'), + tests: path.join(contentPath, 'linter/**/*.js'), + }, js: { entryFile: path.join(contentPath, 'js/index.js') }, diff --git a/core/tasks/linter.js b/core/tasks/linter.js new file mode 100644 index 00000000..dd2c69f1 --- /dev/null +++ b/core/tasks/linter.js @@ -0,0 +1,50 @@ +const fs = require('fs'); +const path = require('path'); +const glob = require('glob'); +const pug = require('pug'); +const cheerio = require('cheerio'); +const chalk = require('chalk'); + +const locals = require('../templates/locals'); +const paths = require('../paths'); + +const allComponentPaths = glob.sync(paths.content.templates.allComponents); +const linterPaths = glob.sync(paths.content.linter.tests); + + module.exports = function (done) { + console.log('Running linters on Pug templates within components...\n'); + + allComponentPaths.forEach(function (templatePath) { + + const fakeIcon = 'include ../../../../core/templates/mixins/icon\n' + + const pugContent = fakeIcon + fs.readFileSync(templatePath, 'utf8'); + + const compiledHtml = pug.compile(pugContent, { + pretty: true, + basedir: '/content', + filename: templatePath + })(locals.getDefaultLocals()); + + const $ = cheerio.load(compiledHtml); + + linterPaths.forEach(function (linterPath) { + const linter = require(path.join('../../', linterPath)); + + const linterResult = linter($); + + if (linterResult && linterResult.length > 0) { + console.log(chalk.underline.bold(templatePath)); + + linterResult.forEach(function (result) { + console.log('\t' + chalk.red(result)); + }); + + console.log('\n'); + } + }); + }); + + console.log('Done linting Pug files!'); + done(); +}; \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 8d7098de..ab666278 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -11,7 +11,7 @@ const copy = require('./core/tasks/copy'); const watch = require('./core/tasks/watch'); const server = require('./core/tasks/server'); const iconFont = require('./core/tasks/icon-font'); - +const linter = require('./core/tasks/linter'); const config = require('./bedrock.config'); gulp.task('templates:clean', templates.clean); @@ -24,6 +24,7 @@ gulp.task('copy:resources', copy.resources); gulp.task('copy:compiledToDist', copy.compiledToDist); gulp.task('bundle', bundle); gulp.task('icon-font', iconFont); +gulp.task('lint', linter); gulp.task('templates:compile', config.styleguide ? ['templates:compile:content', 'templates:compile:styleguide', 'templates:compile:docs'] : diff --git a/package-lock.json b/package-lock.json index a5ff8ebe..5e4b3352 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1321,6 +1321,12 @@ } } }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1775,6 +1781,20 @@ "is-regex": "^1.0.3" } }, + "cheerio": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", + "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", + "dev": true, + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + } + }, "chokidar": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.0.tgz", @@ -2276,6 +2296,24 @@ } } }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "dev": true, + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", + "dev": true + }, "currently-unhandled": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", @@ -2556,12 +2594,47 @@ "integrity": "sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=", "dev": true }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, "domain-browser": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", "dev": true }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, "duplexer": { "version": "0.1.1", "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -2786,6 +2859,12 @@ "has-binary2": "~1.0.2" } }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -4926,6 +5005,33 @@ "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=", "dev": true }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.1.1.tgz", + "integrity": "sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, "http-errors": { "version": "1.6.3", "resolved": "http://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", @@ -6624,6 +6730,15 @@ "set-blocking": "~2.0.0" } }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, "num2fraction": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", @@ -6949,6 +7064,15 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", diff --git a/package.json b/package.json index 2dc768dc..3848f0de 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "browserify": "^16.2.3", "cached-path-relative": ">=1.0.2", "chalk": "^1.1.3", + "cheerio": "^1.0.0-rc.2", "clipboard": "^1.5.3", "codemirror": "^5.18.2", "del": "^2.0.2",