UNPKG

essi

Version:

Enhanced SSI with Juicer & Velocity

347 lines (312 loc) 10.1 kB
var urlLib = require("url"); var pathLib = require("path"); var util = require("util"); var querystring = require("querystring"); var merge = require("merge"); var Helper = require("./helper"); function AssetsTool(realpath, param, assetsFlag) { this.fakePath = "http://fake_path/"; this.from = realpath.replace(pathLib.join(param.rootdir, '/'), this.fakePath); this.cdnPath = param.cdnPath; if (param.cdnPath && /^https?:\/\/|^\/\//.test(param.cdnPath)) { var prepath = param.cdnPath.replace(/([^\/])$/, "$1/"); var version = param.version ? (param.version + '/') : ''; this.cdnPath = urlLib.resolve(prepath, version); } this.content = ''; this.assetsFlag = assetsFlag || false; this.random = param.random; this.css = param.css; this.js = param.js; this.assetsCharset = param.assetsCharset || "utf-8"; this.scripts = { top: [], bottom: [] }; this.styles = { top: [], bottom: [] }; } AssetsTool.prototype = { constructor: AssetsTool, groupToken: function (group) { return "_ESSI_INLINE_" + group + '_'; }, rough_classify: function (tag, path, type) { var fepos = tag.match(/\sfe\-move=(["'])([^"']*?)\1/); var pos = (fepos && fepos[2]) ? fepos[2] : null; if (pos) { this[type][pos].push(tag.replace(/\s{0,}fe\-move=(["'])([^"']*?)\1/, '')); return '' } else { return tag; } }, clear: function (str) { var inlineRegx = /inline=(['"])true\1/; var feMoveRegx = /fe\-move=(["'])[^"']*?\1/; return str.replace(inlineRegx, '').replace(feMoveRegx, '').replace(/\s{1,}/g, ' ').replace(/(^\s*)|(\s*$)/g, ''); }, extractScripts: function () { this.content = this.content.replace(/<script([^>]*?)>[\s\S]*?<\/script>/g, (function (all, attr) { var srcRegx = /src=(['"])([^"']*?)\1/; var matched = attr.match(srcRegx); if (matched && matched[2]) { if (/inline=(['"])true\1/.test(attr)) { attr = this.clear(attr.replace(srcRegx, '')); return "<script " + attr + ">\n" + '<!--\#remote url="' + matched[2] + '"-->' + "\n</script>"; } else { return this.rough_classify(all, matched[2], "scripts"); } } else { return this.rough_classify(all, null, "scripts"); } }).bind(this)); }, extractStyles: function () { this.content = this.content.replace(/<link([^>]*?)>/g, (function (all, attr) { var relRegx = /rel=(['"])stylesheet\1/; var hrefRegx = /href=(['"])([^"']*?)\1/; var matched = attr.match(hrefRegx); if (matched && matched[2]) { if (/inline=(['"])true\1/.test(attr)) { attr = this.clear(attr.replace(/\s{0,}\/\s{0,}$/, '').replace(hrefRegx, '').replace(relRegx, '')); return "<style " + attr + ">\n" + '<!--\#remote url="' + matched[2] + '"-->' + "\n</style>"; } else { return this.rough_classify(all, matched[2], "styles"); } } else { return this.rough_classify(all, null, "styles"); } }).bind(this)); }, rough_place: function (type) { var tag; for (var pos in this[type]) { if (this[type][pos].length == 0) { continue; } tag = this[type][pos].join("\n"); if (pos == "top") { if (/<\/head>/.test(this.content)) { this.content = this.content.replace(/<\/head>/, function (mm) { return tag + "\n" + mm; }); } else { this.content = tag + "\n" + this.content; } } else if (pos == "bottom") { if (/<\/body>/.test(this.content)) { this.content = this.content.replace(/<\/body>/, function (mm) { return tag + "\n" + mm; }); } else { this.content += "\n" + tag; } } } }, addTimestamp: function (src) { var query = {}; src = src.replace(/([^\?])\?([^\?].*)$/, function (all, $1, $2) { query = merge.recursive(true, query, querystring.parse($2)); return $1; }); if (!this.assetsFlag) { if (this.random) { query['_random'] = Helper.random(); } } var qs = querystring.stringify(query); return src + (qs ? ('?' + qs) : ''); }, combo: function (type) { var self = this; var config = { scripts: [ /<script[^>]*? fe\-group=(['"])([^"']*?)\1[^>]*?>[\s\S]*?<\/script>\n{0,}/g, /<script[^>]*? src=(['"])([^"']*?)\1[^>]*?>[\s\S]*?<\/script>/, function (src) { src = self.addTimestamp(src); return '<script type="text/javascript" charset="' + this.assetsCharset + '" src="' + src + '"></script>'; }.bind(this) ], styles: [ /<link[^>]*? fe\-group=(['"])([^"']*?)\1[^>]*?>\n{0,}/g, /<link[^>]*? href=(['"])([^"']*?)\1[^>]*?>/, function (href) { href = self.addTimestamp(href); return '<link type="text/css" rel="stylesheet" href="' + href + '">'; }.bind(this) ] }; if (!this.cdnPath) { this.content = this.content.replace(/\s{0,}fe\-group=(['"])([^"']*?)\1\s{0,}/g, ''); return; } var tag = config[type]; if (typeof tag == "undefined") { return; } var arr = {}; this.content = this.content.replace(tag[0], (function (all, $1, group) { var matched = all.match(tag[1]); var URL = (matched && util.isArray(matched) && matched[2]) ? matched[2] : null; if (URL && (URL.indexOf(this.cdnPath) != -1 || URL.indexOf('$') == 0)) { if (!arr[group]) { arr[group] = []; } arr[group].push(URL.replace(/([^\?])\?[^\?].*$/, "$1")); return arr[group].length == 1 ? this.groupToken(group) : ''; } else { return all; } }).bind(this)); var url = this.getURL(arr); for (var group in url) { this.content = this.content.replace(this.groupToken(group), tag[2](url[group])); } }, getURL: function (lists) { function getReg(path) { var reg = path.split(''); var len = reg.length; for (var i = 0; i < len; i++) { if (['.', '/', '?', ':', '$', '{', '}'].indexOf(reg[i]) != -1) { reg[i] = "\\" + reg[i]; } } return new RegExp('^(' + reg.join("?)(") + "?)"); } var arr; for (var key in lists) { arr = lists[key]; if (arr.length > 1) { var reg = getReg(arr[0]); var matched = ''; arr.sort(function (a, b) { if (!reg) { reg = getReg(a); } var mm = b.match(reg); if (mm) { var tmp = ''; for (var i = 1; i < mm.length; i++) { if (mm[i]) { tmp += mm[i]; } else { break; } } if (!matched || tmp.length < matched.length) { matched = tmp; } if (matched) { reg = getReg(matched); } } return 0; }); if (matched && !/\/$/.test(matched)) { if (/\//.test(matched)) { matched = urlLib.resolve(matched, "./"); } else { matched = ''; } } if (matched) { for (var i = 0; i < arr.length; i++) { arr[i] = arr[i].replace(matched, ''); } } arr = Helper.unique(arr); if (arr.length) { lists[key] = matched + "??" + arr.join(','); } else { lists[key] = matched; } } else if (arr.length) { lists[key] = arr[0]; } else { delete lists[key]; } } return lists; }, html: function (content) { if (typeof content == "undefined") { return this.content; } else { this.content = content; } }, action: function (content, resolving) { this.html(content); var urlResolve = function (all, $1, Q, url, $4) { if (typeof this.cdnPath == "string") { var selfRepo = this.cdnPath.replace(/h?t?t?p?s?:?\/\/[^\/]*?\//, ''); var isAbs = /^https?:\/\/|^\/\/|^\$\!?\{[^\}]*?\}/.test(url); if (this.cdnPath && !isAbs) { url = urlLib.resolve(this.from, url).replace(this.fakePath, this.cdnPath); } if (url.indexOf(selfRepo) != -1 && ((selfRepo == '' && !isAbs) || selfRepo != '')) { var scriptRegx = /(^<script)\s{1,}/i; var charsetRegx = /charset=/i; if (scriptRegx.test($1) && !(charsetRegx.test($1) || charsetRegx.test($4))) { $1 = $1.replace(scriptRegx, function (all, tag) { return tag + ' charset="' + this.assetsCharset + '" '; }.bind(this)); } if (this.assetsFlag) { url = url.replace(/\.css$|\.css(\,)|\.less$|\.less(\,)|\.scss$|\.scss(,)/g, this.css + "$1$2$3"); url = url.replace(/(\.tpl)$|\.js$|\.js(\,)/g, "$1" + this.js + "$2"); } else { url = url.replace(/(\.less)$|(\.scss)$|(\.less)(\,)|(\.scss)(,)/g, "$1$2$3$5.css$4$6"); url = url.replace(/(\.tpl)$|(\.tpl)(\,)$/g, "$1$2.js$3"); } url = this.addTimestamp(url); return $1 + url + $4; } else { return all; } } else { return all; } }.bind(this); if (resolving) { this.content = this.content.replace(/(<script[^>]*? src=(['"]))([^"']*?)(\2[^>]*?>[\s\S]*?<\/script>)/g, urlResolve); this.content = this.content.replace(/(<link[^>]*? href=(['"]))([^"']*?)(\2[^>]*?>)/g, urlResolve); } this.extractStyles(); this.extractScripts(); this.rough_place("styles"); this.rough_place("scripts"); this.combo("styles"); this.combo("scripts"); return this.content; } }; module.exports = AssetsTool;