magix-combine
Version:
合并Magix View的html,js,css成一个js文件,并检测html,js,css中可能存在的问题
310 lines (305 loc) • 13.2 kB
JavaScript
/*
模板处理总入口
*/
let path = require('path');
let fs = require('fs');
let chalk = require('chalk');
let utils = require('./util');
let fd = require('./util-fd');
let deps = require('./util-deps');
let atpath = require('./util-atpath');
let configs = require('./util-config');
let tmplEvent = require('./tmpl-event');
let tmplCmd = require('./tmpl-cmd');
let tmplArt = require('./tmpl-art');
let tmplCutsomTag = require('./tmpl-customtag');
let tmplGuid = require('./tmpl-guid');
let tmplAttr = require('./tmpl-attr');
let tmplClass = require('./tmpl-attr-class');
let tmplPartial = require('./tmpl-partial');
let tmplStatic = require('./tmpl-static');
let unmatchChecker = require('./checker-tmpl-unmatch');
let checker = require('./checker');
let tmplVars = require('./tmpl-vars');
let md5 = require('./util-md5');
let slog = require('./util-log');
let tmplToFn = require('./tmpl-tofn');
let tmplQuick = require('./tmpl-quick');
let tmplL3 = require('./tmpl-l3');
let consts = require('./util-const');
//let tmplDecode = require('./tmpl-decode');
let commentReg = /<!--[\s\S]*?-->/g;
let htmlCommentCelanReg = /<!--[\s\S]*?-->/g;
let tmplVarsReg = /:(const|global|updateby)\[([^\[\]]*)\]/g;
let artEngineReg = /:art(?:\s*=\s*(true|false))?(?:$|:)/;
let sep = path.sep;
let holder = '\u001f';
let removeVdReg = /\u0002/g;
let removeIdReg = /\u0001/g;
let stringReg = /\u0017([^\u0017]*?)\u0017/g;
let unsupportCharsReg = /[\u0000-\u0007\u0011-\u0019\u001e\u001f]/g;
let globalTmplRootReg = /[\u0003\u0006]\./g;
let globalRootReg = /\u0003/g;
let commandAnchorRecover = (tmpl, refTmplCommands) => tmplCmd.recover(tmpl, refTmplCommands).replace(globalTmplRootReg, '').replace(globalRootReg, '$$$$');
let brReg = /(?:\r\n|\r|\n)/;
let brPlaceholder = m => {
let count = m.split(brReg).length;
return new Array(count).join('\n');
};
let processTmpl = (fileContent, cache, cssNamesMap, magixTmpl, e, reject, file, flagsInfo, lang) => {
let key = magixTmpl + holder + fileContent;
let fCache = cache[key];
if (!fCache) {
if (configs.debug && unsupportCharsReg.test(fileContent)) {
slog.log(chalk.red(`[MXC Error(tmpl)] unsupport character : ${unsupportCharsReg.source}`), 'at', chalk.magenta(e.shortHTMLFile));
reject(new Error('[MXC Error(tmpl)] unsupport character'));
return;
}
e.templateLang = lang;
try {
fileContent = configs.compileTmplStart(fileContent, e);
} catch (ex) {
slog.ever(chalk.red('[MXC Error(tmpl)] compile template error ' + ex.message), 'at', chalk.magenta(e.shortHTMLFile));
ex.message += ' at ' + e.shortHTMLFile;
reject(ex);
return;
}
fileContent = fileContent.replace(commentReg, brPlaceholder);
if (flagsInfo.artEngine && magixTmpl) {
if (configs.magixUpdaterQuick) {
fileContent = tmplQuick.preProcess(fileContent, e);
}
fileContent = tmplArt(fileContent, e);
}
if (configs.debug) {
try {
unmatchChecker(fileContent, e);
} catch (ex) {
slog.ever(chalk.red(ex.message), 'at', chalk.magenta(e.shortHTMLFile));
ex.message += ' at ' + e.shortHTMLFile;
reject(ex);
return;
}
}
let srcContent = fileContent;
try {
fileContent = tmplCutsomTag.process(fileContent, {
moduleId: e.moduleId,
pkgName: e.pkgName,
//checkTmplDuplicateAttr: e.checker.tmplDuplicateAttr,
srcOwnerHTMLFile: file,
shortOwnerHTMLFile: e.shortHTMLFile,
artEngine: flagsInfo.artEngine
}, e);
} catch (ex) {
slog.ever(chalk.red(ex.message), 'at', chalk.magenta(e.shortHTMLFile));
ex.message += ' at ' + e.shortHTMLFile;
reject(ex);
return;
}
//console.log(fileContent);
if (flagsInfo.artEngine && magixTmpl && srcContent != fileContent) {
if (configs.magixUpdaterQuick) {
fileContent = tmplQuick.preProcess(fileContent, e);
}
fileContent = tmplArt(fileContent, e);
}
if (configs.debug) {
try {
unmatchChecker(fileContent, e);
} catch (ex) {
slog.ever(chalk.red(ex.message), 'at', chalk.magenta(e.shortHTMLFile));
ex.message += ' at ' + e.shortHTMLFile;
reject(ex);
return;
}
}
let temp = Object.create(null);
cache[key] = temp;
fileContent = fileContent.replace(htmlCommentCelanReg, '').trim();
let gId;
if (configs.tmplOptionalChainingSupport) {
gId = utils.uId('\x00', fileContent);
fileContent = gId + fileContent;
}
fileContent = tmplCmd.compile(fileContent);
if (configs.tmplOptionalChainingSupport) {
let i = fileContent.indexOf(gId);
let prefix = fileContent.substring(0, i);
fileContent = fileContent.substring(i + gId.length);
if (prefix) {
fileContent = `<%${prefix}%>` + fileContent;
}
}
let refTmplCommands = Object.create(null);
e.refLeakGlobal = {
reassigns: []
};
if (magixTmpl) {
try {
fileContent = tmplVars.process(fileContent, e, flagsInfo);
} catch (ex) {
reject(ex);
}
}
//fileContent = tmplCmd.compress(fileContent, e.isOldTemplate);
fileContent = tmplCmd.store(fileContent, refTmplCommands); //模板命令移除,防止影响分析
if (!configs.debug) {
fileContent = fileContent.replace(consts.revisableGReg, m => {
let src = tmplCmd.recover(m, refTmplCommands);
checker.Tmpl.checkStringRevisable(m, src, e);
return md5(m, 'revisableString', configs.revisableStringPrefix);
});
}
if (configs.tmplOutputWithEvents) {
let tmplEvents = tmplEvent.extract(fileContent);
temp.events = tmplEvents;
}
fileContent = tmplAttr.process(fileContent, e, refTmplCommands);
//console.log(fileContent);
try {
fileContent = tmplCmd.tidy(fileContent);
} catch (ex) {
slog.ever(chalk.red('[MXC Error(tmpl)] minify html error : ' + ex.message), 'at', chalk.magenta(e.shortHTMLFile));
reject(ex);
return;
}
if (magixTmpl) {
if (configs.magixUpdaterIncrement) {
if (configs.tmplStaticAnalyze) {
fileContent = tmplStatic(fileContent, e.shortHTMLFile);
}
} else {
fileContent = tmplGuid.add(fileContent, refTmplCommands, e.refLeakGlobal, e);
//console.log(tmplCmd.recover(fileContent,refTmplCommands));
if (e.refLeakGlobal.exists) {
slog.ever(chalk.red('[MXC Error(tmpl)] segment failed'), 'at', chalk.magenta(e.shortHTMLFile), 'more info:', chalk.magenta('https://github.com/thx/magix-combine/issues/21'));
if (e.refLeakGlobal.reassigns) {
e.refLeakGlobal.reassigns.forEach(it => {
slog.ever(it);
});
}
}
}
}
fileContent = configs.compileTmplEnd(fileContent);
//if (ctrl != 'bare') {
fileContent = tmplClass.process(fileContent, cssNamesMap, refTmplCommands, e); //处理class name
//}
for (let p in refTmplCommands) {
let cmd = refTmplCommands[p];
if (utils.isString(cmd)) {
refTmplCommands[p] = cmd.replace(removeVdReg, '')
.replace(removeIdReg, '')
.replace(stringReg, '$1');
}
}
let info;
if (magixTmpl && configs.magixUpdaterIncrement) {
info = {
tmpl: commandAnchorRecover(fileContent, refTmplCommands)
};
} else {
info = tmplPartial.process(fileContent, refTmplCommands, e, flagsInfo);
}
temp.info = info;
//console.log(info);
fCache = temp;
}
return fCache;
};
module.exports = e => {
return new Promise((resolve, reject) => {
let cssNamesMap = e.cssNamesMap,
from = e.from,
moduleId = e.moduleId,
fileContentCache = Object.create(null);
//仍然是读取view.js文件内容,把里面@到的文件内容读取进来
e.content = e.content.replace(configs.fileTmplReg, (match, quote, ctrl, name, ext, flags) => {
name = atpath.resolvePath(name, moduleId);
let file = path.resolve(path.dirname(from) + sep + name + '.' + ext);
let fileContent = name;
let singleFile = (name == 'template' && e.contentInfo);
if (!singleFile) {
deps.addFileDepend(file, e.from, e.to);
e.fileDeps[file] = 1;
} else {
file = e.from;
}
if (singleFile || fs.existsSync(file)) {
let magixTmpl = ctrl == 'updater' || flags;
let oldTemplate = false,
oldEventDataset = Object.create(null);
if (configs.disableMagixUpdater) {
magixTmpl = false;
}
fileContent = singleFile ? e.contentInfo.template : fd.read(file);
if (configs.checkOldTempalte &&
tmplL3.isOldTemplate(fileContent)) {
fileContent = tmplL3.storeOldEvent(fileContent, oldEventDataset);
magixTmpl = false;
oldTemplate = true;
e.isOldTemplate = true;
}
let lang = singleFile ? e.contentInfo.templateLang : ext;
e.htmlModuleId = utils.extractModuleId(file);
e.srcHTMLFile = file;
e.shortHTMLFile = file.replace(configs.moduleIdRemovedPath, '').substring(1);
if (ext != lang) {
slog.ever(chalk.red('[MXC Tip(tmpl)] conflicting template language'), 'at', chalk.magenta(e.shortHTMLFile), 'near', chalk.magenta(match + ' and ' + e.contentInfo.templateTag));
}
let flagsInfo = {
artEngine: configs.tmplArtEngine,
tmplScopedGlobalVars: Object.assign(Object.create(null), configs.tmplGlobalVars),
tmplScopedConstVars: Object.assign(Object.create(null), configs.tmplConstVars)
};
if (flags) {
flags.replace(tmplVarsReg, (m, key, vars) => {
vars = vars.trim();
vars = vars.split(',');
let setKey = '';
if (key == 'const') {
setKey = 'tmplScopedConstVars';
} else if (key == 'global') {
setKey = 'tmplScopedGlobalVars';
} else if (key == 'updateby') {
setKey = 'tmplScopedUpdateBy';
}
if (!flagsInfo[setKey])
flagsInfo[setKey] = Object.create(null);
for (let v of vars) {
flagsInfo[setKey][v] = 1;
}
});
let m = flags.match(artEngineReg);
if (m) {
flagsInfo.artEngine = true;
if (m[1]) {
flagsInfo.artEngine = m[1] === 'true';
}
}
}
let fcInfo = processTmpl(fileContent, fileContentCache, cssNamesMap, magixTmpl, e, reject, file, flagsInfo, lang);
if (magixTmpl) {
if (configs.magixUpdaterIncrement) {
let tmpl = fcInfo.info.tmpl;
return configs.magixUpdaterQuick ? tmplQuick.process(tmpl, e) : tmplToFn(tmpl, e.shortHTMLFile, e.globalVars);
}
let temp = {
html: fcInfo.info.tmpl,
subs: fcInfo.info.list
};
if (configs.debug) temp.file = e.shortHTMLFile;
if (configs.tmplOutputWithEvents) temp.events = fcInfo.events;
return JSON.stringify(temp);
}
if (oldTemplate) {
fcInfo.info.tmpl = tmplL3.recoverOldEvent(fcInfo.info.tmpl, oldEventDataset);
}
return JSON.stringify(fcInfo.info.tmpl);
}
return quote + 'unfound file:' + name + '.' + ext + quote;
});
resolve(e);
});
};