Skip to content
This repository was archived by the owner on Feb 1, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
217 changes: 148 additions & 69 deletions dist/browser/bootlint.js
Original file line number Diff line number Diff line change
Expand Up @@ -10379,6 +10379,31 @@ if (typeof define === 'function' && define.amd)
);

},{}],4:[function(require,module,exports){
/**
* This file automatically generated from `pre-publish.js`.
* Do not manually edit.
*/

module.exports = {
"area": true,
"base": true,
"br": true,
"col": true,
"embed": true,
"hr": true,
"img": true,
"input": true,
"keygen": true,
"link": true,
"menuitem": true,
"meta": true,
"param": true,
"source": true,
"track": true,
"wbr": true
};

},{}],5:[function(require,module,exports){
/*!
* Bootlint - an HTML linter for Bootstrap projects
* https://github.com/twbs/bootlint
Expand All @@ -10391,6 +10416,7 @@ if (typeof define === 'function' && define.amd)
var cheerio = require('cheerio');
var parseUrl = require('url').parse;
var semver = require('semver');
var voidElements = require('void-elements');
var _location = require('./location');
var LocationIndex = _location.LocationIndex;

Expand All @@ -10416,6 +10442,30 @@ var LocationIndex = _location.LocationIndex;
var IN_NODE_JS = !!(cheerio.load);
var MIN_JQUERY_VERSION = '1.9.1';// as of Bootstrap v3.3.0
var CURRENT_BOOTSTRAP_VERSION = '3.3.2';
var BOOTSTRAP_VERSION_4 = '4.0.0';
var PLUGINS = [
'affix',
'alert',
'button',
'carousel',
'collapse',
'dropdown',
'modal',
'popover',
'scrollspy',
'tab',
'tooltip'
];
var BOOTSTRAP_FILES = [
'link[rel="stylesheet"][href$="/bootstrap.css"]',
'link[rel="stylesheet"][href="bootstrap.css"]',
'link[rel="stylesheet"][href$="/bootstrap.min.css"]',
'link[rel="stylesheet"][href="bootstrap.min.css"]',
'script[src$="/bootstrap.js"]',
'script[src="bootstrap.js"]',
'script[src$="/bootstrap.min.js"]',
'script[src="bootstrap.min.js"]'
].join(',');

function compareNums(a, b) {
return a - b;
Expand Down Expand Up @@ -10542,6 +10592,62 @@ var LocationIndex = _location.LocationIndex;
return runs;
}

/**
* This function returns the browser window object, or null if this is not running in a browser environment.
* @returns {(Window|null)}
*/
function getBrowserWindowObject() {
var theWindow = null;
try {
/*eslint-disable no-undef, block-scoped-var */
theWindow = window;// jshint ignore:line
/*eslint-enable no-undef, block-scoped-var */
}
catch (e) {
// deliberately do nothing
}

return theWindow;
}

function versionsIn(strings) {
return strings.map(function (str) {
var match = str.match(/^\d+\.\d+\.\d+$/);
return match ? match[0] : null;
}).filter(function (match) {
return match !== null;
});
}

function versionInLinkedElement($, element) {
var elem = $(element);
var urlAttr = (tagNameOf(element) === 'LINK') ? 'href' : 'src';
var pathSegments = parseUrl(elem.attr(urlAttr)).pathname.split('/');
var versions = versionsIn(pathSegments);
if (!versions.length) {
return null;
}
var version = versions[versions.length - 1];
return version;
}

function jqueryPluginVersions(jQuery) {
/* @covignore */
return PLUGINS.map(function (pluginName) {
var plugin = jQuery.fn[pluginName];
if (!plugin) {
return undefined;
}
var constructor = plugin.Constructor;
if (!constructor) {
return undefined;
}
return constructor.VERSION;
}).filter(function (version) {
return version !== undefined;
}).sort(semver.compare);
}

function bootstrapScriptsIn($) {
var longhands = $('script[src*="bootstrap.js"]').filter(function (i, script) {
var url = $(script).attr('src');
Expand Down Expand Up @@ -10796,16 +10902,11 @@ var LocationIndex = _location.LocationIndex;
if (!/^j[qQ]uery(\.min)?\.js$/.test(filename)) {
return;
}
var matches = pathSegments.map(function (segment) {
var match = segment.match(/^\d+\.\d+\.\d+$/);
return match ? match[0] : null;
}).filter(function (match) {
return match !== null;
});
if (!matches.length) {
var versions = versionsIn(pathSegments);
if (!versions.length) {
return;
}
var version = matches[matches.length - 1];
var version = versions[versions.length - 1];
if (!semver.gte(version, MIN_JQUERY_VERSION, true)) {
reporter(OLD_JQUERY, script);
}
Expand Down Expand Up @@ -11209,15 +11310,16 @@ var LocationIndex = _location.LocationIndex;
});
addLinter("W009", function lintEmptySpacerCols($, reporter) {
var selector = COL_CLASSES.map(function (colClass) {
return colClass + ':not(col):not(:last-child)';
return colClass + ':not(:last-child)';
}).join(',');
var columns = $(selector);
columns.each(function (_index, col) {
var column = $(col);
var isVoidElement = voidElements[col.tagName.toLowerCase()];
// can't just use :empty because :empty excludes nodes with all-whitespace text content
var hasText = !!column.text().trim().length;
var hasChildren = !!column.children(':first-child').length;
if (hasChildren || hasText) {
if (hasChildren || hasText || isVoidElement) {
return;
}

Expand Down Expand Up @@ -11255,44 +11357,11 @@ var LocationIndex = _location.LocationIndex;
});
addLinter("W013", function lintOutdatedBootstrap($, reporter) {
var OUTDATED_BOOTSTRAP = "Bootstrap version might be outdated. Latest version is at least " + CURRENT_BOOTSTRAP_VERSION + " ; saw what appears to be usage of Bootstrap ";
var PLUGINS = [
'affix',
'alert',
'button',
'carousel',
'collapse',
'dropdown',
'modal',
'popover',
'scrollspy',
'tab',
'tooltip'
];
var theWindow = null;
try {
/*eslint-disable no-undef, block-scoped-var */
theWindow = window;// jshint ignore:line
/*eslint-enable no-undef, block-scoped-var */
}
catch (e) {
// deliberately do nothing
}
var theWindow = getBrowserWindowObject();
var globaljQuery = theWindow && (theWindow.$ || theWindow.jQuery);
/* @covignore */
if (globaljQuery) {
var versions = PLUGINS.map(function (pluginName) {
var plugin = globaljQuery.fn[pluginName];
if (!plugin) {
return undefined;
}
var constructor = plugin.Constructor;
if (!constructor) {
return undefined;
}
return constructor.VERSION;
}).filter(function (version) {
return version !== undefined;
}).sort(semver.compare);
var versions = jqueryPluginVersions(globaljQuery);
if (versions.length) {
var minVersion = versions[0];
if (semver.lt(minVersion, CURRENT_BOOTSTRAP_VERSION, true)) {
Expand All @@ -11302,32 +11371,14 @@ var LocationIndex = _location.LocationIndex;
}
}
// check for Bootstrap <link>s and <script>s
var bootstraps = $([
'link[rel="stylesheet"][href$="/bootstrap.css"]',
'link[rel="stylesheet"][href="bootstrap.css"]',
'link[rel="stylesheet"][href$="/bootstrap.min.css"]',
'link[rel="stylesheet"][href="bootstrap.min.css"]',
'script[src$="/bootstrap.js"]',
'script[src="bootstrap.js"]',
'script[src$="/bootstrap.min.js"]',
'script[src="bootstrap.min.js"]'
].join(','));
var bootstraps = $(BOOTSTRAP_FILES);
bootstraps.each(function () {
var elem = $(this);
var urlAttr = (tagNameOf(this) === 'LINK') ? 'href' : 'src';
var pathSegments = parseUrl(elem.attr(urlAttr)).pathname.split('/');
var matches = pathSegments.map(function (segment) {
var match = segment.match(/^\d+\.\d+\.\d+$/);
return match ? match[0] : null;
}).filter(function (match) {
return match !== null;
});
if (!matches.length) {
var version = versionInLinkedElement($, this);
if (version === null) {
return;
}
var version = matches[matches.length - 1];
if (semver.lt(version, CURRENT_BOOTSTRAP_VERSION, true)) {
reporter(OUTDATED_BOOTSTRAP + version, elem);
reporter(OUTDATED_BOOTSTRAP + version, $(this));
}
});
});
Expand All @@ -11343,6 +11394,34 @@ var LocationIndex = _location.LocationIndex;
}
});
});
addLinter("W015", function lintNewBootstrap($, reporter) {
var FUTURE_VERSION_ERROR = "Detected what appears to be Bootstrap v4 or later. This version of Bootlint only supports Bootstrap v3.";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, was the choice to go with 'FUTURE…' because Bootlint will only support V3 long term or is planning for supporting both now just pre-mature optimization? Thanks 😃

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Major versions of Bootstrap are not (fully) backward-compatible with each other, hence Bootstrap v4 will require a new version of Bootlint, whose message here will in turn mention "Bootstrap v5 or later". I suppose we could template this message if we want to be über-future-proof.
See also #168.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense. Thank you for the explanation. 😃

var theWindow = getBrowserWindowObject();

var globaljQuery = theWindow && (theWindow.$ || theWindow.jQuery);
/* @covignore */
if (globaljQuery) {
var versions = jqueryPluginVersions(globaljQuery);
if (versions.length) {
var minVersion = versions[0];
if (semver.gte(minVersion, BOOTSTRAP_VERSION_4, true)) {
reporter(FUTURE_VERSION_ERROR);
return;
}
}
}
// check for Bootstrap <link>s and <script>s
var bootstraps = $(BOOTSTRAP_FILES);
bootstraps.each(function () {
var version = versionInLinkedElement($, this);
if (version === null) {
return;
}
if (semver.gte(version, BOOTSTRAP_VERSION_4, true)) {
reporter(FUTURE_VERSION_ERROR, $(this));
}
});
});

exports._lint = function ($, reporter, disabledIdList, html) {
var locationIndex = IN_NODE_JS ? new LocationIndex(html) : null;
Expand Down Expand Up @@ -11458,7 +11537,7 @@ var LocationIndex = _location.LocationIndex;
}
})(typeof exports === 'object' && exports || this);

},{"./location":1,"cheerio":2,"semver":3,"url":5}],5:[function(require,module,exports){
},{"./location":1,"cheerio":2,"semver":3,"url":6,"void-elements":4}],6:[function(require,module,exports){
/*eslint-env node, browser */
/* jshint browser: true */
/**
Expand Down Expand Up @@ -11498,4 +11577,4 @@ var LocationIndex = _location.LocationIndex;
exports.parse = parse;
})();

},{}]},{},[4]);
},{}]},{},[5]);
Loading