├── .gitignore ├── .github └── workflows │ └── test.yml ├── test ├── fixtures │ ├── pass.html │ ├── missing-jquery.html │ ├── missing-charset.html │ ├── missing-doctype.html │ └── cols-redundant.html └── bootlint_test.js ├── package.json ├── LICENSE ├── CONTRIBUTING.md ├── Gruntfile.js ├── tasks └── bootlint.js ├── README.md └── .eslintrc.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | tmp 4 | *.swp 5 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | on: [push, pull_request] 3 | env: 4 | CI: true 5 | 6 | jobs: 7 | run: 8 | name: Node ${{ matrix.node }} 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | node: [6, 8, 10, 12, 14] 15 | 16 | steps: 17 | - name: Clone repository 18 | uses: actions/checkout@v2 19 | 20 | - name: Set up Node.js 21 | uses: actions/setup-node@v1 22 | with: 23 | node-version: ${{ matrix.node }} 24 | 25 | - name: Install npm dependencies 26 | run: npm install # switch to `npm ci` when Node.js 6.x is dropped 27 | 28 | - name: Run tests 29 | run: npm test 30 | -------------------------------------------------------------------------------- /test/fixtures/pass.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Test 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
    22 | 23 | 24 | -------------------------------------------------------------------------------- /test/fixtures/missing-jquery.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Test 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    20 |
      21 |
    1. 22 |
    23 | 24 | 25 | -------------------------------------------------------------------------------- /test/fixtures/missing-charset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    20 |
      21 |
    1. 22 |
    23 | 24 | 25 | -------------------------------------------------------------------------------- /test/fixtures/missing-doctype.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Test 7 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    20 |
      21 |
    1. 22 |
    23 | 24 | 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "grunt-bootlint", 3 | "description": "A Grunt wrapper for Bootlint, the HTML linter for Bootstrap projects", 4 | "version": "1.1.0", 5 | "homepage": "https://github.com/twbs/grunt-bootlint", 6 | "author": "Zac Echola ", 7 | "repository": { 8 | "type": "git", 9 | "url": "git+https://github.com/twbs/grunt-bootlint.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/twbs/grunt-bootlint/issues" 13 | }, 14 | "license": "MIT", 15 | "keywords": [ 16 | "gruntplugin", 17 | "bootstrap", 18 | "lint" 19 | ], 20 | "engines": { 21 | "node": ">=6" 22 | }, 23 | "files": [ 24 | "tasks/*.js" 25 | ], 26 | "scripts": { 27 | "test": "grunt test" 28 | }, 29 | "dependencies": { 30 | "bootlint": "^1.1.0", 31 | "chalk": "^2.4.2", 32 | "micromatch": "^3.1.10" 33 | }, 34 | "devDependencies": { 35 | "grunt": "1.3.0", 36 | "grunt-contrib-clean": "^2.0.0", 37 | "grunt-contrib-nodeunit": "^2.1.0", 38 | "grunt-eslint": "^21.1.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Zac Echola 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Modifying the code 4 | First, ensure that you have the latest [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed. 5 | 6 | Test that Grunt's CLI is installed by running `grunt --version`. If the command isn't found, run `npm install -g grunt-cli`. For more information about installing Grunt, see the [getting started with Grunt guide](http://gruntjs.com/getting-started). 7 | 8 | 1. Fork and clone the repo. 9 | 2. Run `npm install` to install all build dependencies (including Grunt). 10 | 3. Run `grunt` to grunt this project. 11 | 12 | Assuming that you don't see any red, you're ready to go. Just be sure to run `grunt` after making any changes, to ensure that nothing is broken. 13 | 14 | ## Submitting pull requests 15 | 16 | 1. Create a new branch, please don't work in your `master` branch directly. 17 | 2. Add failing tests for the change you want to make. Run `grunt` to see the tests fail. 18 | 3. Fix stuff. 19 | 4. Run `grunt` to see if the tests pass. Repeat steps 2-4 until done. 20 | 5. Update the documentation to reflect any changes. 21 | 6. Push to your fork and submit a pull request. 22 | 23 | ## Licensing 24 | By contributing your code, you agree to license your contribution under [the MIT License](https://github.com/twbs/grunt-bootlint/blob/master/LICENSE). 25 | 26 | ## Shipping a release 27 | 1. Update the `version` field in `package.json` 28 | 2. Add an entry in the "Release History" section of `README.md`. 29 | 3. Ensure the changes in (2) and (3) have been committed and pushed to `master`. 30 | 4. Tag a new [Release](https://github.com/twbs/grunt-bootlint/releases) in GitHub and write a summary of the changes in the Release's description. 31 | 5. `git checkout master` and `git pull` 32 | 6. `git status` to ensure you have no uncommitted local changes or extraneous untracked files. 33 | 7. `npm publish` 34 | 8. Done. Congratulations! 35 | -------------------------------------------------------------------------------- /test/fixtures/cols-redundant.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Test 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
    21 |
    22 |
    Single-digit columns; sm-lg redundant; random classes interleaved
    23 |
    24 |
    25 |
    Double-digit columns; xs-md redundant; random classes interleaved
    26 |
    27 |
    28 |
    xs-lg redundant
    29 |
    30 |
    31 |
    xs-sm redundant
    32 |
    33 |
    34 |
    sm-md redundant
    35 |
    36 |
    37 |
    md-lg redundant
    38 |
    39 |
    40 | 41 |
    42 |
      43 |
    1. 44 |
    2. 45 |
    3. 46 |
    4. 47 |
    5. 48 |
    6. 49 |
    50 | 51 | 52 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-bootlint 3 | * https://github.com/twbs/grunt-bootlint 4 | * 5 | * Copyright (c) 2014 Zac Echola 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | module.exports = function(grunt) { 12 | // Project configuration. 13 | grunt.initConfig({ 14 | eslint: { 15 | all: [ 16 | 'Gruntfile.js', 17 | 'tasks/*.js', 18 | '<%= nodeunit.tests %>' 19 | ], 20 | options: { 21 | configFile: '.eslintrc.json' 22 | } 23 | }, 24 | 25 | // Before generating any new files, remove any previously-created files. 26 | clean: { 27 | tests: 'tmp' 28 | }, 29 | 30 | // Configuration to be run (and then tested). 31 | bootlint: { 32 | defaultOptions: { 33 | files: { 34 | 'tmp/defaultOptions': 'test/fixtures/**.html' 35 | } 36 | }, 37 | relaxerror: { 38 | options: { 39 | relaxerror: { 40 | 'E001': [], 41 | 'W005': 'test/fixtures/missing-jquery.html' 42 | } 43 | }, 44 | files: { 45 | 'tmp/relaxerror': [ 46 | 'test/fixtures/missing-doctype.html', 47 | 'test/fixtures/missing-charset.html', 48 | 'test/fixtures/missing-jquery.html' 49 | ] 50 | } 51 | }, 52 | stoponerror: { 53 | options: { 54 | stoponerror: true 55 | }, 56 | files: { 57 | 'tmp/stoponerror': [ 58 | 'test/fixtures/missing-doctype.html', 59 | 'test/fixtures/missing-charset.html', 60 | 'test/fixtures/cols-redundant.html' 61 | ] 62 | } 63 | }, 64 | stoponwarning: { 65 | options: { 66 | stoponwarning: true 67 | }, 68 | files: { 69 | 'tmp/stoponwarning': [ 70 | 'test/fixtures/missing-doctype.html', 71 | 'test/fixtures/missing-charset.html', 72 | 'test/fixtures/cols-redundant.html' 73 | ] 74 | } 75 | }, 76 | showallerrors: { 77 | options: { 78 | showallerrors: true 79 | }, 80 | files: { 81 | 'tmp/stoponwarning': [ 82 | 'test/fixtures/missing-doctype.html', 83 | 'test/fixtures/missing-charset.html', 84 | 'test/fixtures/cols-redundant.html' 85 | ] 86 | } 87 | }, 88 | showallerrorswithstop: { 89 | options: { 90 | showallerrors: true, 91 | stoponwarning: true 92 | }, 93 | files: { 94 | 'tmp/stoponwarning': [ 95 | 'test/fixtures/missing-doctype.html', 96 | 'test/fixtures/missing-charset.html', 97 | 'test/fixtures/cols-redundant.html' 98 | ] 99 | } 100 | }, 101 | stoponboth: { 102 | options: { 103 | stoponwarning: true, 104 | stoponerror: true 105 | }, 106 | files: { 107 | 'tmp/stoponboth': 'test/fixtures/**.html' 108 | } 109 | }, 110 | pass: { 111 | files: { 112 | 'tmp/pass': 'test/fixtures/pass.html' 113 | } 114 | } 115 | }, 116 | 117 | // Unit tests. 118 | nodeunit: { 119 | tests: 'test/*_test.js' 120 | } 121 | 122 | }); 123 | 124 | // Actually load this plugin's task(s). 125 | grunt.loadTasks('tasks'); 126 | 127 | // These plugins provide necessary tasks. 128 | grunt.loadNpmTasks('grunt-eslint'); 129 | grunt.loadNpmTasks('grunt-contrib-clean'); 130 | grunt.loadNpmTasks('grunt-contrib-nodeunit'); 131 | 132 | // Whenever the "test" task is run, first clean the "tmp" dir, then lint, 133 | // then test the result. 134 | grunt.registerTask('test', ['clean', 'eslint', 'nodeunit']); 135 | 136 | // By default, run all tests. 137 | grunt.registerTask('default', 'test'); 138 | }; 139 | -------------------------------------------------------------------------------- /tasks/bootlint.js: -------------------------------------------------------------------------------- 1 | /* 2 | * grunt-bootlint 3 | * https://github.com/twbs/grunt-bootlint 4 | * 5 | * Copyright (c) 2014-2015 Zac Echola 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | const bootlint = require('bootlint'); 12 | const chalk = require('chalk'); 13 | const micromatch = require('micromatch'); 14 | 15 | module.exports = function(grunt) { 16 | grunt.registerMultiTask('bootlint', 'An HTML linter for Bootstrap projects', function() { 17 | const options = this.options({ 18 | stoponerror: false, 19 | stoponwarning: false, 20 | showallerrors: false, 21 | relaxerror: [] 22 | }); 23 | 24 | let totalErrCount = 0; 25 | let totalFileCount = 0; 26 | 27 | function getDisabledIdsForFilepath(filepath) { 28 | // Relaxerror defined as array without filepaths 29 | if (Array.isArray(options.relaxerror)) { 30 | return options.relaxerror; 31 | } 32 | 33 | // Relaxerror as object with error IDs as keys and filepaths as values 34 | const disabledIds = Object.keys(options.relaxerror); 35 | 36 | // Lookup disabled IDs filepaths 37 | const returnIds = disabledIds.filter((key) => { 38 | const paths = options.relaxerror[key]; 39 | 40 | // handle 'E001': true, 'E001': [] 41 | if (!Array.isArray(paths) || paths.length === 0) { 42 | return true; 43 | } 44 | 45 | // handle 'E001': ['*'] 46 | if (paths.includes('*')) { 47 | return true; 48 | } 49 | 50 | // test filepath pattern 51 | return micromatch.any(filepath, paths); 52 | }); 53 | 54 | return returnIds; 55 | } 56 | 57 | // Iterate over all specified file groups. 58 | this.files.forEach((f) => { 59 | f.src.filter((filepath) => { 60 | if (!grunt.file.exists(filepath)) { 61 | grunt.log.warn(`Source file "${filepath}" not found.`); 62 | return false; 63 | } 64 | return true; 65 | }) 66 | .forEach((filepath) => { 67 | const src = grunt.file.read(filepath); 68 | const disabledIds = getDisabledIdsForFilepath(filepath); 69 | const reporter = (lint) => { 70 | const isError = lint.id.startsWith('E'); 71 | const isWarning = lint.id.startsWith('W'); 72 | const lintId = isError ? chalk.bgGreen.white(lint.id) : chalk.bgRed.white(lint.id); 73 | let output = false; 74 | 75 | if (lint.elements) { 76 | lint.elements.each((_, element) => { 77 | const loc = element.startLocation; 78 | 79 | grunt.log.warn(`${filepath}:${loc.line + 1}:${loc.column + 1}`, lintId, lint.message); 80 | totalErrCount++; 81 | output = true; 82 | }); 83 | } 84 | 85 | if (!output) { 86 | grunt.log.warn(`${filepath}:`, lintId, lint.message); 87 | totalErrCount++; 88 | } 89 | 90 | if (!options.showallerrors) { 91 | if ((isError && options.stoponerror) || (isWarning && options.stoponwarning)) { 92 | grunt.fail.warn('Too many bootlint errors.'); 93 | } 94 | } 95 | }; 96 | 97 | bootlint.lintHtml(src, reporter, disabledIds); 98 | totalFileCount++; 99 | }); 100 | 101 | const errorStr = grunt.util.pluralize(totalErrCount, 'error/errors'); 102 | const fileStr = grunt.util.pluralize(totalFileCount, 'file/files'); 103 | 104 | if (totalErrCount > 0) { 105 | if (options.showallerrors) { 106 | grunt.fail.warn(`${totalErrCount} lint ${errorStr} found across ${totalFileCount} ${fileStr}.`); 107 | } else { 108 | grunt.log.writeln().fail(`${totalErrCount} lint ${errorStr} found across ${totalFileCount} ${fileStr}.`); 109 | grunt.log.writeln().fail('For details, look up the lint problem IDs in the Bootlint wiki: https://github.com/twbs/bootlint/wiki'); 110 | } 111 | } else { 112 | grunt.log.ok(`${totalFileCount} ${fileStr} lint free.`); 113 | } 114 | }); 115 | }); 116 | }; 117 | -------------------------------------------------------------------------------- /test/bootlint_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const grunt = require('grunt'); 4 | 5 | /* 6 | ======== A Handy Little Nodeunit Reference ======== 7 | https://github.com/caolan/nodeunit 8 | 9 | Test methods: 10 | test.expect(numAssertions) 11 | test.done() 12 | Test assertions: 13 | test.ok(value, [message]) 14 | test.equal(actual, expected, [message]) 15 | test.notEqual(actual, expected, [message]) 16 | test.deepEqual(actual, expected, [message]) 17 | test.notDeepEqual(actual, expected, [message]) 18 | test.strictEqual(actual, expected, [message]) 19 | test.notStrictEqual(actual, expected, [message]) 20 | test.throws(block, [error], [message]) 21 | test.doesNotThrow(block, [error], [message]) 22 | test.ifError(value) 23 | */ 24 | 25 | exports.bootlint = { 26 | defaultOptions(test) { 27 | test.expect(4); 28 | grunt.util.spawn({ 29 | grunt: true, 30 | args: ['bootlint:defaultOptions', '--no-color'] 31 | }, (err, result) => { 32 | test.strictEqual(err, null); 33 | test.ok(result.stdout.includes('test/fixtures/missing-doctype.html'), 34 | 'Should print file path'); 35 | test.ok(result.stdout.includes('Document is missing a DOCTYPE declaration'), 36 | 'Should warn about missing a DOCTYPE'); 37 | test.ok(result.stdout.includes('9 lint errors found across 5 files'), 38 | 'Should print number of lint errors and files'); 39 | test.done(); 40 | }); 41 | }, 42 | relaxerror(test) { 43 | test.expect(5); 44 | grunt.util.spawn({ 45 | grunt: true, 46 | args: ['bootlint:relaxerror', '--no-color'] 47 | }, (err, result) => { 48 | test.strictEqual(err, null); 49 | test.ok(!result.stdout.includes('E001'), 50 | 'Should not warn about missing a DOCTYPE'); 51 | test.ok(result.stdout.includes('W001'), 52 | 'Should warn about missing charset'); 53 | test.ok(!result.stdout.includes('W005'), 54 | 'Should not warn about missing jQuery'); 55 | test.ok(result.stdout.includes('1 lint error found across 3 files'), 56 | 'Should print correct number of lint errors and files'); 57 | test.done(); 58 | }); 59 | }, 60 | stoponerror(test) { 61 | test.expect(3); 62 | grunt.util.spawn({ 63 | grunt: true, 64 | args: ['bootlint:stoponerror', '--no-color'] 65 | }, (err, result) => { 66 | test.throws(err); 67 | test.ok(result.stdout.includes('E001'), 68 | 'Should warn about missing a DOCTYPE'); 69 | test.ok(!result.stdout.includes('W001'), 70 | 'Should not warn about anything after E001'); 71 | test.done(); 72 | }); 73 | }, 74 | stoponwarning(test) { 75 | test.expect(4); 76 | grunt.util.spawn({ 77 | grunt: true, 78 | args: ['bootlint:stoponwarning', '--no-color'] 79 | }, (err, result) => { 80 | test.throws(err); 81 | test.ok(result.stdout.includes('E001'), 82 | 'Should display error of missing a DOCTYPE'); 83 | test.ok(result.stdout.includes('W001'), 84 | 'Should warn about W001'); 85 | test.ok(!result.stdout.includes('E029'), 86 | 'Should not warn about anything after W001'); 87 | test.done(); 88 | }); 89 | }, 90 | stoponboth(test) { 91 | test.expect(2); 92 | grunt.util.spawn({ 93 | grunt: true, 94 | args: ['bootlint:stoponboth', '--no-color'] 95 | }, (err, result) => { 96 | test.throws(err); 97 | test.ok(!result.stdout.includes('E001'), 98 | 'Should not warn about E001'); 99 | test.done(); 100 | }); 101 | }, 102 | showallerrors(test) { 103 | test.expect(2); 104 | grunt.util.spawn({ 105 | grunt: true, 106 | args: ['bootlint:showallerrors', '--no-color'] 107 | }, (err, result) => { 108 | test.throws(err); 109 | test.ok(result.stdout.includes('8 lint errors found across 3 files. Use --force to continue.'), 110 | 'Should show all errors before hard fail.'); 111 | test.done(); 112 | }); 113 | }, 114 | showallerrorswithstop(test) { 115 | test.expect(2); 116 | grunt.util.spawn({ 117 | grunt: true, 118 | args: ['bootlint:showallerrorswithstop', '--no-color'] 119 | }, (err, result) => { 120 | test.throws(err); 121 | test.ok(result.stdout.includes('8 lint errors found across 3 files. Use --force to continue.'), 122 | 'Should show all errors before hard fail even if stopon* is set.'); 123 | test.done(); 124 | }); 125 | }, 126 | pass(test) { 127 | test.expect(2); 128 | grunt.util.spawn({ 129 | grunt: true, 130 | args: ['bootlint:pass', '--no-color'] 131 | }, (err, result) => { 132 | test.strictEqual(err, null); 133 | test.ok(result.stdout.includes('1 file lint free.'), 134 | 'Should print correct number of lint free files'); 135 | test.done(); 136 | }); 137 | } 138 | }; 139 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grunt-bootlint 2 | 3 | > A Grunt wrapper for [Bootlint](https://www.npmjs.org/package/bootlint), the HTML linter for [Bootstrap](https://getbootstrap.com/) projects 4 | 5 | [![NPM version](https://img.shields.io/npm/v/grunt-bootlint.svg)](https://www.npmjs.com/package/grunt-bootlint) 6 | [![Build Status](https://github.com/twbs/grunt-bootlint/workflows/Tests/badge.svg)](https://github.com/twbs/grunt-bootlint/actions?workflow=Tests) 7 | [![MIT License](https://img.shields.io/npm/l/grunt-bootlint.svg)](https://github.com/twbs/grunt-bootlint/blob/master/LICENSE) 8 | [![Dependency Status](https://img.shields.io/david/twbs/grunt-bootlint.svg)](https://david-dm.org/twbs/grunt-bootlint) 9 | [![devDependency Status](https://img.shields.io/david/dev/twbs/grunt-bootlint.svg)](https://david-dm.org/twbs/grunt-bootlint?type=dev) 10 | 11 | ## Getting Started 12 | 13 | If you haven't used [Grunt](https://gruntjs.com/) before, be sure to check out the [Getting Started](https://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](https://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command: 14 | 15 | ```shell 16 | npm install grunt-bootlint --save-dev 17 | ``` 18 | 19 | Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript: 20 | 21 | ```js 22 | grunt.loadNpmTasks('grunt-bootlint'); 23 | ``` 24 | 25 | ## The "bootlint" task 26 | 27 | ### Overview 28 | 29 | In your project's Gruntfile, add a section named `bootlint` to the data object passed into `grunt.initConfig()`. 30 | 31 | ```js 32 | grunt.initConfig({ 33 | bootlint: { 34 | options: { 35 | stoponerror: false, 36 | relaxerror: [] 37 | }, 38 | files: ['path/to/file.html', 'path/to/*.html'] 39 | } 40 | }); 41 | ``` 42 | 43 | ### Options 44 | 45 | ### Usage Examples 46 | 47 | #### Default Options 48 | 49 | In this example, the default options are used to lint files for common problems in bootstrap. 50 | 51 | ```js 52 | grunt.initConfig({ 53 | bootlint: { 54 | options: { 55 | relaxerror: [], 56 | showallerrors: false, 57 | stoponerror: false, 58 | stoponwarning: false 59 | }, 60 | files: ['test/fixtures/*.html'] 61 | } 62 | }); 63 | ``` 64 | 65 | ### Settings 66 | 67 | #### options.stoponerror 68 | 69 | * Type: `Boolean` 70 | * Default: `false` 71 | 72 | Breaks out of grunt task on first error problem ID. Use `--force` to force continue. 73 | 74 | #### options.stoponwarning 75 | 76 | * Type: `Boolean` 77 | * Default: `false` 78 | 79 | Breaks out of grunt task on first warning problem ID. Use `--force` to force continue. 80 | 81 | #### options.showallerrors 82 | 83 | * Type: `Boolean` 84 | * Default: `false` 85 | 86 | Shows all errors and warnings before stopping the task. (Overrides `stoponerror` and `stoponwarning`, above.) 87 | 88 | #### options.relaxerror 89 | 90 | * Type: `Array` | `Object` 91 | * Default: `[]` 92 | 93 | Array of [bootlint problem ID codes][] (`String`s) to explicitly ignore. 94 | 95 | Object of [bootlint problem ID codes][] as **keys** and filepath globs as array **value**. 96 | 97 | ##### Example 98 | 99 | ```js 100 | relaxerror: { 101 | 'E001': [], 102 | 'W005': [ 103 | 'path/to/file.html', 104 | 'file/path/*.glob' 105 | ] 106 | }, 107 | ``` 108 | 109 | ## Contributing 110 | 111 | In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](https://gruntjs.com/). 112 | 113 | ## Release History 114 | 115 | * 2016-04-05 - v0.10.1: Updates for Grunt 1.0.0 compatibility and adds pluralize for files/errors. 116 | * 2015-11-24 - v0.10.0: Updates Bootlint to v0.14.1 and adds the ability to ignore lint problems on a per-file basis using `relaxerror`. 117 | * 2015-06-01 - v0.9.1: Minor update to license metadata. 118 | * 2015-03-16 - v0.9.0: Updates Bootlint to v0.12.0 119 | * 2015-02-25 - v0.8.0: Updates Bootlint to v0.11.0 120 | * 2015-01-23 - v0.7.0: Updates Bootlint to v0.10.0 121 | * 2014-12-23 - v0.6.0: Updates Bootlint to v0.9.1 122 | * 2014-11-12 - v0.5.3: Fixes issue with `stoponerror` option 123 | * 2014-11-12 - ~~v0.5.2: Fixes issue with `stoponerror` option~~ *This was a bad release. Please upgrade.* 124 | * 2014-11-10 - v0.5.1: Displays message when files pass 125 | * 2014-11-10 - v0.5.0: Updates Bootlint, adds line/col numbers to output, quieter output. 126 | * 2014-11-03 - v0.4.0: Updates Bootlint dependency. 127 | * 2014-10-17 - v0.3.0: Basic support for Bootlint 0.5.0. **Changes `relaxerror` to use Bootlint problem IDs** 128 | * 2014-09-25 - v0.2.1: Removes color dependency. 129 | * 2014-09-25 - v0.2.0: First formal release. 130 | 131 | ## License and copyright 132 | 133 | Code released under [the MIT license](https://github.com/twbs/grunt-bootlint/blob/master/LICENSE). 134 | 135 | [bootlint problem ID codes]: https://github.com/twbs/bootlint/wiki 136 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "rules": { 9 | "array-bracket-spacing": [ 10 | "error", 11 | "never" 12 | ], 13 | "array-callback-return": "error", 14 | "arrow-body-style": [ 15 | "error", 16 | "always" 17 | ], 18 | "arrow-parens": [ 19 | "error", 20 | "always" 21 | ], 22 | "arrow-spacing": "error", 23 | "block-scoped-var": "error", 24 | "block-spacing": "error", 25 | "brace-style": [ 26 | "error", 27 | "1tbs" 28 | ], 29 | "callback-return": "error", 30 | "camelcase": [ 31 | "error", 32 | { 33 | "properties": "never" 34 | } 35 | ], 36 | "comma-dangle": "error", 37 | "comma-spacing": [ 38 | "error", 39 | { 40 | "before": false, 41 | "after": true 42 | } 43 | ], 44 | "comma-style": [ 45 | "error", 46 | "last" 47 | ], 48 | "computed-property-spacing": [ 49 | "error", 50 | "never" 51 | ], 52 | "consistent-return": "error", 53 | "consistent-this": "off", 54 | "curly": "error", 55 | "default-case": "error", 56 | "dot-notation": "error", 57 | "eol-last": "error", 58 | "eqeqeq": "error", 59 | "func-call-spacing": "error", 60 | "func-names": [ 61 | "off", 62 | "never" 63 | ], 64 | "func-style": [ 65 | "off", 66 | "declaration" 67 | ], 68 | "global-require": "error", 69 | "guard-for-in": "error", 70 | "handle-callback-err": "error", 71 | "indent": [ 72 | "error", 73 | 2, 74 | { 75 | "MemberExpression": "off", 76 | "SwitchCase": 1 77 | } 78 | ], 79 | "init-declarations": [ 80 | "off", 81 | "always" 82 | ], 83 | "key-spacing": [ 84 | "error", 85 | { 86 | "beforeColon": false, 87 | "afterColon": true, 88 | "mode": "minimum" 89 | } 90 | ], 91 | "keyword-spacing": [ 92 | "error", 93 | { 94 | "before": true, 95 | "after": true 96 | } 97 | ], 98 | "max-statements-per-line": [ 99 | "error", 100 | { 101 | "max": 1 102 | } 103 | ], 104 | "multiline-ternary": [ 105 | "error", 106 | "always-multiline" 107 | ], 108 | "new-cap": [ 109 | "error", 110 | { 111 | "properties": false 112 | } 113 | ], 114 | "newline-after-var": [ 115 | "off", 116 | "always" 117 | ], 118 | "newline-per-chained-call": [ 119 | "off", 120 | { 121 | "ignoreChainWithDepth": 3 122 | } 123 | ], 124 | "new-parens": "error", 125 | "no-alert": "error", 126 | "no-array-constructor": "error", 127 | "no-bitwise": "error", 128 | "no-buffer-constructor": "error", 129 | "no-caller": "error", 130 | "no-catch-shadow": "error", 131 | "no-confusing-arrow": "error", 132 | "no-console": "off", 133 | "no-constant-condition": ["error", 134 | { 135 | "checkLoops": false 136 | } 137 | ], 138 | "no-div-regex": "error", 139 | "no-duplicate-imports": "error", 140 | "no-else-return": "error", 141 | "no-empty": "error", 142 | "no-empty-function": "error", 143 | "no-eq-null": "error", 144 | "no-eval": "error", 145 | "no-extra-bind": "error", 146 | "no-extra-label": "error", 147 | "no-extra-parens": [ 148 | "error", 149 | "all", 150 | { 151 | "nestedBinaryExpressions": false 152 | } 153 | ], 154 | "no-floating-decimal": "error", 155 | "no-global-assign": "error", 156 | "no-implicit-coercion": "error", 157 | "no-implicit-globals": "error", 158 | "no-implied-eval": "error", 159 | "no-inline-comments": "off", 160 | "no-label-var": "error", 161 | "no-lone-blocks": "error", 162 | "no-lonely-if": "error", 163 | "no-loop-func": "error", 164 | "no-magic-numbers": [ 165 | "off", 166 | { 167 | "ignoreArrayIndexes": true 168 | } 169 | ], 170 | "no-mixed-operators": "error", 171 | "no-mixed-requires": "error", 172 | "no-multiple-empty-lines": "error", 173 | "no-multi-spaces": [ 174 | "error", 175 | { 176 | "ignoreEOLComments": true, 177 | "exceptions": { 178 | "Property": true, 179 | "VariableDeclarator": true 180 | } 181 | } 182 | ], 183 | "no-multi-str": "error", 184 | "no-negated-condition": "error", 185 | "no-nested-ternary": "error", 186 | "no-new-object": "error", 187 | "no-octal": "error", 188 | "no-param-reassign": [ 189 | "off", 190 | { 191 | "props": false 192 | } 193 | ], 194 | "no-path-concat": "error", 195 | "no-prototype-builtins": "error", 196 | "no-return-assign": "error", 197 | "no-self-compare": "error", 198 | "no-sequences": "error", 199 | "no-shadow": "error", 200 | "no-shadow-restricted-names": "error", 201 | "no-throw-literal": "error", 202 | "no-trailing-spaces": "error", 203 | "no-undefined": "off", 204 | "no-undef-init": "error", 205 | "no-underscore-dangle": "error", 206 | "no-unmodified-loop-condition": "error", 207 | "no-unneeded-ternary": "error", 208 | "no-unsafe-negation": "error", 209 | "no-unused-expressions": "error", 210 | "no-unused-labels": "error", 211 | "no-use-before-define": "error", 212 | "no-useless-call": "error", 213 | "no-useless-computed-key": "error", 214 | "no-useless-concat": "error", 215 | "no-useless-constructor": "error", 216 | "no-useless-escape": "error", 217 | "no-useless-rename": "error", 218 | "no-var": "error", 219 | "no-void": "error", 220 | "no-whitespace-before-property": "error", 221 | "no-with": "error", 222 | "object-shorthand": "error", 223 | "object-curly-newline": [ 224 | "off", 225 | { 226 | "multiline": true, 227 | "minProperties": 2 228 | } 229 | ], 230 | "object-curly-spacing": [ 231 | "error", 232 | "never" 233 | ], 234 | "object-property-newline": "error", 235 | "one-var": [ 236 | "error", 237 | "never" 238 | ], 239 | "one-var-declaration-per-line": [ 240 | "error", 241 | "always" 242 | ], 243 | "operator-assignment": [ 244 | "error", 245 | "always" 246 | ], 247 | "operator-linebreak": [ 248 | "error", 249 | "after" 250 | ], 251 | "prefer-arrow-callback": "error", 252 | "prefer-const": "error", 253 | "prefer-destructuring": [ 254 | "error", 255 | { 256 | "object": true, 257 | "array": false 258 | } 259 | ], 260 | "prefer-spread": "error", 261 | "prefer-template": "error", 262 | "quote-props": [ 263 | "error", 264 | "consistent" 265 | ], 266 | "quotes": [ 267 | "error", 268 | "single" 269 | ], 270 | "radix": "error", 271 | "rest-spread-spacing": [ 272 | "error", 273 | "never" 274 | ], 275 | "semi": [ 276 | "error", 277 | "always" 278 | ], 279 | "semi-spacing": "error", 280 | "sort-imports": "error", 281 | "space-before-blocks": [ 282 | "error", 283 | "always" 284 | ], 285 | "space-before-function-paren": [ 286 | "error", 287 | "never" 288 | ], 289 | "spaced-comment": [ 290 | "off", 291 | "always" 292 | ], 293 | "space-infix-ops": "error", 294 | "space-in-parens": [ 295 | "error", 296 | "never" 297 | ], 298 | "space-unary-ops": "error", 299 | "strict": "error", 300 | "symbol-description": "error", 301 | "template-curly-spacing": "error", 302 | "unicode-bom": [ 303 | "error", 304 | "never" 305 | ], 306 | "use-isnan": "error", 307 | "wrap-iife": [ 308 | "error", 309 | "inside" 310 | ], 311 | "yoda": [ 312 | "error", 313 | "never" 314 | ] 315 | } 316 | } 317 | --------------------------------------------------------------------------------