Refactor: replace linter modules with linter-defaults and linter-engines

This commit is contained in:
eight 2018-09-02 15:49:33 +08:00
parent 2efc2d8dd2
commit e68abad922
4 changed files with 170 additions and 165 deletions

View File

@ -89,8 +89,8 @@
<script src="edit/codemirror-default.js"></script>
<script src="edit/linter.js"></script>
<script src="edit/linter-csslint.js"></script>
<script src="edit/linter-stylelint.js"></script>
<script src="edit/linter-defaults.js"></script>
<script src="edit/linter-engines.js"></script>
<script src="edit/linter-meta.js"></script>
<script src="edit/linter-help-dialog.js"></script>
<script src="edit/linter-report.js"></script>

View File

@ -1,89 +0,0 @@
/* global linter editorWorker memoize */
'use strict';
// eslint-disable-next-line no-var
var csslint = (() => {
const DEFAULT = {
// Default warnings
'display-property-grouping': 1,
'duplicate-properties': 1,
'empty-rules': 1,
'errors': 1,
'warnings': 1,
'known-properties': 1,
// Default disabled
'adjoining-classes': 0,
'box-model': 0,
'box-sizing': 0,
'bulletproof-font-face': 0,
'compatible-vendor-prefixes': 0,
'duplicate-background-images': 0,
'fallback-colors': 0,
'floats': 0,
'font-faces': 0,
'font-sizes': 0,
'gradients': 0,
'ids': 0,
'import': 0,
'import-ie-limit': 0,
'important': 0,
'order-alphabetical': 0,
'outline-none': 0,
'overqualified-elements': 0,
'qualified-headings': 0,
'regex-selectors': 0,
'rules-count': 0,
'selector-max': 0,
'selector-max-approaching': 0,
'selector-newline': 0,
'shorthand': 0,
'star-property-hack': 0,
'text-indent': 0,
'underscore-property-hack': 0,
'unique-headings': 0,
'universal-selector': 0,
'unqualified-attributes': 0,
'vendor-prefix': 0,
'zero-units': 0
};
let config;
const prepareConfig = memoize(() => {
chrome.storage.onChanged.addListener((changes, area) => {
if (area !== 'sync' || !changes.hasOwnProperty('editorCSSLintConfig')) {
return;
}
getNewValue().then(linter.refresh);
});
return getNewValue();
function getNewValue() {
return chromeSync.getLZValue('editorCSSLintConfig')
.then(newConfig => {
config = Object.assign({}, DEFAULT, newConfig);
});
}
});
linter.register((text, options, cm) => {
if (prefs.get('editor.linter') !== 'csslint' || cm.getOption('mode') !== 'css') {
return;
}
return prepareConfig()
.then(() => 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)
);
});
return {DEFAULT};
})();

View File

@ -1,39 +1,38 @@
/* global linter editorWorker memoize */
'use strict';
// eslint-disable-next-line no-var
var stylelint = (() => {
const DEFAULT_SEVERITY = {severity: 'warning'};
const DEFAULT = {
var LINTER_DEFAULTS = (() => {
const SEVERITY = {severity: 'warning'};
const STYLELINT = {
// 'sugarss' is a indent-based syntax like Sass or Stylus
// ref: https://github.com/postcss/postcss#syntaxes
// syntax: 'sugarss',
// ** recommended rules **
// ref: https://github.com/stylelint/stylelint-config-recommended/blob/master/index.js
rules: {
'at-rule-no-unknown': [true, DEFAULT_SEVERITY],
'block-no-empty': [true, DEFAULT_SEVERITY],
'color-no-invalid-hex': [true, DEFAULT_SEVERITY],
'at-rule-no-unknown': [true, SEVERITY],
'block-no-empty': [true, SEVERITY],
'color-no-invalid-hex': [true, SEVERITY],
'declaration-block-no-duplicate-properties': [true, {
'ignore': ['consecutive-duplicates-with-different-values'],
'severity': 'warning'
}],
'declaration-block-no-shorthand-property-overrides': [true, DEFAULT_SEVERITY],
'font-family-no-duplicate-names': [true, DEFAULT_SEVERITY],
'function-calc-no-unspaced-operator': [true, DEFAULT_SEVERITY],
'function-linear-gradient-no-nonstandard-direction': [true, DEFAULT_SEVERITY],
'keyframe-declaration-no-important': [true, DEFAULT_SEVERITY],
'media-feature-name-no-unknown': [true, DEFAULT_SEVERITY],
'declaration-block-no-shorthand-property-overrides': [true, SEVERITY],
'font-family-no-duplicate-names': [true, SEVERITY],
'function-calc-no-unspaced-operator': [true, SEVERITY],
'function-linear-gradient-no-nonstandard-direction': [true, SEVERITY],
'keyframe-declaration-no-important': [true, SEVERITY],
'media-feature-name-no-unknown': [true, SEVERITY],
/* recommended true */
'no-empty-source': false,
'no-extra-semicolons': [true, DEFAULT_SEVERITY],
'no-invalid-double-slash-comments': [true, DEFAULT_SEVERITY],
'property-no-unknown': [true, DEFAULT_SEVERITY],
'selector-pseudo-class-no-unknown': [true, DEFAULT_SEVERITY],
'selector-pseudo-element-no-unknown': [true, DEFAULT_SEVERITY],
'no-extra-semicolons': [true, SEVERITY],
'no-invalid-double-slash-comments': [true, SEVERITY],
'property-no-unknown': [true, SEVERITY],
'selector-pseudo-class-no-unknown': [true, SEVERITY],
'selector-pseudo-element-no-unknown': [true, SEVERITY],
'selector-type-no-unknown': false, // for scss/less/stylus-lang
'string-no-newline': [true, DEFAULT_SEVERITY],
'unit-no-unknown': [true, DEFAULT_SEVERITY],
'string-no-newline': [true, SEVERITY],
'unit-no-unknown': [true, SEVERITY],
// ** non-essential rules
'comment-no-empty': false,
@ -172,59 +171,49 @@ var stylelint = (() => {
*/
}
};
let config;
const CSSLINT = {
// Default warnings
'display-property-grouping': 1,
'duplicate-properties': 1,
'empty-rules': 1,
'errors': 1,
'warnings': 1,
'known-properties': 1,
const prepareConfig = memoize(() => {
chrome.storage.onChanged.addListener((changes, area) => {
if (area !== 'sync' || !changes.hasOwnProperty('editorStylelintConfig')) {
return;
}
getNewValue().then(linter.run);
});
return getNewValue();
function getNewValue() {
return chromeSync.getLZValue('editorStylelintConfig')
.then(newConfig => {
const output = {};
output.rules = Object.assign({}, DEFAULT.rules, newConfig && newConfig.rules);
output.syntax = 'sugarss';
config = output;
});
}
});
linter.register((text, options, cm) => {
if (
!prefs.get('editor.linter') ||
cm.getOption('mode') === 'css' && prefs.get('editor.linter') !== 'stylelint'
) {
return;
}
return prepareConfig()
.then(() => editorWorker.stylelint(text, config))
.then(({results}) => {
if (!results[0]) {
return [];
}
const output = results[0].warnings.map(({line, column: ch, text, severity}) =>
({
from: {line: line - 1, ch: ch - 1},
to: {line: line - 1, ch},
message: text
.replace('Unexpected ', '')
.replace(/^./, firstLetter => firstLetter.toUpperCase())
.replace(/\s*\([^(]+\)$/, ''), // strip the rule,
rule: text.replace(/^.*?\s*\(([^(]+)\)$/, '$1'),
severity,
})
);
return cm.doc.mode.name !== 'stylus' ?
output :
output.filter(({message}) =>
!message.includes('"@css"') || !message.includes('(at-rule-no-unknown)'));
});
});
return {DEFAULT};
// Default disabled
'adjoining-classes': 0,
'box-model': 0,
'box-sizing': 0,
'bulletproof-font-face': 0,
'compatible-vendor-prefixes': 0,
'duplicate-background-images': 0,
'fallback-colors': 0,
'floats': 0,
'font-faces': 0,
'font-sizes': 0,
'gradients': 0,
'ids': 0,
'import': 0,
'import-ie-limit': 0,
'important': 0,
'order-alphabetical': 0,
'outline-none': 0,
'overqualified-elements': 0,
'qualified-headings': 0,
'regex-selectors': 0,
'rules-count': 0,
'selector-max': 0,
'selector-max-approaching': 0,
'selector-newline': 0,
'shorthand': 0,
'star-property-hack': 0,
'text-indent': 0,
'underscore-property-hack': 0,
'unique-headings': 0,
'universal-selector': 0,
'unqualified-attributes': 0,
'vendor-prefix': 0,
'zero-units': 0
};
return {STYLELINT, CSSLINT, SEVERITY};
})();

105
edit/linter-engines.js Normal file
View File

@ -0,0 +1,105 @@
/* global LINTER_DEFAULTS linter editorWorker */
'use strict';
(() => {
registerLinters({
csslint: {
storageName: 'editorCSSLintConfig',
lint: csslint,
validMode: mode => mode === 'css',
getConfig: config => Object.assign({}, LINTER_DEFAULTS.CSSLINT, config)
},
stylelint: {
storageName: 'editorStylelintConfig',
lint: stylelint,
validMode: () => true,
getConfig: config => ({
syntax: 'sugarss',
rules: Object.assign({}, LINTER_DEFAULTS.STYLELINT.rules, config && config.rules)
})
}
});
function stylelint(text, config, mode) {
return editorWorker.stylelint(text, config)
.then(({results}) => {
if (!results[0]) {
return [];
}
const output = results[0].warnings.map(({line, column: ch, text, severity}) =>
({
from: {line: line - 1, ch: ch - 1},
to: {line: line - 1, ch},
message: text
.replace('Unexpected ', '')
.replace(/^./, firstLetter => firstLetter.toUpperCase())
.replace(/\s*\([^(]+\)$/, ''), // strip the rule,
rule: text.replace(/^.*?\s*\(([^(]+)\)$/, '$1'),
severity,
})
);
return mode !== 'stylus' ?
output :
output.filter(({message}) =>
!message.includes('"@css"') || !message.includes('(at-rule-no-unknown)'));
});
}
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' || !changes.hasOwnProperty('editorStylelintConfig')) {
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');
for (const [name, engine] of Object.entries(engines)) {
if (name === selectedLinter && engine.validMode(mode)) {
return getConfig(name).then(config => engine.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);
});
}
}
})();