UNPKG

magix-combine

Version:

合并Magix View的html,js,css成一个js文件,并检测html,js,css中可能存在的问题

399 lines (393 loc) 20.8 kB
/* 样式处理入口 读取js代码中的样式占位规则,把占位规则处理成真实的内容 */ let cssnano = require('cssnano'); let path = require('path'); let configs = require('./util-config'); let atpath = require('./util-atpath'); let cssAtRule = require('./css-atrule'); let cssFileRead = require('./css-read'); let deps = require('./util-deps'); let checker = require('./checker'); let cssGlobal = require('./css-global'); let cssComment = require('./css-comment'); let utils = require('./util'); let cloneAssign = utils.cloneAssign; let { cssNameNewProcessor, cssNameGlobalProcessor, genCssNamesKey, cssRefReg, refProcessor } = require('./css-selector'); const CleanCss = require('clean-css'); //处理css文件 //另外一个思路是:解析出js中的字符串,然后在字符串中做替换就会更保险,目前先不这样做。 //https://github.com/Automattic/xgettext-js //处理js文件中如 'global@x.less' '@x.less:selector' 'ref@../x.scss' 等各种情况 //"abc(@style.css:xx)yyzz" //[ref="@../default.css:inmain"] .open{ // color:red //} let cssTmplReg = /(['"]?)\(?(global|ref|names)?\u0012@([\w\.\-\/\\]+?)(\.css|\.less|\.mx|\.mmx|\.style)(?::?\[([\w-,]+)\]|:\.?([\w\-]+))?\)?\1(;?)/g; let sep = path.sep; const cleanCss = new CleanCss(); module.exports = (e, inwatch) => { if (inwatch) { checker.CSS.clearUsed(e.from); checker.CSS.clearUsedTags(e.from); } let cssNamesMap = Object.create(null); let cssNamesToFiles = Object.create(null); let cssNamesKey; let addToGlobalCSS = true; let gCSSNamesMap = Object.create(null); let gCSSNamesToFiles = Object.create(null); let currentFile = ''; let cssContentCache = Object.create(null); let gCSSTagToFiles = Object.create(null); return cssGlobal.process({ context: e, inwatch: inwatch }).then(gInfo => { //console.log(e.cssNamesInFiles); //console.log('global', gCSSNamesMap); return new Promise((resolve, reject) => { cloneAssign(gCSSNamesMap, gInfo.globalCssNamesMap); cloneAssign(gCSSNamesToFiles, gInfo.globalCssNamesInFiles); cloneAssign(gCSSTagToFiles, gInfo.globalCssTagsInFiles); e.cssNamesMap = gCSSNamesMap; e.cssNamesInFiles = gCSSNamesToFiles; e.cssTagsInFiles = gCSSTagToFiles; cssTmplReg.lastIndex = 0; if (cssTmplReg.test(e.content)) { //有需要处理的@规则 cssTmplReg.lastIndex = 0; let count = 0; let tempMatchToFile = Object.create(null); let folder = path.dirname(e.from); let cmtStores = Object.create(null); let resume = () => { e.content = e.content.replace(cssTmplReg, (m, q, prefix, name, ext, keys, key, tail) => { let info = tempMatchToFile[m]; let { file, scopedStyle, shortCssFile, globalStyle, markUsedFiles } = info; let fileName = path.basename(file); let r = cssContentCache[file]; //从缓存中获取当前文件的信息 //如果不存在就返回一个不存在的提示 if (!r.exists) { if (q) { m = m.slice(1, -1); } checker.CSS.markUnexists(m, e.from); return ['\'$throw_' + name + ext + '\'', q + 'unfound:' + name + ext + q]; } let fileContent = r.css; let store = cmtStores[file]; if (store) { fileContent = cssComment.recover(fileContent, store); } cssNamesKey = genCssNamesKey(file); if (scopedStyle || globalStyle) { cssNamesMap = gCSSNamesMap; } else { cssNamesMap = Object.create(null); cssNamesToFiles = Object.create(null); currentFile = file; let cssTagsToFiles = Object.create(null); let cssTagsMap = Object.create(null); if (prefix != 'global') { //如果不是项目中全局使用的 addToGlobalCSS = prefix != 'names'; //不是读取css名称对象的 if (keys || key) { //有后缀时也不添加到全局 addToGlobalCSS = false; } if (!r.cssNames) { fileContent = fileContent.replace(cssRefReg, (m, q, f, ext, selector) => { return refProcessor(file, f, ext, selector, gInfo); }); try { fileContent = cssNameNewProcessor(fileContent, { shortFile: shortCssFile, namesMap: gCSSNamesMap, globalReservedMap: gInfo.globalReservedMap, namesToFiles: gCSSNamesToFiles, namesKey: cssNamesKey, cNamesMap: cssNamesMap, cNamesToFiles: cssNamesToFiles, addToGlobalCSS: addToGlobalCSS, file: currentFile, fileTags: cssTagsMap, tagsToFiles: cssTagsToFiles }); } catch (ex) { reject(ex); } //@规则处理 fileContent = cssAtRule(fileContent, cssNamesKey, false, gInfo); //if (addToGlobalCSS) { r.cssNames = cssNamesMap; r.fileContent = fileContent; r.namesToFiles = cssNamesToFiles; r.tagsToFiles = cssTagsToFiles; r.cssTags = cssTagsMap; cloneAssign(gCSSTagToFiles, cssTagsToFiles); checker.CSS.fileToTags(file, cssTagsMap, inwatch); checker.CSS.fileToSelectors(file, cssNamesMap, inwatch); //} } else { cssNamesMap = r.cssNames; cssNamesToFiles = r.namesToFiles; cssTagsToFiles = r.tagsToFiles; fileContent = r.fileContent; if (addToGlobalCSS) { cloneAssign(gCSSNamesMap, cssNamesMap); cloneAssign(gCSSNamesToFiles, cssNamesToFiles); cloneAssign(gCSSTagToFiles, cssTagsToFiles); } } } else { //global let globals = configs.globalCss; let unchecked = configs.uncheckGlobalCss; if (globals.indexOf(file) == -1) { if (unchecked.indexOf(file) == -1) { fileContent = fileContent.replace(cssRefReg, (m, q, f, ext, selector) => { return refProcessor(file, f, ext, selector, gInfo); }); try { cssNameGlobalProcessor(fileContent, { shortFile: shortCssFile, namesMap: gCSSNamesMap, namesToFiles: gCSSNamesToFiles, cNamesMap: cssNamesMap, cNamesToFiles: cssNamesToFiles, lazyGlobal: true, file: currentFile, fileTags: cssTagsMap, tagsToFiles: cssTagsToFiles }); } catch (ex) { reject(ex); } cloneAssign(gCSSNamesMap, cssNamesMap); cloneAssign(gCSSTagToFiles, cssTagsToFiles); cssGlobal.addReserved(cssNamesMap); //checker.CSS.fileToSelectors(file, cssNamesMap, inwatch); //checker.CSS.fileToTags(file, cssTagsMap, inwatch); } //checker.CSS.markGlobal(e.from, 'global@' + name + ext); } } } let replacement; if (prefix == 'names' || keys) { //如果是读取css选择器名称对象 if (keys) { //从对象中只挑取某几个key checker.CSS.markUsed(markUsedFiles, keys.split(','), e.from); replacement = JSON.stringify(cssNamesMap, keys.split(',')); } else { //全部名称对象 checker.CSS.markUsed(markUsedFiles, Object.keys(cssNamesMap), e.from); replacement = JSON.stringify(cssNamesMap); } } else if (prefix == 'ref') { //如果是引用css则什么都不用做 replacement = ''; tail = ''; } else if (key) { //仅读取文件中的某个名称 checker.CSS.markUsed(markUsedFiles, key, e.from); let c = cssNamesMap[key]; if (!c) { if (configs.selectorSilentErrorCss) { c = key; } else { checker.CSS.markUnexists(m, e.from); c = 'unfound-[' + key + ']-from-' + fileName; } } replacement = q + c + q; } else { //输出整个css文件内容 if (configs.debug) { if (r.map) { fileContent += r.map; let c = JSON.stringify(fileContent); c = configs.applyStyleProcessor(c, shortCssFile, cssNamesKey, e); replacement = JSON.stringify(cssNamesKey) + ',' + c; } else if (r.styles) { replacement = '['; for (let s of r.styles) { let c = JSON.stringify(s.css + (s.map || '')); c = configs.applyStyleProcessor(c, s.short, s.key, e); replacement += JSON.stringify(s.key) + ',' + c + ','; } replacement = replacement.slice(0, -1); replacement += ']'; } else { let c = JSON.stringify(fileContent); c = configs.applyStyleProcessor(c, shortCssFile, cssNamesKey, e); replacement = JSON.stringify(cssNamesKey) + ',' + c; } } else { let c = JSON.stringify(fileContent); c = configs.applyStyleProcessor(c, shortCssFile, cssNamesKey, e); replacement = JSON.stringify(cssNamesKey) + ',' + c; } } tail = tail ? tail : ''; return replacement + tail; }); resolve(e); }; let check = () => { count--; if (!count && !check.$resume) { //依赖的文件全部读取完毕 check.$resume = true; resume(); } }; let setFileCSS = (file, shortCssFile, css) => { let p = configs.cssContentProcessor(css, shortCssFile, e); if (!p.then) { p = Promise.resolve(p); } p.then(css => { cssContentCache[file].css = css; check(); }, error => { if (e.contentInfo) { file += '@' + e.contentInfo.fileName; } reject(error); check(); }); }; let processFile = (match, name, ext, file) => { count++; //记录当前文件个数,因为文件读取是异步,我们等到当前模块依赖的css都读取完毕后才可以继续处理 let scopedStyle = false; let globalStyle = false; let refInnerStyle = e.contentInfo && name == 'style'; let shortCssFile; let markUsedFiles; if (name == 'scoped' && ext == '.style') { file = name + ext; scopedStyle = true; shortCssFile = file; configs.scopedCss.forEach(sc => { deps.addFileDepend(sc, e.from, e.to); }); markUsedFiles = configs.scopedCss; } else if (name == 'global' && ext == '.style') { file = name + ext; shortCssFile = file; globalStyle = true; configs.globalCss.forEach(sc => { deps.addFileDepend(sc, e.from, e.to); }); markUsedFiles = configs.globalCss; } else { name = atpath.resolveName(name, e.moduleId); //先处理名称 if (refInnerStyle) { file = e.from; } else { deps.addFileDepend(file, e.from, e.to); e.fileDeps[file] = 1; } markUsedFiles = file; shortCssFile = file.replace(configs.moduleIdRemovedPath, '').substring(1); } tempMatchToFile[match] = { markUsedFiles, scopedStyle, globalStyle, file, shortCssFile }; if (!cssContentCache[file]) { //文件尚未读取 cssContentCache[file] = 1; let promise; if (scopedStyle) { promise = Promise.resolve({ exists: true, content: gInfo.scopedStyle, styles: gInfo.scopedStyles }); } else if (globalStyle) { promise = Promise.resolve({ exists: true, content: gInfo.globalStyle, styles: gInfo.globalStyles }); } else { promise = cssFileRead(file, e, match, ext, refInnerStyle); } promise.then(info => { //写入缓存,因为同一个view.js中可能对同一个css文件多次引用 cssContentCache[file] = { exists: info.exists, css: '' }; if (info.exists && info.content) { cssContentCache[file].map = info.map; cssContentCache[file].styles = info.styles; if (!configs.debug) { const result = configs.unstable_performanceOptimization ? Promise.resolve({ css: cleanCss.minify(info.content).styles, }) : cssnano().process(info.content, Object.assign({}, configs.cssnano) ) result.then(r => { setFileCSS(file, shortCssFile, r.css); }, error => { if (e.contentInfo) { file += '@' + e.contentInfo.fileName; } reject(error); check(); }); } else { let cssStr = info.content; let store = cmtStores[file] = Object.create(null); cssStr = cssComment.store(cssStr, store); setFileCSS(file, shortCssFile, cssStr); } } else { check(); } }).catch(reject); } else { check(); } }; let tasks = []; let doTask = () => { if (tasks.length) { let i = 0; while (i < tasks.length) { processFile.apply(null, tasks[i++]); } } else { resume(); } }; e.content.replace(cssTmplReg, (m, q, prefix, name, ext) => { name = atpath.resolveName(name, e.moduleId); let file = path.resolve(folder + sep + name + ext); if (configs.scopedCssMap[file]) { name = 'scoped'; ext = '.style'; } else if (configs.globalCssMap[file]) { name = 'global'; ext = '.style'; } tasks.push([m, name, ext, file]); }); doTask(); } else { resolve(e); } }); }); };