2018-11-07 06:09:29 +00:00
|
|
|
/* global LINTER_DEFAULTS linter editorWorker prefs chromeSync */
|
2018-10-01 14:03:17 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
(() => {
|
|
|
|
registerLinters({
|
|
|
|
csslint: {
|
2020-11-08 10:31:07 +00:00
|
|
|
storageName: chromeSync.LZ_KEY.csslint,
|
2018-10-01 14:03:17 +00:00
|
|
|
lint: csslint,
|
|
|
|
validMode: mode => mode === 'css',
|
|
|
|
getConfig: config => Object.assign({}, LINTER_DEFAULTS.CSSLINT, config)
|
|
|
|
},
|
|
|
|
stylelint: {
|
2020-11-08 10:31:07 +00:00
|
|
|
storageName: chromeSync.LZ_KEY.stylelint,
|
2018-10-01 14:03:17 +00:00
|
|
|
lint: stylelint,
|
|
|
|
validMode: () => true,
|
|
|
|
getConfig: config => ({
|
|
|
|
syntax: 'sugarss',
|
|
|
|
rules: Object.assign({}, LINTER_DEFAULTS.STYLELINT.rules, config && config.rules)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2020-10-28 09:46:54 +00:00
|
|
|
async function stylelint(text, config, mode) {
|
|
|
|
const raw = await editorWorker.stylelint(text, config);
|
|
|
|
if (!raw) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
// Hiding the errors about "//" comments as we're preprocessing only when saving/applying
|
|
|
|
// and we can't just pre-remove the comments since "//" may be inside a string token or whatever
|
|
|
|
const slashCommentAllowed = mode === 'text/x-less' || mode === 'stylus';
|
|
|
|
const res = [];
|
|
|
|
for (const w of raw.warnings) {
|
|
|
|
const msg = w.text.match(/^(?:Unexpected\s+)?(.*?)\s*\([^()]+\)$|$/)[1] || w.text;
|
|
|
|
if (!slashCommentAllowed || !(
|
|
|
|
w.rule === 'no-invalid-double-slash-comments' ||
|
|
|
|
w.rule === 'property-no-unknown' && msg.includes('"//"')
|
|
|
|
)) {
|
|
|
|
res.push({
|
|
|
|
from: {line: w.line - 1, ch: w.column - 1},
|
|
|
|
to: {line: w.line - 1, ch: w.column},
|
|
|
|
message: msg.slice(0, 1).toUpperCase() + msg.slice(1),
|
|
|
|
severity: w.severity,
|
|
|
|
rule: w.rule,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
2018-10-01 14:03:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function csslint(text, config) {
|
|
|
|
return editorWorker.csslint(text, config)
|
|
|
|
.then(results =>
|
|
|
|
results
|
|
|
|
.map(({line, col: ch, message, rule, type: severity}) => line && {
|
|
|
|
message,
|
|
|
|
from: {line: line - 1, ch: ch - 1},
|
|
|
|
to: {line: line - 1, ch},
|
|
|
|
rule: rule.id,
|
|
|
|
severity,
|
|
|
|
})
|
|
|
|
.filter(Boolean)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function registerLinters(engines) {
|
|
|
|
const configs = new Map();
|
|
|
|
|
|
|
|
chrome.storage.onChanged.addListener((changes, area) => {
|
|
|
|
if (area !== 'sync') {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (const [name, engine] of Object.entries(engines)) {
|
|
|
|
if (changes.hasOwnProperty(engine.storageName)) {
|
|
|
|
chromeSync.getLZValue(engine.storageName)
|
|
|
|
.then(config => {
|
|
|
|
configs.set(name, engine.getConfig(config));
|
|
|
|
linter.run();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
linter.register((text, options, cm) => {
|
|
|
|
const selectedLinter = prefs.get('editor.linter');
|
|
|
|
if (!selectedLinter) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const mode = cm.getOption('mode');
|
|
|
|
if (engines[selectedLinter].validMode(mode)) {
|
|
|
|
return runLint(selectedLinter);
|
|
|
|
}
|
|
|
|
for (const [name, engine] of Object.entries(engines)) {
|
|
|
|
if (engine.validMode(mode)) {
|
|
|
|
return runLint(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function runLint(name) {
|
|
|
|
return getConfig(name)
|
|
|
|
.then(config => engines[name].lint(text, config, mode));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
function getConfig(name) {
|
|
|
|
if (configs.has(name)) {
|
|
|
|
return Promise.resolve(configs.get(name));
|
|
|
|
}
|
|
|
|
return chromeSync.getLZValue(engines[name].storageName)
|
|
|
|
.then(config => {
|
|
|
|
configs.set(name, engines[name].getConfig(config));
|
|
|
|
return configs.get(name);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})();
|