chameleon-linter
Version:
cml规范校验工具
168 lines (155 loc) • 5.36 kB
JavaScript
const config = require('../config');
function getCmlType(mediaParams = '') {
let types = [];
if (mediaParams) {
let typeStr = /\((.*)\)/.exec(mediaParams);
if (typeStr && typeStr[1]) {
types = typeStr[1].split(',')
.filter(type => !!type.trim())
.map(type => {
return type.trim().toLowerCase();
});
}
}
return types;
}
function detectFloatProp(rule, result) {
rule.walkDecls('float', (decl) => {
result.style.messages.push({
line: decl.source.start.line,
column: decl.source.start.column,
token: decl.prop,
msg: `Weex does not support style property: "${decl.prop}", therefor you should not use "float" under a media rule with weex parameter or use "float" in a template file with platform type set to weex`
});
});
}
function isMediaNode(node) {
return node && node.type === 'atrule' && node.name === 'media';
}
const RULER_MAP = {
'important': {
rule: function (decl, platform) {
return config.neexLintWeex() && (!platform || platform == 'weex') && decl.important === true;
},
msg: function (decl) {
return `The CSS attribute "${decl.prop}" does not support "!important"`
}
},
'percentages': {
rule: function (decl, platform) {
return config.neexLintWeex() && (!platform || platform == 'weex') && /%\s*$/g.test(decl.value);
},
msg: function (decl, platform) {
return `The CSS attribute "${decl.prop}" does not support percentages`;
}
},
'lineHeight': {
rule: function (decl, platform) {
return config.neexLintWeex() && (!platform || platform == 'weex') && decl.prop == 'line-height' && /^\d+$/.test(decl.value);
},
msg: function (decl) {
return `The CSS attribute "${decl.prop}" does not support the number type unit`;
}
},
'cpx': {
rule: function (decl, platform) {
return config.getRuleOption('cpx-support') && !platform && /\d+\s*(rpx|px)\s*$/g.test(decl.value);
},
msg: function (decl) {
return `The CSS attribute "${decl.prop}" must use \'cpx\' as it\'s unit`;
}
},
'display': {
rule: function (decl, platform) {
return config.neexLintWeex() && (!platform || platform == 'weex') && decl.prop == 'display' && (decl.value == 'none' || decl.value == 'inline-block');
},
msg: function (decl) {
return 'The CSS attribute "display" does not support "' + decl.value + '" as it\'s value';
}
}
// 'flex': {
// rule: function (decl, platform) {
// if (config.neexLintWeex() && (!platform || platform === 'weex') && decl.prop == 'display' && decl.value == 'flex') {
// let flag = true;
// decl.parent.nodes.forEach(node => {
// if (node.prop == 'flex-direction') {
// flag = false;
// }
// });
// return flag;
// }
// },
// msg: function (decl) {
// return 'When the CSS attribute "display" value is "flex", you need to add the attribute "flex-direction" at the same time';
// }
// }
};
const SELECTOR_MAP = {
'pseudo': {
rule: function (selector, platform) {
let flag = false;
if (config.neexLintWeex() && (!platform || platform == 'weex')) {
selector.replace(/\:([^\s]+)/g, (match, pseudo) => {
if (['active', 'focus', 'disabled', 'enabled'].indexOf(pseudo) == -1) {
flag = true;
}
});
}
return flag;
},
msg: function (selector) {
return 'The CSS selector "' + selector + '" only supports pseudo-classes of "active, focus, disabled, enabled"';
}
}
};
module.exports = (result) => {
if (result.style && result.style.ast) {
let platform = result.style.platform;
let root = result.style.ast;
root.walk((node) => {
(node.selectors || []).forEach(selector => {
Object.keys(SELECTOR_MAP).forEach(key => {
if (SELECTOR_MAP[key].rule(selector, platform)) {
result.style.messages.push({
line: node.source.start.line,
column: node.source.start.column,
token: node.value,
msg: SELECTOR_MAP[key].msg(selector)
});
}
});
});
});
root.walkDecls((decl) => {
Object.keys(RULER_MAP).forEach(key => {
let stylePolymorphic = false;
let media = decl.parent && decl.parent.parent;
if (media && media.name == 'media') {
if (new RegExp(/cml\-type\s*\(\s*[\S]+\s*\)/g).test(media.params)) {
stylePolymorphic = true;
}
}
if (!stylePolymorphic && RULER_MAP[key].rule(decl, platform)) {
result.style.messages.push({
line: decl.source.start.line,
column: decl.source.start.column,
token: decl.value,
msg: RULER_MAP[key].msg(decl)
});
}
});
});
// polymorphic components forbid float property.
if (config.neexLintWeex() && platform === 'weex') {
detectFloatProp(root, result);
}
// single file components.
if (config.neexLintWeex() && (platform === undefined || platform === 'cml')) {
root.walkRules((rule) => {
if (!isMediaNode(rule.parent) || ~getCmlType(rule.parent.params).indexOf('weex')) {
detectFloatProp(rule, result);
}
});
}
}
};