use CSSLint in a web werkker

This commit is contained in:
tophf 2017-11-27 17:02:23 +03:00
parent f3cf6e1856
commit 99512da9da
4 changed files with 75 additions and 87 deletions

View File

@ -40,6 +40,8 @@ body {
box-shadow: 0 0 3rem -1.2rem black; box-shadow: 0 0 3rem -1.2rem black;
box-sizing: border-box; box-sizing: border-box;
z-index: 10; z-index: 10;
display: flex;
flex-direction: column;
} }
#header h1 { #header h1 {
margin-top: 0; margin-top: 0;
@ -468,9 +470,9 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
} }
/************ lint ************/ /************ lint ************/
#lint > div { #lint {
overflow-y: auto; overflow-y: auto;
} overflow-x: hidden;}
#lint table { #lint table {
font-size: 100%; font-size: 100%;
border-spacing: 0; border-spacing: 0;
@ -505,6 +507,7 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
} }
#lint td[role="message"] { #lint td[role="message"] {
text-align: left; text-align: left;
white-space: nowrap;
} }
#message-box.center.lint-config #message-box-contents { #message-box.center.lint-config #message-box-contents {
text-align: left; text-align: left;

View File

@ -1,38 +1,20 @@
/* global CodeMirror CSSLint parserlib stylelint linterConfig */ /* global CodeMirror CSSLint parserlib stylelint linterConfig */
'use strict'; 'use strict';
CodeMirror.registerHelper('lint', 'csslint', code => { CodeMirror.registerHelper('lint', 'csslint', code => new Promise(resolve => {
if (!CSSLint.suppressUsoVarError) { CSSLint.onmessage = ({data}) => {
CSSLint.suppressUsoVarError = true; resolve(
parserlib.css.Tokens[parserlib.css.Tokens.COMMENT].hide = false; data.map(({line, col, message, rule, type}) => line && {
const isUsoVar = ({value}) => value.startsWith('/*[[') && value.endsWith(']]*/'); message,
CSSLint.addRule({ from: {line: line - 1, ch: col - 1},
id: 'uso-vars', to: {line: line - 1, ch: col},
init(parser, reporter) { rule: rule.id,
parser.addListener('error', function ({message, line, col}) { severity: type
if (!isUsoVar(this._tokenStream._token)) { }).filter(Boolean));
const {_lt, _ltIndex: i} = this._tokenStream; };
if (i < 2 || !_lt.slice(0, i - 1).reverse().some(isUsoVar)) { const config = deepCopy(linterConfig.getCurrent('csslint'));
reporter.error(message, line, col); CSSLint.postMessage({action: 'verify', code, config});
} }));
}
});
},
});
}
const rules = deepCopy(linterConfig.getCurrent('csslint'));
Object.defineProperty(rules, 'errors', {get: () => 0, set: () => 0});
rules['uso-vars'] = 1;
return CSSLint.verify(code, rules).messages
.map(({line, col, message, rule, type}) => line && {
message,
from: {line: line - 1, ch: col - 1},
to: {line: line - 1, ch: col},
rule: rule.id,
severity: type
})
.filter(Boolean);
});
CodeMirror.registerHelper('lint', 'stylelint', code => CodeMirror.registerHelper('lint', 'stylelint', code =>
stylelint.lint({ stylelint.lint({

View File

@ -38,6 +38,9 @@ var linterConfig = {
return CodeMirror.lint && CodeMirror.lint[linter] ? { return CodeMirror.lint && CodeMirror.lint[linter] ? {
getAnnotations: CodeMirror.lint[linter], getAnnotations: CodeMirror.lint[linter],
delay: prefs.get('editor.lintDelay'), delay: prefs.get('editor.lintDelay'),
onUpdateLinting(annotationsNotSorted, annotations, cm) {
updateLintReport(cm, 0);
},
} : false; } : false;
}, },
@ -64,12 +67,18 @@ var linterConfig = {
findInvalidRules(config, linter = linterConfig.getDefault()) { findInvalidRules(config, linter = linterConfig.getDefault()) {
const rules = linter === 'stylelint' ? config.rules : config; const rules = linter === 'stylelint' ? config.rules : config;
const allRules = new Set( return new Promise(resolve => {
linter === 'stylelint' if (linter === 'stylelint') {
? Object.keys(stylelint.rules) resolve(Object.keys(stylelint.rules));
: CSSLint.getRules().map(rule => rule.id) } else {
); CSSLint.onmessage = ({data}) =>
return Object.keys(rules).filter(rule => !allRules.has(rule)); resolve(data.map(rule => rule.id));
CSSLint.postMessage({action: 'getRules'});
}
}).then(allRules => {
allRules = new Set(allRules);
return Object.keys(rules).filter(rule => !allRules.has(rule));
});
}, },
stringify(config = this.getCurrent()) { stringify(config = this.getCurrent()) {
@ -140,7 +149,6 @@ function initLint() {
$('#lint-help').addEventListener('click', showLintHelp); $('#lint-help').addEventListener('click', showLintHelp);
$('#lint').addEventListener('click', gotoLintIssue); $('#lint').addEventListener('click', gotoLintIssue);
$('#linter-settings').addEventListener('click', linterConfig.openOnClick); $('#linter-settings').addEventListener('click', linterConfig.openOnClick);
window.addEventListener('resize', resizeLintReport);
updateLinter(); updateLinter();
linterConfig.watchStorage(); linterConfig.watchStorage();
@ -332,22 +340,6 @@ function renderLintReport(someBlockChanged) {
$('#issue-count').textContent = issueCount; $('#issue-count').textContent = issueCount;
container.replaceChild(newContent, content); container.replaceChild(newContent, content);
container.classList.toggle('hidden', !newContent.children.length); container.classList.toggle('hidden', !newContent.children.length);
resizeLintReport();
}
}
function resizeLintReport() {
// subtracted value to prevent scrollbar
const magicBuffer = 20;
const content = $('#lint table');
if (content) {
const bounds = content.getBoundingClientRect();
const newMaxHeight = bounds.bottom <= window.innerHeight ? '' :
// subtract out a bit of padding or the vertical scrollbar extends beyond the viewport
(window.innerHeight - bounds.top - magicBuffer) + 'px';
if (newMaxHeight !== content.style.maxHeight) {
content.parentNode.style.maxHeight = newMaxHeight;
}
} }
} }
@ -421,7 +413,11 @@ function setupLinterSettingsEvents(popup) {
const linter = linterConfig.setLinter(event.target.dataset.linter); const linter = linterConfig.setLinter(event.target.dataset.linter);
const json = tryJSONparse(popup.codebox.getValue()); const json = tryJSONparse(popup.codebox.getValue());
if (json) { if (json) {
const invalid = linterConfig.findInvalidRules(json, linter); showLinterErrorMessage(linter, t('linterJSONError'));
popup.codebox.focus();
return;
}
linterConfig.findInvalidRules(json, linter).then(invalid => {
if (invalid.length) { if (invalid.length) {
showLinterErrorMessage(linter, [ showLinterErrorMessage(linter, [
t('linterInvalidConfigError'), t('linterInvalidConfigError'),
@ -436,10 +432,8 @@ function setupLinterSettingsEvents(popup) {
linterConfig.save(json); linterConfig.save(json);
linterConfig.showSavedMessage(); linterConfig.showSavedMessage();
popup.codebox.markClean(); popup.codebox.markClean();
} else { popup.codebox.focus();
showLinterErrorMessage(linter, t('linterJSONError')); });
}
popup.codebox.focus();
}); });
$('.reset', popup).addEventListener('click', event => { $('.reset', popup).addEventListener('click', event => {
event.preventDefault(); event.preventDefault();
@ -524,8 +518,8 @@ function loadLinterAssets(name = linterConfig.getDefault()) {
function loadLibrary() { function loadLibrary() {
if (name === 'csslint' && !window.CSSLint) { if (name === 'csslint' && !window.CSSLint) {
window.CSSLint = new Worker('/vendor-overwrites/csslint/csslint-worker.js');
return loadScript([ return loadScript([
'/vendor-overwrites/csslint/csslint-worker.js',
'/edit/lint-defaults-csslint.js' '/edit/lint-defaults-csslint.js'
]); ]);
} }

View File

@ -10931,31 +10931,40 @@ CSSLint.addFormatter({
} }
}); });
/* if (!CSSLint.suppressUsoVarError) {
* Web worker for CSSLint CSSLint.suppressUsoVarError = true;
*/ parserlib.css.Tokens[parserlib.css.Tokens.COMMENT].hide = false;
const isUsoVar = ({value}) => value.startsWith('/*[[') && value.endsWith(']]*/');
CSSLint.addRule({
id: 'uso-vars',
init(parser, reporter) {
parser.addListener('error', function ({message, line, col}) {
if (!isUsoVar(this._tokenStream._token)) {
const {_lt, _ltIndex: i} = this._tokenStream;
if (i < 2 || !_lt.slice(0, i - 1).reverse().some(isUsoVar)) {
reporter.error(message, line, col);
}
}
});
},
});
}
/* global self, JSON */ self.onmessage = ({data: {action, code, config}}) => {
switch (action) {
// message indicates to start linting case 'getRules':
self.onmessage = function(event) { self.postMessage(CSSLint.getRules());
"use strict"; return;
var data = event.data,
message,
text,
ruleset,
results;
try { case 'verify':
message = JSON.parse(data); Object.defineProperty(config, 'errors', {get: () => 0, set: () => 0});
text = message.text; config['uso-vars'] = 1;
ruleset = message.ruleset; self.postMessage(CSSLint.verify(code, config).messages.map(m => {
} catch (ex) { // the functions are non-tranferable and we need only an id
text = data; m.rule = {id: m.rule.id};
} return m;
}));
results = CSSLint.verify(text, ruleset); return;
}
// Not all browsers support structured clone, so JSON stringify results
self.postMessage(JSON.stringify(results));
}; };