magix-combine
Version:
合并Magix View的html,js,css成一个js文件,并检测html,js,css中可能存在的问题
258 lines (253 loc) • 9.29 kB
JavaScript
/*
处理样式选择器
1. 记录不推荐的选择器
2. 添加前缀,保证项目唯一
3. 压缩选择器(开启压缩的情况下)
*/
let path = require('path');
let utils = require('./util');
let md5 = require('./util-md5');
let configs = require('./util-config');
let checker = require('./checker');
let cssParser = require('./css-parser');
let sep = path.sep;
let slashReg = /[\/\.]/g;
//let cssCommentReg = /\/\*[\s\S]+?\*\//g;
//[ref="@../default.css:inmain"] .open{
// color:red
//}
let cssRefReg = /\[\s*ref\s*=(['"])@([\w\.\-\/\\]+?)(\.css|\.less|\.scss|\.mx|\.mmx|\.style):([\w\-]+)\1\]/g;
let genCssNamesKey = (file, ignorePrefix) => {
/*if (configs.scopedCssMap[file]) {
file = 'scoped.style';
}*/
//获取模块的id
let cssId;
if (configs.debug) {
//mc-【abc∕def∕test‧less】open-dialog «»
cssId = utils.extractModuleId(file);
cssId = '_' + cssId.replace(slashReg, '_') + '_';
//cssId = file.replace(configs.moduleIdRemovedPath, '').slice(1);
//cssId = '«' + cssId.replace(/[\/\\]/g, '∕').replace(/\./g, '‧') + '»';
} else {
cssId = md5(file, 'md5CssFileResult');
}
//css前缀是配置项中的前缀加上模块的md5信息
if (!ignorePrefix) {
cssId = configs.projectName + cssId;
}
return cssId;
};
let genCssSelector = (selector, cssNameKey, reservedNames, key) => {
let mappedName = selector;
if (configs.debug) { //压缩,我们采用md5处理,同样的name要生成相同的key
if (cssNameKey) {
mappedName = cssNameKey + '-' + mappedName;
}
} else {
mappedName = configs.projectName + md5(selector + '\x00' + cssNameKey, key || 'md5CssSelectorResult', null, false, reservedNames);
if (configs.selectorDSEndReg.test(selector)) {
mappedName += '-';
}
}
return mappedName;
};
let refProcessor = (relateFile, file, ext, name, e) => {
if (file == 'global' && ext == '.style') {
return name;
} else if (file == 'scoped' && ext == '.style') {
if (e) {
let sname = e.globalCssNamesMap[name];
if (!sname) {
throw new Error('[MXC Error(css-selector)] can not found ' + name + ' at scoped.style');
}
let dFiles = e.globalCssNamesInFiles[name + '!r'];
dFiles.forEach(f => {
checker.CSS.markUsed(f, name, relateFile);
});
return '.@' + sname;
} else {
throw new Error('[MXC Error(css-selector)] unsupport use scoped.style in ' + relateFile);
}
} else {
/*
a.css
[ref='@./b.css:good'] .name{
}
file='path/to/b.css';
relateFile='path/to/a.css';
b good a
*/
file = path.resolve(path.dirname(relateFile) + sep + file + ext);
if (e && configs.globalCssMap[file]) {
return name;
}
if (e && configs.scopedCssMap[file]) {
let sname = e.globalCssNamesMap[name];
if (!sname) {
throw new Error('[MXC Error(css-selector)] can not found ' + name + ' at ' + file);
}
let dFiles = e.globalCssNamesInFiles[name + '!r'];
dFiles.forEach(f => {
checker.CSS.markUsed(f, name, relateFile);
});
return '.@' + sname;
}
checker.CSS.markUsed(file, name, relateFile);
let p = name.replace(configs.selectorKeepNameReg, '$1');
let t = name.replace(p, '');
let id = genCssSelector(p, genCssNamesKey(file), e && e.globalReservedMap) + t;
return '.@' + id;
}
};
/**
* 添加到全局样式
* @param {string} name 原始样式名
* @param {string} transformSelector 变化后的,即可能是压缩后的样式
* @param {number} guid 目前仅标记是否全局的标识
* @param {boolean} lazyGlobal 是否在文件中标记全局的
* @param {string} file 所在文件
* @param {object} namesToFiles 名称到文件映射对象
*/
let addGlobal = (name, transformSelector, guid, lazyGlobal, file, namesToFiles) => {
if (!namesToFiles[name]) { //不存在
namesToFiles[name] = Object.create(null);
namesToFiles[name + '!s'] = Object.create(null);
} else if (!lazyGlobal && namesToFiles[name + '!g'] != guid) { //是否全局
namesToFiles[name + '!s'] = Object.create(null);
}
namesToFiles[name + '!g'] = guid;
namesToFiles[name][file] = 1;
if (!lazyGlobal) {
namesToFiles[name + '!s'][transformSelector] = file;
}
if (lazyGlobal) { //在文件中才标识的
let list = namesToFiles[name + '!r'];
if (list && list.length >= 0) {
if (!list[file]) {
list[file] = 1;
list.push(file);
}
} else {
namesToFiles[name + '!r'] = [file];
}
//checker.CSS.markLazyDeclared(name);
} else {
namesToFiles[name + '!r'] = [file];
}
};
let ignoreTags = {
html: 1,
body: 1
};
let cssNameNewProcessor = (css, ctx) => {
let pInfo = cssParser(css, ctx.shortFile);
if (pInfo.nests.length) { //标记过于复杂的样式规则
checker.CSS.markGlobal(ctx.file, '"' + pInfo.nests.join('","') + '"');
}
let tokens = pInfo.tokens;
let modifiers = [];
for (let token of tokens) {
let id = token.name;
if (token.type == 'tag' || token.type == 'sattr') {
if (token.type == 'sattr') {
id = '[' + id + ']';
}
if (!ignoreTags[id]) { //标签或属性选择器
ctx.fileTags[id] = id;
if (!ctx.tagsToFiles[id]) {
ctx.tagsToFiles[id] = Object.create(null);
}
ctx.tagsToFiles[id][ctx.file] = id;
}
} else if (token.type == 'class') {
let result = id;
if (!token.isGlobal) {
let p = id.replace(configs.selectorKeepNameReg, '$1');
let t = id.replace(p, '');
let i = genCssSelector(p, ctx.namesKey, ctx.globalReservedMap);
if (t) {
result = i + t;
} else {
result = i;
}
ctx.cNamesMap[id] = result;
if (ctx.addToGlobalCSS) {
//记录重名的
if (configs.log &&
ctx.namesMap[id] &&
ctx.namesToFiles[id] &&
!ctx.namesToFiles[id][ctx.file]) {
checker.CSS.markExists('.' + id, ctx.file, Object.keys(ctx.namesToFiles[id]) + '');
}
ctx.namesMap[id] = result;
ctx.namesMap[p] = i;
addGlobal(id, result, 0, 0, ctx.file, ctx.namesToFiles);
addGlobal(p, i, 0, 0, ctx.file, ctx.namesToFiles);
}
ctx.cNamesToFiles[id + '!r'] = [ctx.file];
}
modifiers.push({
start: token.start,
end: token.end,
content: result
});
} else if (token.type == 'global') {
modifiers.push({
start: token.start,
end: token.end,
content: token.content
});
}
}
for (let i = modifiers.length; i--;) {
let m = modifiers[i];
css = css.substring(0, m.start) + m.content + css.substring(m.end);
}
return css;
};
let cssNameGlobalProcessor = (css, ctx) => {
let pInfo = cssParser(css, ctx.shortFile);
if (pInfo.nests.length && !ctx.lazyGlobal) {
checker.CSS.markGlobal(ctx.file, '"' + pInfo.nests.join('","') + '"');
}
let tokens = pInfo.tokens;
for (let i = tokens.length; i--;) {
let token = tokens[i];
let id = token.name;
if (token.type == 'tag' || token.type == 'sattr') {
if (token.type == 'sattr') {
id = '[' + id + ']';
}
if (!ignoreTags[id]) {
ctx.fileTags[id] = id;
if (!ctx.tagsToFiles[id]) {
ctx.tagsToFiles[id] = Object.create(null);
}
ctx.tagsToFiles[id][ctx.file] = id;
}
} else if (token.type == 'class') {
ctx.cNamesMap[id] = id;
//记录重名的
if (configs.log &&
ctx.namesMap[id] &&
ctx.namesToFiles[id] &&
!ctx.namesToFiles[id][ctx.file]) {
checker.CSS.markExists('.' + id, ctx.file, Object.keys(ctx.namesToFiles[id]) + '');
}
ctx.namesMap[id] = id;
addGlobal(id, id, ctx.globalGuid, ctx.lazyGlobal, ctx.file, ctx.namesToFiles);
if (ctx.cNamesToFiles) {
ctx.cNamesToFiles[id + '!r'] = ctx.namesToFiles[id + '!r'];
}
}
}
};
module.exports = {
cssRefReg,
refProcessor,
genCssNamesKey,
genCssSelector,
cssNameNewProcessor,
cssNameGlobalProcessor
};