Skip to content
This repository was archived by the owner on Feb 1, 2022. It is now read-only.

Commit 0258293

Browse files
committed
Support new grid
Ports the following checks: - W009 (empty spacer column) - E005 (row and col on same element) - E012 (input-group and col on the same element) - E013 (invalid children of rows) - E014 (invalid parent of cols) - E029 (col 0 classes) - E037 (float classes and col) - E051 (float style and col) - E052 (float style and row)
1 parent 6a7110d commit 0258293

31 files changed

+234
-268
lines changed

src/bootlint.js

+50-75
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,23 @@ var LocationIndex = _location.LocationIndex;
1616
(function (exports) {
1717
'use strict';
1818
var NUM_COLS = 12;
19-
var COL_REGEX = /\bcol-(xs|sm|md|lg)-(\d{1,2})\b/;
20-
var COL_REGEX_G = /\bcol-(xs|sm|md|lg)-(\d{1,2})\b/g;
19+
var COL_REGEX = /\bcol(?:-(sm|md|lg|xl))?(?:-(auto|\d{1,2}))?\b/;
20+
var COL_REGEX_G = /\bcol(?:-(sm|md|lg|xl))?(?:-(auto|\d{1,2}))?\b/g;
2121
var COL_CLASSES = [];
22-
var SCREENS = ['xs', 'sm', 'md', 'lg'];
22+
var SCREENS = ['', 'sm', 'md', 'lg', 'xl'];
2323
SCREENS.forEach(function (screen) {
24-
for (var n = 1; n <= NUM_COLS; n++) {
25-
COL_CLASSES.push('.col-' + screen + '-' + n);
24+
for (var n = -1; n <= NUM_COLS; n++) {
25+
COL_CLASSES.push('.col' + (screen && '-' + screen) + (n < 0 ? '' : '-' + (n || 'auto')));
2626
}
2727
});
2828
var SCREEN2NUM = {
29-
xs: 0,
30-
sm: 1,
31-
md: 2,
32-
lg: 3
29+
'': 0,
30+
'sm': 1,
31+
'md': 2,
32+
'lg': 3,
33+
'xl': 4
3334
};
34-
var NUM2SCREEN = ['xs', 'sm', 'md', 'lg'];
35+
var NUM2SCREEN = ['', 'sm', 'md', 'lg', 'xl'];
3536
var IN_NODE_JS = Boolean(cheerio.load);
3637
var MIN_JQUERY_VERSION = '1.9.1'; // as of Bootstrap v3.3.0
3738
var CURRENT_BOOTSTRAP_VERSION = '3.4.1';
@@ -131,11 +132,11 @@ var LocationIndex = _location.LocationIndex;
131132
var width2screens = {};
132133
while (true) {
133134
var match = COL_REGEX_G.exec(classes);
134-
if (!match) {
135+
if (!match || !match[1] && !match[2]) {
135136
break;
136137
}
137-
var screen = match[1];
138-
width = match[2];
138+
var screen = match[1] || '';
139+
width = match[2] || ''; // can also be 'auto'
139140
var screens = width2screens[width];
140141
if (!screens) {
141142
screens = width2screens[width] = [];
@@ -379,7 +380,7 @@ var LocationIndex = _location.LocationIndex;
379380
// deliberately do nothing
380381
// empty
381382
}
382-
/* istanbul ignore if */
383+
// istanbul ignore if
383384
if (theWindow) {
384385
// check browser global jQuery
385386
var globaljQuery = theWindow.$ || theWindow.jQuery;
@@ -476,7 +477,6 @@ var LocationIndex = _location.LocationIndex;
476477
}
477478
});
478479
*/
479-
/*
480480
addLinter('W009', function lintEmptySpacerCols($, reporter) {
481481
var selector = COL_CLASSES.map(function (colClass) {
482482
return colClass + ':not(:last-child)';
@@ -492,18 +492,9 @@ var LocationIndex = _location.LocationIndex;
492492
return;
493493
}
494494

495-
var colClasses = column.attr('class').split(/\s+/g).filter(function (klass) {
496-
return COL_REGEX.test(klass);
497-
});
498-
colClasses = sortedColumnClasses(colClasses.join(' ')).trim();
499-
500-
var colRegex = new RegExp('\\b(col-)(' + SCREENS.join('|') + ')(-\\d+)\\b', 'g');
501-
var offsetClasses = colClasses.replace(colRegex, '$1$2-offset$3');
502-
503-
reporter('Using empty spacer columns isn\'t necessary with Bootstrap\'s grid. So instead of having an empty grid column with `class="' + colClasses + '"` , just add `class="' + offsetClasses + '"` to the next grid column.', column);
495+
reporter('Using empty spacer columns isn\'t necessary with Bootstrap\'s grid.', column);
504496
});
505497
});
506-
*/
507498
/*
508499
addLinter('W010', function lintMediaPulls($, reporter) {
509500
var mediaPulls = $('.media>.pull-left, .media>.pull-right');
@@ -534,7 +525,7 @@ var LocationIndex = _location.LocationIndex;
534525
var OUTDATED_BOOTSTRAP = 'Bootstrap version might be outdated. Latest version is at least ' + CURRENT_BOOTSTRAP_VERSION + ' ; saw what appears to be usage of Bootstrap ';
535526
var theWindow = getBrowserWindowObject();
536527
var globaljQuery = theWindow && (theWindow.$ || theWindow.jQuery);
537-
/* istanbul ignore if */
528+
// istanbul ignore if
538529
if (globaljQuery) {
539530
var versions = jqueryPluginVersions(globaljQuery);
540531
if (versions.length) {
@@ -578,7 +569,7 @@ var LocationIndex = _location.LocationIndex;
578569
var theWindow = getBrowserWindowObject();
579570
580571
var globaljQuery = theWindow && (theWindow.$ || theWindow.jQuery);
581-
/* istanbul ignore if */
572+
// istanbul ignore if
582573
if (globaljQuery) {
583574
var versions = jqueryPluginVersions(globaljQuery);
584575
if (versions.length) {
@@ -695,18 +686,16 @@ var LocationIndex = _location.LocationIndex;
695686
}
696687
});
697688
*/
698-
/*
699689
addLinter('E005', function lintRowAndColOnSameElem($, reporter) {
700690
var selector = COL_CLASSES.map(function (col) {
701691
return '.row' + col;
702692
}).join(',');
703693

704694
var rowCols = $(selector);
705695
if (rowCols.length) {
706-
reporter('Found both `.row` and `.col-*-*` used on the same element', rowCols);
696+
reporter('Found both `.row` and `.col*` used on the same element', rowCols);
707697
}
708698
});
709-
*/
710699
/*
711700
addLinter('E006', function lintInputGroupFormControlTypes($, reporter) {
712701
var selectInputGroups = $('.input-group select');
@@ -759,43 +748,38 @@ var LocationIndex = _location.LocationIndex;
759748
}
760749
});
761750
*/
762-
/*
763751
addLinter('E012', function lintGridClassMixedWithInputGroup($, reporter) {
764752
var selector = COL_CLASSES.map(function (colClass) {
765753
return '.input-group' + colClass;
766754
}).join(',');
767755

768756
var badMixes = $(selector);
769757
if (badMixes.length) {
770-
reporter('`.input-group` and `.col-*-*` cannot be used directly on the same element. Instead, nest the `.input-group` within the `.col-*-*`', badMixes);
758+
reporter('`.input-group` and `.col*` cannot be used directly on the same element. Instead, nest the `.input-group` within the `.col*`', badMixes);
771759
}
772760
});
773-
*/
774-
/*
775761
addLinter('E013', function lintRowChildrenAreCols($, reporter) {
776-
var ALLOWED_CHILDREN = COL_CLASSES.concat(['script', '.clearfix', '.bs-customizer-input']);
777-
var selector = '.row>*' + ALLOWED_CHILDREN.map(function (colClass) {
762+
var ALLOWED_CHILDREN = COL_CLASSES.concat(['script', '.clearfix']);
763+
var disallowedChildren = ALLOWED_CHILDREN.map(function (colClass) {
778764
return ':not(' + colClass + ')';
779765
}).join('');
766+
var selector = '.row>*' + disallowedChildren + ',.form-row>*' + disallowedChildren;
780767

781768
var nonColRowChildren = $(selector);
782769
if (nonColRowChildren.length) {
783-
reporter('Only columns (`.col-*-*`) may be children of `.row`s', nonColRowChildren);
770+
reporter('Only columns (`.col*`) or `.clearfix` may be children of `.row`s or `.form-row`s', nonColRowChildren);
784771
}
785772
});
786-
*/
787-
/*
788773
addLinter('E014', function lintColParentsAreRowsOrFormGroups($, reporter) {
789774
var selector = COL_CLASSES.map(function (colClass) {
790-
return '*:not(.row):not(.form-group)>' + colClass + ':not(col):not(th):not(td)';
775+
return '*:not(.row):not(.form-row)>' + colClass + ':not(col):not(th):not(td)';
791776
}).join(',');
792777

793778
var colsOutsideRowsAndFormGroups = $(selector);
794779
if (colsOutsideRowsAndFormGroups.length) {
795-
reporter('Columns (`.col-*-*`) can only be children of `.row`s or `.form-group`s', colsOutsideRowsAndFormGroups);
780+
reporter('Columns (`.col*`) can only be children of `.row`s or `.form-row`s', colsOutsideRowsAndFormGroups);
796781
}
797782
});
798-
*/
799783
/*
800784
addLinter('E015', function lintInputGroupsWithMultipleAddOnsPerSide($, reporter) {
801785
var addOnClasses = ['.input-group-addon', '.input-group-btn'];
@@ -944,7 +928,6 @@ var LocationIndex = _location.LocationIndex;
944928
}
945929
});
946930
*/
947-
/*
948931
addLinter('E029', function lintRedundantColumnClasses($, reporter) {
949932
var columns = $(COL_CLASSES.join(','));
950933
columns.each(function (_index, col) {
@@ -953,27 +936,26 @@ var LocationIndex = _location.LocationIndex;
953936
var simplifiedClasses = classes;
954937
var width2screens = width2screensFor(classes);
955938
var isRedundant = false;
956-
for (var width = 1; width <= NUM_COLS; width++) {
957-
var screens = width2screens[width];
958-
if (!screens) {
959-
continue;
960-
}
961-
var runs = incrementingRunsFrom(screens);
962-
if (!runs.length) {
963-
continue;
964-
}
939+
for (var width in width2screens) {
940+
if (Object.prototype.hasOwnProperty.call(width2screens, width)) {
941+
var screens = width2screens[width];
942+
var runs = incrementingRunsFrom(screens);
943+
if (!runs.length) {
944+
continue;
945+
}
965946

966-
isRedundant = true;
947+
isRedundant = true;
967948

968-
for (var i = 0; i < runs.length; i++) {
969-
var run = runs[i];
970-
var min = run[0];
971-
var max = run[1];
949+
for (var i = 0; i < runs.length; i++) {
950+
var run = runs[i];
951+
var min = run[0];
952+
var max = run[1];
972953

973-
// remove redundant classes
974-
for (var screenNum = min + 1; screenNum <= max; screenNum++) {
975-
var colClass = 'col-' + NUM2SCREEN[screenNum] + '-' + width;
976-
simplifiedClasses = withoutClass(simplifiedClasses, colClass);
954+
// remove redundant classes
955+
for (var screenNum = min + 1; screenNum <= max; screenNum++) {
956+
var colClass = 'col' + (NUM2SCREEN[screenNum] && '-' + NUM2SCREEN[screenNum]) + (width && '-' + width);
957+
simplifiedClasses = withoutClass(simplifiedClasses, colClass);
958+
}
977959
}
978960
}
979961
}
@@ -992,7 +974,6 @@ var LocationIndex = _location.LocationIndex;
992974
);
993975
});
994976
});
995-
*/
996977
/*
997978
addLinter('E030', function lintSoloGlyphiconClasses($, reporter) {
998979
var missingGlyphiconClass = $('[class*="glyphicon-"]:not(.glyphicon):not(.glyphicon-class)').filter(function () {
@@ -1076,17 +1057,15 @@ var LocationIndex = _location.LocationIndex;
10761057
}
10771058
});
10781059
*/
1079-
/*
10801060
addLinter('E037', function lintColZeros($, reporter) {
10811061
var selector = SCREENS.map(function (screen) {
1082-
return '.col-' + screen + '-0';
1062+
return '.col' + (screen && '-' + screen) + '-0';
10831063
}).join(',');
10841064
var elements = $(selector);
10851065
if (elements.length) {
1086-
reporter('Column widths must be positive integers (and <= 12 by default). Found usage(s) of invalid nonexistent `.col-*-0` classes.', elements);
1066+
reporter('Column widths must be positive integers (and <= 12 by default). Found usage(s) of invalid nonexistent `.col*-0` classes.', elements);
10871067
}
10881068
});
1089-
*/
10901069
/*
10911070
addLinter('E038', function lintMediaPulls($, reporter) {
10921071
var mediaPullsOutsideMedia = $('.media-left, .media-right').filter(function () {
@@ -1227,14 +1206,13 @@ var LocationIndex = _location.LocationIndex;
12271206
}
12281207
});
12291208
*/
1230-
/*
12311209
addLinter('E051', function lintColumnsNoFloats($, reporter) {
12321210
var pullSelector = COL_CLASSES.map(function (col) {
1233-
return '.pull-left' + col + ',.pull-right' + col;
1211+
return '.float-left' + col + ',.float-right' + col;
12341212
}).join(',');
12351213
var pulledCols = $(pullSelector);
12361214
if (pulledCols.length) {
1237-
reporter('`.pull-right` and `.pull-left` must not be used on `.col-*-*` elements', pulledCols);
1215+
reporter('`.float-right` and `.float-left` must not be used on `.col*` elements', pulledCols);
12381216
}
12391217
var styledSelector = COL_CLASSES.map(function (col) {
12401218
return col + '[style]';
@@ -1244,15 +1222,13 @@ var LocationIndex = _location.LocationIndex;
12441222
return /float\s*:\s*[a-z]+/i.test($(el).attr('style'));
12451223
});
12461224
if (styledCols.length) {
1247-
reporter('Manually added `float` styles must not be added on `.col-*-*` elements', styledCols);
1225+
reporter('Manually added `float` styles must not be added on `.col*` elements', styledCols);
12481226
}
12491227
});
1250-
*/
1251-
/*
12521228
addLinter('E052', function lintRowsNoFloats($, reporter) {
1253-
var pulledRows = $('.row.pull-right, .row.pull-left');
1229+
var pulledRows = $('.row.float-right, .row.float-left');
12541230
if (pulledRows.length) {
1255-
reporter('`.pull-right` and `.pull-left` must not be used on `.row` elements', pulledRows);
1231+
reporter('`.float-right` and `.float-left` must not be used on `.row` elements', pulledRows);
12561232
}
12571233
var styledRows = $('.row[style]').filter(function (i, el) {
12581234
//test for `float:*` in the style attribute
@@ -1262,7 +1238,6 @@ var LocationIndex = _location.LocationIndex;
12621238
reporter('Manually added `float` styles must not be added on `.row` elements', styledRows);
12631239
}
12641240
});
1265-
*/
12661241
exports._lint = function ($, reporter, disabledIdList, html) {
12671242
var locationIndex = IN_NODE_JS ? new LocationIndex(html) : null;
12681243
var reporterWrapper = IN_NODE_JS ?

test/_old_fixtures/doctype/html4.html

-25
This file was deleted.

test/_old_fixtures/doctype/html5-legacy.html

-23
This file was deleted.

test/_old_fixtures/doctype/missing.html

-24
This file was deleted.

test/_old_fixtures/x-ua-compatible/present.html

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
<meta http-equiv="X-UA-Compatible" content="IE=edge">
66
<meta name="viewport" content="width=device-width, initial-scale=1">
77
<title>Test</title>
8+
<link rel="stylesheet" href="../../../node_modules/qunit/qunit/qunit.css">
9+
810
<!--[if lt IE 9]>
911
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
1012
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
1113
<![endif]-->
12-
<script src="../../lib/jquery.min.js"></script>
13-
14-
<link rel="stylesheet" href="../../lib/qunit.css">
15-
<script src="../../lib/qunit.js"></script>
14+
<script src="../../../node_modules/jquery/dist/jquery.min.js"></script>
15+
<script src="../../../node_modules/qunit/qunit/qunit.js"></script>
1616
<script src="../../../dist/browser/bootlint.js"></script>
1717
<script src="../generic-qunit.js"></script>
1818
</head>

0 commit comments

Comments
 (0)