worker for stylelint; hints in linter config popup

This commit is contained in:
tophf 2017-11-28 20:03:50 +03:00
parent c2d68612ec
commit 493c1a65c0
9 changed files with 331 additions and 193 deletions

View File

@ -215,6 +215,10 @@
"message": "Use default", "message": "Use default",
"description": "'Set to default' button in a confirm dialog" "description": "'Set to default' button in a confirm dialog"
}, },
"confirmDiscardChanges": {
"message": "Discard the changes?",
"description": "Generic label or title displayed when trying to close something (not a style) with unsaved changes"
},
"confirmSave": { "confirmSave": {
"message": "Save", "message": "Save",
"description": "'Save' button in a confirm dialog" "description": "'Save' button in a confirm dialog"

View File

@ -1,3 +1,6 @@
.CodeMirror-hints {
z-index: 999;
}
.CodeMirror-hint:hover { .CodeMirror-hint:hover {
color: white; color: white;
background: #08f; background: #08f;

View File

@ -1718,6 +1718,7 @@ function fromMozillaFormat() {
popup.codebox.focus(); popup.codebox.focus();
popup.codebox.on('changes', cm => { popup.codebox.on('changes', cm => {
popup.classList.toggle('ready', !cm.isBlank()); popup.classList.toggle('ready', !cm.isBlank());
cm.markClean();
}); });
// overwrite default extraKeys as those are inapplicable in popup context // overwrite default extraKeys as those are inapplicable in popup context
popup.codebox.options.extraKeys = { popup.codebox.options.extraKeys = {
@ -1885,15 +1886,18 @@ function showKeyMapHelp() {
} }
} }
function showHelp(title, body) { function showHelp(title = '', body) {
const div = $('#help-popup'); const div = $('#help-popup');
div.classList.remove('big'); div.classList.remove('big');
$('.contents', div).textContent = ''; const contents = $('.contents', div);
$('.contents', div).appendChild(typeof body === 'string' ? tHTML(body) : body); contents.textContent = '';
if (body) {
contents.appendChild(typeof body === 'string' ? tHTML(body) : body);
}
$('.title', div).textContent = title; $('.title', div).textContent = title;
if (getComputedStyle(div).display === 'none') { if (getComputedStyle(div).display === 'none') {
document.addEventListener('keydown', closeHelp); window.addEventListener('keydown', closeHelp, true);
// avoid chaining on multiple showHelp() calls // avoid chaining on multiple showHelp() calls
$('.dismiss', div).onclick = closeHelp; $('.dismiss', div).onclick = closeHelp;
} }
@ -1902,16 +1906,19 @@ function showHelp(title, body) {
return div; return div;
function closeHelp(e) { function closeHelp(e) {
if ( if (!e || e.type === 'click' ||
!e || (e.which === 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey &&
e.type === 'click' || !$('.CodeMirror-hints, #message-box') && !(document.activeElement instanceof HTMLInputElement))) {
((e.keyCode || e.which) === 27 && !e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey) if (e && div.codebox && !div.codebox.options.readOnly && !div.codebox.isClean()) {
) { messageBox.confirm(t('confirmDiscardChanges')).then(ok => ok && closeHelp());
return;
}
div.style.display = ''; div.style.display = '';
const contents = $('.contents');
contents.textContent = ''; contents.textContent = '';
clearTimeout(contents.timer); clearTimeout(contents.timer);
document.removeEventListener('keydown', closeHelp); window.removeEventListener('keydown', closeHelp, true);
window.dispatchEvent(new Event('closeHelp'));
(editors.lastActive || editors[0]).focus();
} }
} }
} }

View File

@ -1,38 +1,26 @@
/* global CodeMirror CSSLint parserlib stylelint linterConfig */ /* global CodeMirror linterConfig */
'use strict'; 'use strict';
CodeMirror.registerHelper('lint', 'csslint', code => new Promise(resolve => { CodeMirror.registerHelper('lint', 'csslint', code =>
CSSLint.onmessage = ({data}) => { linterConfig.invokeWorker({code, config: linterConfig.getCurrent()}).then(results =>
resolve( results.map(({line, col: ch, message, rule, type: severity}) => line && {
data.map(({line, col, message, rule, type}) => line && { message,
message, from: {line: line - 1, ch: ch - 1},
from: {line: line - 1, ch: col - 1}, to: {line: line - 1, ch},
to: {line: line - 1, ch: col}, rule: rule.id,
rule: rule.id, severity,
severity: type }).filter(Boolean)));
}).filter(Boolean));
};
const config = deepCopy(linterConfig.getCurrent('csslint'));
CSSLint.postMessage({action: 'verify', code, config});
}));
CodeMirror.registerHelper('lint', 'stylelint', code => CodeMirror.registerHelper('lint', 'stylelint', code =>
stylelint.lint({ linterConfig.invokeWorker({code, config: linterConfig.getCurrent()}).then(({results}) =>
code, !results[0] && [] ||
config: deepCopy(linterConfig.getCurrent('stylelint')), results[0].warnings.map(({line, column:ch, text, severity}) => ({
}).then(({results}) => { from: {line: line - 1, ch: ch - 1},
if (!results[0]) { to: {line: line - 1, ch},
return []; message: text
}
return results[0].warnings.map(warning => ({
from: CodeMirror.Pos(warning.line - 1, warning.column - 1),
to: CodeMirror.Pos(warning.line - 1, warning.column),
message: warning.text
.replace('Unexpected ', '') .replace('Unexpected ', '')
.replace(/^./, firstLetter => firstLetter.toUpperCase()) .replace(/^./, firstLetter => firstLetter.toUpperCase())
.replace(/\s*\([^(]+\)$/, ''), // strip the rule, .replace(/\s*\([^(]+\)$/, ''), // strip the rule,
rule: warning.text.replace(/^.*?\s*\(([^(]+)\)$/, '$1'), rule: text.replace(/^.*?\s*\(([^(]+)\)$/, '$1'),
severity : warning.severity severity,
})); }))));
})
);

View File

@ -1,5 +1,5 @@
/* global CodeMirror messageBox */ /* global CodeMirror messageBox */
/* global editors makeSectionVisible showCodeMirrorPopup showHelp */ /* global editors makeSectionVisible showCodeMirrorPopup showHelp hotkeyRerouter */
/* global loadScript require CSSLint stylelint */ /* global loadScript require CSSLint stylelint */
/* global makeLink */ /* global makeLink */
'use strict'; 'use strict';
@ -20,8 +20,16 @@ var linterConfig = {
csslint: 'editorCSSLintConfig', csslint: 'editorCSSLintConfig',
stylelint: 'editorStylelintConfig', stylelint: 'editorStylelintConfig',
}, },
worker: {
csslint: {path: '/vendor-overwrites/csslint/csslint-worker.js'},
stylelint: {path: '/vendor-overwrites/stylelint/stylelint-bundle.min.js'},
},
allRuleIds: {
csslint: null,
stylelint: null,
},
getDefault() { getName() {
// some dirty hacks to override editor.linter getting from prefs // some dirty hacks to override editor.linter getting from prefs
const linter = prefs.get('editor.linter'); const linter = prefs.get('editor.linter');
if (linter && editors[0] && editors[0].getOption('mode') !== 'css') { if (linter && editors[0] && editors[0].getOption('mode') !== 'css') {
@ -30,11 +38,11 @@ var linterConfig = {
return linter; return linter;
}, },
getCurrent(linter = linterConfig.getDefault()) { getCurrent(linter = linterConfig.getName()) {
return this.fallbackToDefaults(this[linter] || {}); return this.fallbackToDefaults(this[linter] || {});
}, },
getForCodeMirror(linter = linterConfig.getDefault()) { getForCodeMirror(linter = linterConfig.getName()) {
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'),
@ -44,7 +52,7 @@ var linterConfig = {
} : false; } : false;
}, },
fallbackToDefaults(config, linter = linterConfig.getDefault()) { fallbackToDefaults(config, linter = linterConfig.getName()) {
if (config && Object.keys(config).length) { if (config && Object.keys(config).length) {
if (linter === 'stylelint') { if (linter === 'stylelint') {
// always use default syntax because we don't expose it in config UI // always use default syntax because we don't expose it in config UI
@ -56,33 +64,52 @@ var linterConfig = {
} }
}, },
setLinter(linter = linterConfig.getDefault()) { setLinter(linter = linterConfig.getName()) {
linter = linter.toLowerCase(); linter = linter.toLowerCase();
linter = linter === 'csslint' || linter === 'stylelint' ? linter : ''; linter = linter === 'csslint' || linter === 'stylelint' ? linter : '';
if (linterConfig.getDefault() !== linter) { if (linterConfig.getName() !== linter) {
prefs.set('editor.linter', linter); prefs.set('editor.linter', linter);
} }
return linter; return linter;
}, },
findInvalidRules(config, linter = linterConfig.getDefault()) { invokeWorker(message) {
const rules = linter === 'stylelint' ? config.rules : config; const worker = linterConfig.worker[message.linter || linterConfig.getName()];
if (!worker.queue) {
worker.queue = [];
worker.instance.onmessage = ({data}) => {
worker.queue.shift().resolve(data);
if (worker.queue.length) {
worker.instance.postMessage(worker.queue[0].message);
}
};
}
return new Promise(resolve => { return new Promise(resolve => {
if (linter === 'stylelint') { worker.queue.push({message, resolve});
resolve(Object.keys(stylelint.rules)); if (worker.queue.length === 1) {
} else { worker.instance.postMessage(message);
CSSLint.onmessage = ({data}) =>
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));
getAllRuleIds(linter = linterConfig.getName()) {
return Promise.resolve(
this.allRuleIds[linter] ||
this.invokeWorker({linter, action: 'getAllRuleIds'})
.then(ids => (this.allRuleIds[linter] = ids.sort()))
);
},
findInvalidRules(config, linter = linterConfig.getName()) {
return this.getAllRuleIds(linter).then(allRuleIds => {
const allRuleIdsSet = new Set(allRuleIds);
const rules = linter === 'stylelint' ? config.rules : config;
return Object.keys(rules).filter(rule => !allRuleIdsSet.has(rule));
}); });
}, },
stringify(config = this.getCurrent()) { stringify(config = this.getCurrent()) {
if (linterConfig.getDefault() === 'stylelint') { if (linterConfig.getName() === 'stylelint') {
config.syntax = undefined; config.syntax = undefined;
} }
return JSON.stringify(config, null, 2) return JSON.stringify(config, null, 2)
@ -91,7 +118,7 @@ var linterConfig = {
save(config) { save(config) {
config = this.fallbackToDefaults(config); config = this.fallbackToDefaults(config);
const linter = linterConfig.getDefault(); const linter = linterConfig.getName();
this[linter] = config; this[linter] = config;
BG.chromeSync.setLZValue(this.storageName[linter], config); BG.chromeSync.setLZValue(this.storageName[linter], config);
return config; return config;
@ -155,7 +182,7 @@ function initLint() {
prefs.subscribe(['editor.linter'], updateLinter); prefs.subscribe(['editor.linter'], updateLinter);
} }
function updateLinter({immediately, linter = linterConfig.getDefault()} = {}) { function updateLinter({immediately, linter = linterConfig.getName()} = {}) {
if (!immediately) { if (!immediately) {
debounce(updateLinter, 0, {immediately: true, linter}); debounce(updateLinter, 0, {immediately: true, linter});
return; return;
@ -358,17 +385,16 @@ function gotoLintIssue(event) {
} }
function showLintHelp() { function showLintHelp() {
const linter = linterConfig.getDefault(); const linter = linterConfig.getName();
const baseUrl = linter === 'stylelint' const baseUrl = linter === 'stylelint'
? 'https://stylelint.io/user-guide/rules/' ? 'https://stylelint.io/user-guide/rules/'
// some CSSLint rules do not have a url // some CSSLint rules do not have a url
: 'https://github.com/CSSLint/csslint/issues/535'; : 'https://github.com/CSSLint/csslint/issues/535';
let headerLink, template; let headerLink, template;
if (linter === 'csslint') { if (linter === 'csslint') {
const CSSLintRules = CSSLint.getRules();
headerLink = makeLink('https://github.com/CSSLint/csslint/wiki/Rules-by-ID', 'CSSLint'); headerLink = makeLink('https://github.com/CSSLint/csslint/wiki/Rules-by-ID', 'CSSLint');
template = ruleID => { template = ruleID => {
const rule = CSSLintRules.find(rule => rule.id === ruleID); const rule = linterConfig.allRuleIds.csslint.find(rule => rule.id === ruleID);
return rule && return rule &&
$element({tag: 'li', appendChild: [ $element({tag: 'li', appendChild: [
$element({tag: 'b', appendChild: makeLink(rule.url || baseUrl, rule.name)}), $element({tag: 'b', appendChild: makeLink(rule.url || baseUrl, rule.name)}),
@ -398,150 +424,202 @@ function showLintHelp() {
); );
} }
function showLinterErrorMessage(title, contents) { function showLinterErrorMessage(title, contents, popup) {
messageBox({ messageBox({
title, title,
contents, contents,
className: 'danger center lint-config', className: 'danger center lint-config',
buttons: [t('confirmOK')], buttons: [t('confirmOK')],
}); }).then(() => popup && popup.codebox.focus());
}
function setupLinterSettingsEvents(popup) {
$('.save', popup).addEventListener('click', event => {
event.preventDefault();
const linter = linterConfig.setLinter(event.target.dataset.linter);
const json = tryJSONparse(popup.codebox.getValue());
if (json) {
showLinterErrorMessage(linter, t('linterJSONError'));
popup.codebox.focus();
return;
}
linterConfig.findInvalidRules(json, linter).then(invalid => {
if (invalid.length) {
showLinterErrorMessage(linter, [
t('linterInvalidConfigError'),
$element({
tag: 'ul',
appendChild: invalid.map(name =>
$element({tag: 'li', textContent: name})),
}),
]);
return;
}
linterConfig.save(json);
linterConfig.showSavedMessage();
popup.codebox.markClean();
popup.codebox.focus();
});
});
$('.reset', popup).addEventListener('click', event => {
event.preventDefault();
const linter = linterConfig.setLinter(event.target.dataset.linter);
popup.codebox.setValue(linterConfig.stringify(linterConfig.defaults[linter] || {}));
popup.codebox.focus();
});
$('.cancel', popup).addEventListener('click', event => {
event.preventDefault();
$('.dismiss').dispatchEvent(new Event('click'));
});
} }
function setupLinterPopup(config) { function setupLinterPopup(config) {
const linter = linterConfig.getDefault(); const linter = linterConfig.getName();
const linterTitle = linter === 'stylelint' ? 'Stylelint' : 'CSSLint'; const linterTitle = linter === 'stylelint' ? 'Stylelint' : 'CSSLint';
const defaultConfig = linterConfig.stringify(linterConfig.defaults[linter] || {});
function makeButton(className, text, options = {}) {
return $element(Object.assign(options, {
tag: 'button',
className,
type: 'button',
textContent: t(text),
dataset: {linter}
}));
}
function makeLink(url, textContent) {
return $element({tag: 'a', target: '_blank', href: url, textContent});
}
const title = t('linterConfigPopupTitle', linterTitle); const title = t('linterConfigPopupTitle', linterTitle);
const contents = $element({ const popup = showCodeMirrorPopup(title, null, {
appendChild: [ lint: false,
$element({ extraKeys: {'Ctrl-Enter': save},
tag: 'p', hintOptions: {hint},
appendChild: [
t('linterRulesLink') + ' ',
makeLink(
linter === 'stylelint'
? 'https://stylelint.io/user-guide/rules/'
: 'https://github.com/CSSLint/csslint/wiki/Rules-by-ID',
linterTitle
),
linter === 'csslint' ? ' ' + t('linterCSSLintSettings') : ''
]
}),
makeButton('save', 'styleSaveLabel', {disabled: true}),
makeButton('cancel', 'confirmCancel'),
makeButton('reset', 'genericResetLabel', {title: t('linterResetMessage')}),
$element({
tag: 'span',
className: 'saved-message',
textContent: t('genericSavedMessage')
})
]
}); });
const popup = showCodeMirrorPopup(title, contents, {lint: false}); $('.contents', popup).appendChild(makeFooter());
contents.parentNode.appendChild(contents);
popup.codebox.focus(); const cm = popup.codebox;
popup.codebox.setValue(config); cm.focus();
popup.codebox.clearHistory(); cm.setValue(config);
popup.codebox.markClean(); cm.clearHistory();
popup.codebox.on('change', cm => { cm.markClean();
$('.save', popup).disabled = cm.isClean(); cm.on('changes', updateButtonState);
updateButtonState();
hotkeyRerouter.setState(false);
window.addEventListener('closeHelp', function _() {
window.removeEventListener('closeHelp', _);
hotkeyRerouter.setState(true);
}); });
setupLinterSettingsEvents(popup);
loadScript([ loadScript([
'/vendor/codemirror/mode/javascript/javascript.js', '/vendor/codemirror/mode/javascript/javascript.js',
'/vendor/codemirror/addon/lint/json-lint.js', '/vendor/codemirror/addon/lint/json-lint.js',
'/vendor/jsonlint/jsonlint.js' '/vendor/jsonlint/jsonlint.js'
]).then(() => { ]).then(() => {
popup.codebox.setOption('mode', 'application/json'); cm.setOption('mode', 'application/json');
popup.codebox.setOption('lint', 'json'); cm.setOption('lint', 'json');
}); });
function makeFooter() {
const makeButton = (className, onclick, text, options = {}) =>
$element(Object.assign(options, {
className,
onclick,
tag: 'button',
type: 'button',
textContent: t(text),
}));
return $element({
appendChild: [
$element({
tag: 'p',
appendChild: [
t('linterRulesLink') + ' ',
$element({
tag: 'a',
target: '_blank',
href: linter === 'stylelint'
? 'https://stylelint.io/user-guide/rules/'
: 'https://github.com/CSSLint/csslint/wiki/Rules-by-ID',
textContent: linterTitle
}),
linter === 'csslint' ? ' ' + t('linterCSSLintSettings') : ''
]
}),
makeButton('save', save, 'styleSaveLabel', {title: 'Ctrl-Enter'}),
makeButton('cancel', cancel, 'confirmClose'),
makeButton('reset', reset, 'genericResetLabel', {title: t('linterResetMessage')}),
$element({
tag: 'span',
className: 'saved-message',
textContent: t('genericSavedMessage')
})
]
});
}
function save(event) {
if (event instanceof Event) {
event.preventDefault();
}
const json = tryJSONparse(cm.getValue());
if (!json) {
showLinterErrorMessage(linter, t('linterJSONError'), popup);
cm.focus();
}
linterConfig.findInvalidRules(json, linter).then(invalid => {
if (invalid.length) {
showLinterErrorMessage(linter, [
t('linterInvalidConfigError'),
$element({tag: 'ul', appendChild: invalid.map(name =>
$element({tag: 'li', textContent: name})),
}),
], popup);
return;
}
linterConfig.setLinter(linter);
linterConfig.save(json);
linterConfig.showSavedMessage();
cm.markClean();
cm.focus();
updateButtonState();
});
}
function reset(event) {
event.preventDefault();
if (linterConfig.getName() !== linter) {
linterConfig.setLinter(linter);
}
cm.setValue(defaultConfig);
cm.focus();
updateButtonState();
}
function cancel(event) {
event.preventDefault();
$('.dismiss').dispatchEvent(new Event('click'));
}
function updateButtonState() {
$('.save', popup).disabled = cm.isClean();
$('.reset', popup).disabled = cm.getValue() === defaultConfig;
$('.cancel', popup).textContent = t(cm.isClean() ? 'confirmClose' : 'confirmCancel');
}
function hint(cm) {
return Promise.all([
linterConfig.getAllRuleIds(linter),
linter !== 'stylelint' || hint.allOptions ||
linterConfig.invokeWorker({action: 'getAllRuleOptions', linter})
.then(options => (hint.allOptions = options)),
])
.then(([ruleIds, options]) => {
const cursor = cm.getCursor();
const {start, end, string, type, state: {lexical}} = cm.getTokenAt(cursor);
const {line, ch} = cursor;
const quoted = string.startsWith('"');
const leftPart = string.slice(quoted ? 1 : 0, ch - start).trim();
const depth = getLexicalDepth(lexical);
const search = cm.getSearchCursor(/"([-\w]+)"/, {line, ch: start - 1});
let [, prevWord] = search.find(true) || [];
let words = [];
if (depth === 1 && linter === 'stylelint') {
words = quoted ? ['rules'] : [];
} else if ((depth === 1 || depth === 2) && type && type.includes('property')) {
words = ruleIds;
} else if (depth === 2 || depth === 3 && lexical.type === ']') {
words = !quoted ? ['true', 'false', 'null'] :
ruleIds.includes(prevWord) && (options[prevWord] || [])[0] || [];
} else if (depth === 4 && prevWord === 'severity') {
words = ['error', 'warning'];
} else if (depth === 4) {
words = ['ignore', 'ignoreAtRules', 'except', 'severity'];
} else if (depth === 5 && lexical.type === ']' && quoted) {
while (prevWord && !ruleIds.includes(prevWord)) {
prevWord = (search.find(true) || [])[1];
}
words = (options[prevWord] || []).slice(-1)[0] || ruleIds;
}
return {
list: words.filter(word => word.startsWith(leftPart)),
from: {line, ch: start + (quoted ? 1 : 0)},
to: {line, ch: string.endsWith('"') ? end - 1 : end},
};
});
}
function getLexicalDepth(lexicalState) {
let depth = 0;
while ((lexicalState = lexicalState.prev)) {
depth++;
}
return depth;
}
} }
function loadLinterAssets(name = linterConfig.getDefault()) { function loadLinterAssets(name = linterConfig.getName()) {
if (!name) { const worker = linterConfig.worker[name];
return Promise.resolve(); return !name || !worker || worker.instance ? Promise.resolve() :
} loadScript((worker.instance ? [] : [
return loadLibrary().then(loadAddon); (worker.instance = new Worker(worker.path)),
`/edit/lint-defaults-${name}.js`,
function loadLibrary() { ]).concat(CodeMirror.lint ? [] : [
if (name === 'csslint' && !window.CSSLint) {
window.CSSLint = new Worker('/vendor-overwrites/csslint/csslint-worker.js');
return loadScript([
'/edit/lint-defaults-csslint.js'
]);
}
if (name === 'stylelint' && !window.stylelint) {
return loadScript([
'/vendor-overwrites/stylelint/stylelint-bundle.min.js',
'/edit/lint-defaults-stylelint.js'
]).then(() => (window.stylelint = require('stylelint')));
}
return Promise.resolve();
}
function loadAddon() {
if (CodeMirror.lint) {
return;
}
return loadScript([
'/vendor/codemirror/addon/lint/lint.css', '/vendor/codemirror/addon/lint/lint.css',
'/msgbox/msgbox.css', '/msgbox/msgbox.css',
'/vendor/codemirror/addon/lint/lint.js', '/vendor/codemirror/addon/lint/lint.js',
'/edit/lint-codemirror-helper.js', '/edit/lint-codemirror-helper.js',
'/msgbox/msgbox.js' '/msgbox/msgbox.js'
]); ]));
}
} }

View File

@ -70,7 +70,7 @@ function createSourceEditor(style) {
update(); update();
function update() { function update() {
linterEl.value = linterConfig.getDefault(); linterEl.value = linterConfig.getName();
const cssLintOption = linterEl.querySelector('[value="csslint"]'); const cssLintOption = linterEl.querySelector('[value="csslint"]');
if (cm.getOption('mode') !== 'css') { if (cm.getOption('mode') !== 'css') {

View File

@ -15,6 +15,7 @@ function messageBox({
if (onshow) { if (onshow) {
onshow(messageBox.element); onshow(messageBox.element);
} }
messageBox.element.focus();
return new Promise(_resolve => { return new Promise(_resolve => {
messageBox.resolve = _resolve; messageBox.resolve = _resolve;
}); });

View File

@ -10950,14 +10950,15 @@ if (!CSSLint.suppressUsoVarError) {
}); });
} }
self.onmessage = ({data: {action, code, config}}) => { self.onmessage = ({data: {action = 'run', code, config}}) => {
switch (action) { switch (action) {
case 'getRules': case 'getAllRuleIds':
self.postMessage(CSSLint.getRules()); // the functions are non-tranferable and we need only an id
self.postMessage(CSSLint.getRules().map(rule => rule.id));
return; return;
case 'verify': case 'run':
Object.defineProperty(config, 'errors', {get: () => 0, set: () => 0}); Object.defineProperty(config, 'errors', {get: () => 0, set: () => 0});
config['uso-vars'] = 1; config['uso-vars'] = 1;
self.postMessage(CSSLint.verify(code, config).messages.map(m => { self.postMessage(CSSLint.verify(code, config).messages.map(m => {

View File

@ -1672,4 +1672,60 @@ N,R,y-N,"inline"])):(K.lastIndex=G+1,K.test(E),y=0===K.lastIndex?E.length-1:K.la
{}],609:[function(a,l,g){l.exports=function(a,g,f){if(0===a.length)return a;if(g){f||a.sort(g);f=1;for(var d=a.length,c=a[0],b,h=1;h<d;++h)b=c,c=a[h],g(c,b)&&(h===f?f++:a[f++]=c);a.length=f;return a}f||a.sort();g=1;f=a.length;d=a[0];for(b=1;b<f;++b)c=d,d=a[b],d!==c&&(b===g?g++:a[g++]=d);a.length=g;return a}},{}],610:[function(a,l,g){function k(a,f){function d(){for(var c=Array(arguments.length),b=0;b<c.length;b++)c[b]=arguments[b];var d=a.apply(this,c),f=c[c.length-1];"function"===typeof d&&d!==f&& {}],609:[function(a,l,g){l.exports=function(a,g,f){if(0===a.length)return a;if(g){f||a.sort(g);f=1;for(var d=a.length,c=a[0],b,h=1;h<d;++h)b=c,c=a[h],g(c,b)&&(h===f?f++:a[f++]=c);a.length=f;return a}f||a.sort();g=1;f=a.length;d=a[0];for(b=1;b<f;++b)c=d,d=a[b],d!==c&&(b===g?g++:a[g++]=d);a.length=g;return a}},{}],610:[function(a,l,g){function k(a,f){function d(){for(var c=Array(arguments.length),b=0;b<c.length;b++)c[b]=arguments[b];var d=a.apply(this,c),f=c[c.length-1];"function"===typeof d&&d!==f&&
Object.keys(f).forEach(function(a){d[a]=f[a]});return d}if(a&&f)return k(a)(f);if("function"!==typeof a)throw new TypeError("need wrapper function");Object.keys(a).forEach(function(c){d[c]=a[c]});return d}l.exports=k},{}],611:[function(a,l,g){var k=a("fs"),h=a("path"),f=a("mkdirp");l.exports=function(a,c,b){var d=h.dirname(a);k.exists(d,function(g){g?k.writeFile(a,c,b):f(d,function(d){if(d)return b(d);k.writeFile(a,c,b)})})};l.exports.sync=function(a,c){var b=h.dirname(a);k.existsSync(b)||f.sync(b); Object.keys(f).forEach(function(a){d[a]=f[a]});return d}if(a&&f)return k(a)(f);if("function"!==typeof a)throw new TypeError("need wrapper function");Object.keys(a).forEach(function(c){d[c]=a[c]});return d}l.exports=k},{}],611:[function(a,l,g){var k=a("fs"),h=a("path"),f=a("mkdirp");l.exports=function(a,c,b){var d=h.dirname(a);k.exists(d,function(g){g?k.writeFile(a,c,b):f(d,function(d){if(d)return b(d);k.writeFile(a,c,b)})})};l.exports.sync=function(a,c){var b=h.dirname(a);k.existsSync(b)||f.sync(b);
k.writeFileSync(a,c)};l.exports.stream=function(a){var c=h.dirname(a);k.existsSync(c)||f.sync(c);return k.createWriteStream(a)}},{fs:1,mkdirp:173,path:14}],stylelint:[function(a,l,g){g=a("./utils/checkAgainstRule");var k=a("./createPlugin"),h=a("./createStylelint"),f=a("./formatters"),d=a("./postcssPlugin"),c=a("./utils/report"),b=a("./utils/ruleMessages"),p=a("./rules"),m=a("./standalone");a=a("./utils/validateOptions");d.utils={report:c,ruleMessages:b,validateOptions:a,checkAgainstRule:g};d.lint= k.writeFileSync(a,c)};l.exports.stream=function(a){var c=h.dirname(a);k.existsSync(c)||f.sync(c);return k.createWriteStream(a)}},{fs:1,mkdirp:173,path:14}],stylelint:[function(a,l,g){g=a("./utils/checkAgainstRule");var k=a("./createPlugin"),h=a("./createStylelint"),f=a("./formatters"),d=a("./postcssPlugin"),c=a("./utils/report"),b=a("./utils/ruleMessages"),p=a("./rules"),m=a("./standalone");a=a("./utils/validateOptions");d.utils={report:c,ruleMessages:b,validateOptions:a,checkAgainstRule:g};d.lint=
m;d.rules=p;d.formatters=f;d.createPlugin=k;d.createLinter=h;l.exports=d},{"./createPlugin":334,"./createStylelint":335,"./formatters":338,"./postcssPlugin":346,"./rules":429,"./standalone":520,"./utils/checkAgainstRule":529,"./utils/report":592,"./utils/ruleMessages":593,"./utils/validateOptions":595}]},{},[]); m;d.rules=p;d.formatters=f;d.createPlugin=k;d.createLinter=h;l.exports=d},{"./createPlugin":334,"./createStylelint":335,"./formatters":338,"./postcssPlugin":346,"./rules":429,"./standalone":520,"./utils/checkAgainstRule":529,"./utils/report":592,"./utils/ruleMessages":593,"./utils/validateOptions":595}]},{},[]);
(() => {
const stylelint = require('stylelint');
self.onmessage = ({data: {action = 'run', code, config}}) => {
switch (action) {
case 'getAllRuleIds':
// the functions are non-tranferable
self.postMessage(Object.keys(stylelint.rules));
return;
case 'getAllRuleOptions':
self.postMessage(getAllRuleOptions());
return;
case 'run':
stylelint.lint({code, config}).then(results =>
self.postMessage(results));
return;
}
};
function getAllRuleOptions() {
const options = {};
const rxPossible = /\bpossible:("(?:[^"]*?)"|\[(?:[^\]]*?)\]|\{(?:[^}]*?)\})/g;
const rxString = /"([-\w\s]{3,}?)"/g;
for (const id of Object.keys(stylelint.rules)) {
const ruleCode = String(stylelint.rules[id]);
const sets = [];
let m, mStr;
while ((m = rxPossible.exec(ruleCode))) {
const possible = m[1];
const set = [];
while ((mStr = rxString.exec(possible))) {
const s = mStr[1];
if (s.includes(' ')) {
set.push(...s.split(/\s+/));
} else {
set.push(s);
}
}
if (possible.includes('ignoreAtRules')) {
set.push('ignoreAtRules');
}
if (possible.includes('ignoreShorthands')) {
set.push('ignoreShorthands');
}
if (set.length) {
sets.push(set);
}
}
if (sets.length) {
options[id] = sets;
}
}
return options;
}
})();