UNPKG

3.25 kBJavaScriptView Raw
1var Promise = require('promise');
2var lodash = require('lodash'),
3 Parser = require('./parser'),
4 knife = require('./knife'),
5 presets = require('./presets'),
6 RuleList = require('./rule_list'),
7 InlineConfig = require('./inline_config');
8
9/**
10 * A linter is configured with a set of rules that are fed the raw
11 * html and ast nodes.
12 * @constructor
13 */
14var Linter = function (rules) {
15 this.rules = RuleList.fromRuleMap(rules);
16 this.parser = new Parser();
17 this.inlineConfig = new InlineConfig();
18};
19module.exports = Linter;
20
21/**
22 * Adds a plugin to the linter.
23 * @param {Object} plugin - the plugin to add to the linter.
24 */
25Linter.prototype.use = function (plugin) {
26 if (plugin.rules) {
27 plugin.rules.forEach(function (rule) {
28 this.rules.addRule(rule);
29 }.bind(this));
30 }
31};
32
33/**
34 * Lints the HTML with the options supplied in the environments setup.
35 * @param {String} html - the html as a string to lint.
36 */
37Linter.prototype.lint = function (html) {
38 var opts = Linter.getOptions(arguments),
39 lines = knife.shred(html),
40 dom = this.parser.parse(html),
41 issues = null,
42 maxerr = (!opts.maxerr && opts.maxerr !== 0 ? Infinity : opts.maxerr);
43
44 this.setupSubscriptions();
45
46 this.setupInlineConfigs(dom);
47
48 issues = this.lintByLine(lines, opts);
49 issues = issues.concat(this.lintDom(dom, opts));
50
51 issues = issues.concat(this.resetRules(opts));
52 this.inlineConfig.clear();
53
54 if (maxerr >= 0) {
55 issues = lodash.first(issues, maxerr);
56 }
57
58 return Promise.all(issues)
59 .then(function (resolved) {
60 return lodash.flatten(resolved);
61 });
62};
63Linter.prototype.lint = Promise.nodeify(Linter.prototype.lint);
64
65Linter.getOptions = function (args) {
66 var optList = Array.prototype.slice.call(args, 1);
67 optList = lodash.flatten(optList);
68
69 optList.unshift('default');
70
71 return presets.flattenOpts(optList);
72};
73
74Linter.prototype.setupSubscriptions = function () {
75 this.rules.forEach(function (rule) {
76 rule.subscribers = this.rules.getSubscribers(rule.name);
77 }.bind(this));
78};
79
80Linter.prototype.lintByLine = function (lines, opts) {
81 return this.rules.getRule('line').lint(lines, opts, this.inlineConfig);
82};
83
84Linter.prototype.lintDom = function (dom, opts) {
85 return this.rules.getRule('dom').lint(dom, opts, this.inlineConfig);
86};
87
88Linter.prototype.resetRules = function (opts) {
89 var issues = [];
90
91 this.rules.forEach(function (rule) {
92 if (!rule.end) {
93 return;
94 }
95
96 var result = rule.end(opts);
97 if (result) {
98 issues.push(result);
99 }
100 });
101
102 return lodash.flatten(issues);
103};
104
105Linter.prototype.setupInlineConfigs = function (dom) {
106 var comments = [];
107 var feedComments = function (element) {
108 if (element.type === 'comment') {
109 comments.push(element);
110 this.inlineConfig.feedComment(element);
111 }
112 if (element.children && element.children.length > 0) {
113 element.children.forEach(function (child) {
114 feedComments(child);
115 });
116 }
117 }.bind(this);
118 if (dom.length) {
119 dom.forEach(feedComments);
120 }
121};