From b1f1ba6008aaf9ddd82e1c812426e901443595f3 Mon Sep 17 00:00:00 2001 From: Rodrigo Fernandes Date: Sat, 16 Apr 2016 12:06:03 +0100 Subject: [PATCH] Use pre-compiled templates in node --- .gitignore | 3 + .jscsrc | 2 - circle.yml | 3 +- package.json | 11 +- scripts/.ignore | 0 scripts/hulk.js | 206 ++++++++++++++++++ release.sh => scripts/release.sh | 11 +- src/hoganjs-utils.js | 30 ++- src/printer-utils.js | 4 +- src/templates/diff2html-templates.js | 9 + .../line-by-line-column-line-number.mustache | 2 +- src/templates/line-by-line-line.mustache | 12 +- 12 files changed, 263 insertions(+), 30 deletions(-) create mode 100644 scripts/.ignore create mode 100755 scripts/hulk.js rename release.sh => scripts/release.sh (89%) create mode 100644 src/templates/diff2html-templates.js diff --git a/.gitignore b/.gitignore index d9ee7ac..6ed8e20 100644 --- a/.gitignore +++ b/.gitignore @@ -19,5 +19,8 @@ target/ node_modules/ npm-debug.log +# Istanbul +coverage/ + # Bower bower_components/ diff --git a/.jscsrc b/.jscsrc index 03eb9b2..8d2a6cc 100644 --- a/.jscsrc +++ b/.jscsrc @@ -21,9 +21,7 @@ "disallowTrailingWhitespace": true, "maximumLineLength": 130, "requireCamelCaseOrUpperCaseIdentifiers": true, - "requireCapitalizedComments": true, "requireCapitalizedConstructors": true, - "requireCurlyBraces": true, "requireSpaceAfterKeywords": [ "if", "else", diff --git a/circle.yml b/circle.yml index addc8c7..b239e90 100644 --- a/circle.yml +++ b/circle.yml @@ -5,6 +5,5 @@ test: - nvm install 5 && npm test post: - npm install - - istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec + - npm test - cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage - - rm -rf ./coverage diff --git a/package.json b/package.json index c4ca33d..21825d0 100644 --- a/package.json +++ b/package.json @@ -32,14 +32,15 @@ "url": "https://www.github.com/rtfpessoa/diff2html/issues" }, "engines": { - "node": ">=0.10" + "node": ">=0.12" }, "preferGlobal": true, "scripts": { - "release": "bash release.sh", - "test": "mocha", + "release": "./scripts/release.sh", + "templates": "./scripts/hulk.js --wrapper node --variable 'browserTemplates' ./src/templates/*.mustache > ./src/templates/diff2html-templates.js", + "test": "istanbul cover _mocha --report lcovonly -- -u exports -R spec ./test/**/*", "style": "jscs src test", - "codacy": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/.bin/codacy-coverage && rm -rf ./coverage" + "codacy": "istanbul cover _mocha --report lcovonly -- -u exports -R spec ./test/**/* && cat ./coverage/lcov.info | codacy-coverage" }, "main": "./src/diff2html.js", "browser": { @@ -56,7 +57,9 @@ "fast-html-parser": "^1.0.1", "istanbul": "^0.4.2", "jscs": "^2.11.0", + "mkdirp": "^0.5.1", "mocha": "^2.4.5", + "nopt": "^3.0.6", "uglifyjs": "^2.4.10" }, "license": "MIT", diff --git a/scripts/.ignore b/scripts/.ignore new file mode 100644 index 0000000..e69de29 diff --git a/scripts/hulk.js b/scripts/hulk.js new file mode 100755 index 0000000..38e58ad --- /dev/null +++ b/scripts/hulk.js @@ -0,0 +1,206 @@ +#!/usr/bin/env node + +/* + * Copyright 2011 Twitter, Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +// dependencies +var hogan = require('hogan.js'); +var path = require('path'); +var nopt = require('nopt'); +var mkderp = require('mkdirp'); +var fs = require('fs'); + + +// locals +var specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\']; +var specialsRegExp = new RegExp('(\\' + specials.join('|\\') + ')', 'g'); +var options = { + 'namespace': String, + 'outputdir': path, + 'variable': String, + 'wrapper': String, + 'version': true, + 'help': true +}; +var shortHand = { + 'n': ['--namespace'], + 'o': ['--outputdir'], + 'vn': ['--variable'], + 'w': ['--wrapper'], + 'h': ['--help'], + 'v': ['--version'] +}; +var templates; + + +// options +options = nopt(options, shortHand); + + +// escape special regexp characters +function esc(text) { + return text.replace(specialsRegExp, '\\$1'); +} + + +// cyan function for rob +function cyan(text) { + return '\033[36m' + text + '\033[39m'; +} + + +// check for dirs and correct ext (<3 for windows) +function extractFiles(args) { + var usage = '\n' + + cyan('USAGE:') + ' hulk [--wrapper wrapper] [--outputdir outputdir] ' + + '[--namespace namespace] [--variable variable] FILES\n\n' + + cyan('OPTIONS:') + ' [-w, --wrapper] :: wraps the template (i.e. amd)\n' + + ' [-o, --outputdir] :: outputs the templates as individual files to a directory\n\n' + + ' [-n, --namespace] :: prepend string to template names\n\n' + + ' [-vn, --variable] :: variable name for non-amd wrapper\n\n' + + cyan('EXAMPLE:') + ' hulk --wrapper amd ./templates/*.mustache\n\n' + + cyan('NOTE:') + ' hulk supports the "*" wildcard and allows you to target specific extensions too\n'; + var files = []; + + if (options.version) { + console.log(require('../package.json').version); + process.exit(0); + } + + if (!args.length || options.help) { + console.log(usage); + process.exit(0); + } + + args.forEach(function(arg) { + + if (/\*/.test(arg)) { + arg = arg.split('*'); + return files = files.concat( + fs.readdirSync(arg[0] || '.') + .map(function(f) { + var file = path.join(arg[0], f); + return new RegExp(esc(arg[1]) + '$').test(f) && fs.statSync(file).isFile() && file; + }) + .filter(function(f) { + return f; + }) + ); + } + + if (fs.statSync(arg).isFile()) files.push(arg); + + }); + + return files; +} + + +// remove utf-8 byte order mark, http://en.wikipedia.org/wiki/Byte_order_mark +function removeByteOrderMark(text) { + if (text.charCodeAt(0) === 0xfeff) { + return text.substring(1); + } + return text; +} + + +// wrap templates +function wrap(file, name, openedFile) { + switch (options.wrapper) { + case "amd": + return 'define(' + (!options.outputdir ? '"' + path.join(path.dirname(file), name) + '", ' : '') + + '[ "hogan.js" ], function(Hogan){ return new Hogan.Template(' + + hogan.compile(openedFile, {asString: 1}) + + ');});'; + case "node": + var globalObj = 'global.' + (options.variable || 'templates') + '["' + name + '"]'; + var globalStmt = globalObj + ' = new Hogan.Template(' + hogan.compile(openedFile, {asString: 1}) + ');'; + var nodeOutput = globalStmt; + + // if we have a template per file the export will expose the template directly + if (options.outputdir) { + nodeOutput = nodeOutput + '\n' + 'module.exports = ' + globalObj + ';'; + } + + return nodeOutput; + default: + return (options.variable || 'templates') + + '["' + name + '"] = new Hogan.Template(' + + hogan.compile(openedFile, {asString: 1}) + + ');'; + } +} + + +function prepareOutput(content) { + var variableName = options.variable || 'templates'; + switch (options.wrapper) { + case "amd": + return content; + case "node": + var nodeExport = ''; + + // if we have aggregated templates the export will expose the template map + if (!options.outputdir) { + nodeExport = 'module.exports = global.' + variableName + ';\n'; + } + + return '(function() {\n' + + 'if (!!!global.' + variableName + ') global.' + variableName + ' = {};\n' + + content + '\n' + + nodeExport + + '})();'; + default: + return 'if (!!!' + variableName + ') var ' + variableName + ' = {};\n' + content; + } +} + + +// write the directory +if (options.outputdir) { + mkderp.sync(options.outputdir); +} + + +// Prepend namespace to template name +function namespace(name) { + return (options.namespace || '') + name; +} + + +// write a template foreach file that matches template extension +templates = extractFiles(options.argv.remain) + .map(function(file) { + var openedFile = fs.readFileSync(file, 'utf-8'); + var name; + if (!openedFile) return; + name = namespace(path.basename(file).replace(/\..*$/, '')); + openedFile = removeByteOrderMark(openedFile.trim()); + openedFile = wrap(file, name, openedFile); + if (!options.outputdir) return openedFile; + fs.writeFileSync(path.join(options.outputdir, name + '.js') + , prepareOutput(openedFile)); + }) + .filter(function(t) { + return t; + }); + + +// output templates +if (!templates.length || options.outputdir) process.exit(0); + +console.log(prepareOutput(templates.join('\n'))); diff --git a/release.sh b/scripts/release.sh similarity index 89% rename from release.sh rename to scripts/release.sh index e757f2d..80f0d9c 100755 --- a/release.sh +++ b/scripts/release.sh @@ -14,6 +14,8 @@ INPUT_JS_FILE=${INPUT_DIR}/diff2html.js INPUT_JS_UI_FILE=${INPUT_UI_DIR}/js/diff2html-ui.js INPUT_CSS_FILE=${INPUT_UI_DIR}/css/diff2html.css +GENERATED_TEMPLATES_FILE=${INTPUT_TEMPLATES_DIR}/diff2html-templates.js + OUTPUT_DIR=dist OUTPUT_JS_FILE=${OUTPUT_DIR}/diff2html.js OUTPUT_MIN_JS_FILE=${OUTPUT_DIR}/diff2html.min.js @@ -30,16 +32,15 @@ echo "Cleaning previous versions ..." rm -rf ${OUTPUT_DIR} mkdir -p ${OUTPUT_DIR} -echo "Copying css file to ${OUTPUT_CSS_FILE}" -cp -f ${INPUT_CSS_FILE} ${OUTPUT_CSS_FILE} - echo "Minifying ${OUTPUT_CSS_FILE} to ${OUTPUT_MIN_CSS_FILE}" +cp -f ${INPUT_CSS_FILE} ${OUTPUT_CSS_FILE} cleancss --advanced --compatibility=ie8 -o ${OUTPUT_MIN_CSS_FILE} ${OUTPUT_CSS_FILE} -echo "Pre-compile hogan.js templates in ${INTPUT_TEMPLATES_DIR}" -hulk --variable "browserTemplates" ${INTPUT_TEMPLATES_DIR}/*.mustache > ${OUTPUT_TEMPLATES_FILE} +echo "Pre-compile hogan.js templates" +npm run templates echo "Minifying ${OUTPUT_TEMPLATES_FILE} to ${OUTPUT_MIN_TEMPLATES_FILE}" +browserify -e ${GENERATED_TEMPLATES_FILE} -o ${OUTPUT_TEMPLATES_FILE} uglifyjs ${OUTPUT_TEMPLATES_FILE} -c -o ${OUTPUT_MIN_TEMPLATES_FILE} echo "Generating js aggregation file in ${OUTPUT_JS_FILE}" diff --git a/src/hoganjs-utils.js b/src/hoganjs-utils.js index 31cad57..84b2016 100644 --- a/src/hoganjs-utils.js +++ b/src/hoganjs-utils.js @@ -12,10 +12,17 @@ var hogan = require('hogan.js'); + var hoganTemplates; + var templatesPath = path.resolve(__dirname, 'templates'); var templatesCache = {}; function HoganJsUtils() { + try { + hoganTemplates = require('./templates/diff2html-templates.js'); + } catch (_ignore) { + hoganTemplates = {}; + } } HoganJsUtils.prototype.render = function(namespace, view, params) { @@ -32,22 +39,29 @@ HoganJsUtils.prototype._getTemplate = function(templateKey) { var template = this._readFromCache(templateKey); - if (!template && fs) { - var templatePath = path.join(templatesPath, templateKey); - var templateContent = fs.readFileSync(templatePath + '.mustache', 'utf8'); - template = hogan.compile(templateContent); - this._addToCache(templateKey, template); + if (!template) { + template = this._loadTemplate(templateKey); } return template; }; - HoganJsUtils.prototype._addToCache = function(templateKey, template) { - templatesCache[templateKey] = template; + HoganJsUtils.prototype._loadTemplate = function(templateKey) { + var template; + if (fs.readFileSync) { + var templatePath = path.join(templatesPath, templateKey); + var templateContent = fs.readFileSync(templatePath + '.mustache', 'utf8'); + template = hogan.compile(templateContent); + templatesCache[templateKey] = template; + } + + return template; }; HoganJsUtils.prototype._readFromCache = function(templateKey) { - return global.browserTemplates && global.browserTemplates[templateKey] || templatesCache[templateKey]; + return global.browserTemplates && global.browserTemplates[templateKey] || + hoganTemplates[templateKey] || + templatesCache[templateKey]; }; HoganJsUtils.prototype._templateKey = function(namespace, view) { diff --git a/src/printer-utils.js b/src/printer-utils.js index b405f14..109228c 100644 --- a/src/printer-utils.js +++ b/src/printer-utils.js @@ -39,8 +39,8 @@ var oldFilename = file.oldName; var newFilename = file.newName; - if (oldFilename && newFilename && oldFilename !== newFilename - && !isDevNullName(oldFilename) && !isDevNullName(newFilename)) { + if (oldFilename && newFilename && oldFilename !== newFilename && + !isDevNullName(oldFilename) && !isDevNullName(newFilename)) { return oldFilename + ' -> ' + newFilename; } else if (newFilename && !isDevNullName(newFilename)) { return newFilename; diff --git a/src/templates/diff2html-templates.js b/src/templates/diff2html-templates.js new file mode 100644 index 0000000..1fcb5ec --- /dev/null +++ b/src/templates/diff2html-templates.js @@ -0,0 +1,9 @@ +(function() { +if (!!!global.browserTemplates) global.browserTemplates = {}; +global.browserTemplates["line-by-line-column-line-number"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("
");t.b(t.t(t.f("blockHeader",c,p,0)));t.b("
");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("");return t.fl(); },partials: {}, subs: { }}); +global.browserTemplates["line-by-line-empty-diff"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b(" File without changes");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("");return t.fl(); },partials: {}, subs: { }}); +global.browserTemplates["line-by-line-file-diff"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("
");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" +");t.b(t.v(t.d("file.addedLines",c,p,0)));t.b("");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" -");t.b(t.v(t.d("file.deletedLines",c,p,0)));t.b("");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("  ");t.b(t.v(t.f("fileDiffName",c,p,0)));t.b("");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("diffs",c,p,0)));t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b("
");t.b("\n" + i);t.b("
");return t.fl(); },partials: {}, subs: { }}); +global.browserTemplates["line-by-line-line"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("
");t.b(t.v(t.f("oldNumber",c,p,0)));t.b("
");t.b("\n" + i);t.b("
");t.b(t.v(t.f("newNumber",c,p,0)));t.b("
");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("
");t.b("\n" + i);if(t.s(t.f("prefix",c,p,1),c,p,0,253,329,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" ");t.b(t.t(t.f("prefix",c,p,0)));t.b("");t.b("\n" + i);});c.pop();}if(t.s(t.f("content",c,p,1),c,p,0,361,435,"{{ }}")){t.rs(c,p,function(c,p,t){t.b(" ");t.b(t.t(t.f("content",c,p,0)));t.b("");t.b("\n" + i);});c.pop();}t.b("
");t.b("\n" + i);t.b(" ");t.b("\n" + i);t.b("");return t.fl(); },partials: {}, subs: { }}); +global.browserTemplates["line-by-line-wrapper"] = new Hogan.Template({code: function (c,p,i) { var t=this;t.b(i=i||"");t.b("
");t.b("\n" + i);t.b(" ");t.b(t.t(t.f("content",c,p,0)));t.b("\n" + i);t.b("
");return t.fl(); },partials: {}, subs: { }}); +module.exports = global.browserTemplates; +})(); diff --git a/src/templates/line-by-line-column-line-number.mustache b/src/templates/line-by-line-column-line-number.mustache index ca6d6cb..72825ed 100644 --- a/src/templates/line-by-line-column-line-number.mustache +++ b/src/templates/line-by-line-column-line-number.mustache @@ -1,6 +1,6 @@ -
{{{blockHeader}}}
+
{{{blockHeader}}}
diff --git a/src/templates/line-by-line-line.mustache b/src/templates/line-by-line-line.mustache index 081c2e2..1893a24 100644 --- a/src/templates/line-by-line-line.mustache +++ b/src/templates/line-by-line-line.mustache @@ -5,12 +5,12 @@
- {{#prefix}} - {{{prefix}}} - {{/prefix}} - {{#content}} - {{{content}}} - {{/content}} + {{#prefix}} + {{{prefix}}} + {{/prefix}} + {{#content}} + {{{content}}} + {{/content}}