From ada2a87e5211fd9de27179971f7c39c928854b95 Mon Sep 17 00:00:00 2001 From: tombye Date: Sun, 16 Feb 2014 20:24:50 +0000 Subject: [PATCH 1/9] Add HoganJS 3.0.0 NPM only holds 2.0.0 so include the core files from this version: https://github.com/twitter/hogan.js/commit/b1328c06eedf316bbe4c54b189ecb47cff7ac2ed --- lib/compiler.js | 419 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/hogan.js | 21 +++ lib/template.js | 345 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 785 insertions(+) create mode 100755 lib/compiler.js create mode 100755 lib/hogan.js create mode 100755 lib/template.js diff --git a/lib/compiler.js b/lib/compiler.js new file mode 100755 index 0000000000..1508827e0e --- /dev/null +++ b/lib/compiler.js @@ -0,0 +1,419 @@ +/* + * 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. + */ + +(function (Hogan) { + // Setup regex assignments + // remove whitespace according to Mustache spec + var rIsWhitespace = /\S/, + rQuot = /\"/g, + rNewline = /\n/g, + rCr = /\r/g, + rSlash = /\\/g; + + Hogan.tags = { + '#': 1, '^': 2, '<': 3, '$': 4, + '/': 5, '!': 6, '>': 7, '=': 8, '_v': 9, + '{': 10, '&': 11, '_t': 12 + }; + + Hogan.scan = function scan(text, delimiters) { + var len = text.length, + IN_TEXT = 0, + IN_TAG_TYPE = 1, + IN_TAG = 2, + state = IN_TEXT, + tagType = null, + tag = null, + buf = '', + tokens = [], + seenTag = false, + i = 0, + lineStart = 0, + otag = '{{', + ctag = '}}'; + + function addBuf() { + if (buf.length > 0) { + tokens.push({tag: '_t', text: new String(buf)}); + buf = ''; + } + } + + function lineIsWhitespace() { + var isAllWhitespace = true; + for (var j = lineStart; j < tokens.length; j++) { + isAllWhitespace = + (Hogan.tags[tokens[j].tag] < Hogan.tags['_v']) || + (tokens[j].tag == '_t' && tokens[j].text.match(rIsWhitespace) === null); + if (!isAllWhitespace) { + return false; + } + } + + return isAllWhitespace; + } + + function filterLine(haveSeenTag, noNewLine) { + addBuf(); + + if (haveSeenTag && lineIsWhitespace()) { + for (var j = lineStart, next; j < tokens.length; j++) { + if (tokens[j].text) { + if ((next = tokens[j+1]) && next.tag == '>') { + // set indent to token value + next.indent = tokens[j].text.toString() + } + tokens.splice(j, 1); + } + } + } else if (!noNewLine) { + tokens.push({tag:'\n'}); + } + + seenTag = false; + lineStart = tokens.length; + } + + function changeDelimiters(text, index) { + var close = '=' + ctag, + closeIndex = text.indexOf(close, index), + delimiters = trim( + text.substring(text.indexOf('=', index) + 1, closeIndex) + ).split(' '); + + otag = delimiters[0]; + ctag = delimiters[delimiters.length - 1]; + + return closeIndex + close.length - 1; + } + + if (delimiters) { + delimiters = delimiters.split(' '); + otag = delimiters[0]; + ctag = delimiters[1]; + } + + for (i = 0; i < len; i++) { + if (state == IN_TEXT) { + if (tagChange(otag, text, i)) { + --i; + addBuf(); + state = IN_TAG_TYPE; + } else { + if (text.charAt(i) == '\n') { + filterLine(seenTag); + } else { + buf += text.charAt(i); + } + } + } else if (state == IN_TAG_TYPE) { + i += otag.length - 1; + tag = Hogan.tags[text.charAt(i + 1)]; + tagType = tag ? text.charAt(i + 1) : '_v'; + if (tagType == '=') { + i = changeDelimiters(text, i); + state = IN_TEXT; + } else { + if (tag) { + i++; + } + state = IN_TAG; + } + seenTag = i; + } else { + if (tagChange(ctag, text, i)) { + tokens.push({tag: tagType, n: trim(buf), otag: otag, ctag: ctag, + i: (tagType == '/') ? seenTag - otag.length : i + ctag.length}); + buf = ''; + i += ctag.length - 1; + state = IN_TEXT; + if (tagType == '{') { + if (ctag == '}}') { + i++; + } else { + cleanTripleStache(tokens[tokens.length - 1]); + } + } + } else { + buf += text.charAt(i); + } + } + } + + filterLine(seenTag, true); + + return tokens; + } + + function cleanTripleStache(token) { + if (token.n.substr(token.n.length - 1) === '}') { + token.n = token.n.substring(0, token.n.length - 1); + } + } + + function trim(s) { + if (s.trim) { + return s.trim(); + } + + return s.replace(/^\s*|\s*$/g, ''); + } + + function tagChange(tag, text, index) { + if (text.charAt(index) != tag.charAt(0)) { + return false; + } + + for (var i = 1, l = tag.length; i < l; i++) { + if (text.charAt(index + i) != tag.charAt(i)) { + return false; + } + } + + return true; + } + + // the tags allowed inside super templates + var allowedInSuper = {'_t': true, '\n': true, '$': true, '/': true}; + + function buildTree(tokens, kind, stack, customTags) { + var instructions = [], + opener = null, + tail = null, + token = null; + + tail = stack[stack.length - 1]; + + while (tokens.length > 0) { + token = tokens.shift(); + + if (tail && tail.tag == '<' && !(token.tag in allowedInSuper)) { + throw new Error('Illegal content in < super tag.'); + } + + if (Hogan.tags[token.tag] <= Hogan.tags['$'] || isOpener(token, customTags)) { + stack.push(token); + token.nodes = buildTree(tokens, token.tag, stack, customTags); + } else if (token.tag == '/') { + if (stack.length === 0) { + throw new Error('Closing tag without opener: /' + token.n); + } + opener = stack.pop(); + if (token.n != opener.n && !isCloser(token.n, opener.n, customTags)) { + throw new Error('Nesting error: ' + opener.n + ' vs. ' + token.n); + } + opener.end = token.i; + return instructions; + } else if (token.tag == '\n') { + token.last = (tokens.length == 0) || (tokens[0].tag == '\n'); + } + + instructions.push(token); + } + + if (stack.length > 0) { + throw new Error('missing closing tag: ' + stack.pop().n); + } + + return instructions; + } + + function isOpener(token, tags) { + for (var i = 0, l = tags.length; i < l; i++) { + if (tags[i].o == token.n) { + token.tag = '#'; + return true; + } + } + } + + function isCloser(close, open, tags) { + for (var i = 0, l = tags.length; i < l; i++) { + if (tags[i].c == close && tags[i].o == open) { + return true; + } + } + } + + function stringifySubstitutions(obj) { + var items = []; + for (var key in obj) { + items.push('"' + esc(key) + '": function(c,p,t,i) {' + obj[key] + '}'); + } + return "{ " + items.join(",") + " }"; + } + + function stringifyPartials(codeObj) { + var partials = []; + for (var key in codeObj.partials) { + partials.push('"' + esc(key) + '":{name:"' + esc(codeObj.partials[key].name) + '", ' + stringifyPartials(codeObj.partials[key]) + "}"); + } + return "partials: {" + partials.join(",") + "}, subs: " + stringifySubstitutions(codeObj.subs); + } + + Hogan.stringify = function(codeObj, text, options) { + return "{code: function (c,p,i) { " + Hogan.wrapMain(codeObj.code) + " }," + stringifyPartials(codeObj) + "}"; + } + + var serialNo = 0; + Hogan.generate = function(tree, text, options) { + serialNo = 0; + var context = { code: '', subs: {}, partials: {} }; + Hogan.walk(tree, context); + + if (options.asString) { + return this.stringify(context, text, options); + } + + return this.makeTemplate(context, text, options); + } + + Hogan.wrapMain = function(code) { + return 'var t=this;t.b(i=i||"");' + code + 'return t.fl();'; + } + + Hogan.template = Hogan.Template; + + Hogan.makeTemplate = function(codeObj, text, options) { + var template = this.makePartials(codeObj); + template.code = new Function('c', 'p', 'i', this.wrapMain(codeObj.code)); + return new this.template(template, text, this, options); + } + + Hogan.makePartials = function(codeObj) { + var key, template = {subs: {}, partials: codeObj.partials, name: codeObj.name}; + for (key in template.partials) { + template.partials[key] = this.makePartials(template.partials[key]); + } + for (key in codeObj.subs) { + template.subs[key] = new Function('c', 'p', 't', 'i', codeObj.subs[key]); + } + return template; + } + + function esc(s) { + return s.replace(rSlash, '\\\\') + .replace(rQuot, '\\\"') + .replace(rNewline, '\\n') + .replace(rCr, '\\r'); + } + + function chooseMethod(s) { + return (~s.indexOf('.')) ? 'd' : 'f'; + } + + function createPartial(node, context) { + var prefix = "<" + (context.prefix || ""); + var sym = prefix + node.n + serialNo++; + context.partials[sym] = {name: node.n, partials: {}}; + context.code += 't.b(t.rp("' + esc(sym) + '",c,p,"' + (node.indent || '') + '"));'; + return sym; + } + + Hogan.codegen = { + '#': function(node, context) { + context.code += 'if(t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),' + + 'c,p,0,' + node.i + ',' + node.end + ',"' + node.otag + " " + node.ctag + '")){' + + 't.rs(c,p,' + 'function(c,p,t){'; + Hogan.walk(node.nodes, context); + context.code += '});c.pop();}'; + }, + + '^': function(node, context) { + context.code += 'if(!t.s(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,1),c,p,1,0,0,"")){'; + Hogan.walk(node.nodes, context); + context.code += '};'; + }, + + '>': createPartial, + '<': function(node, context) { + var ctx = {partials: {}, code: '', subs: {}, inPartial: true}; + Hogan.walk(node.nodes, ctx); + var template = context.partials[createPartial(node, context)]; + template.subs = ctx.subs; + template.partials = ctx.partials; + }, + + '$': function(node, context) { + var ctx = {subs: {}, code: '', partials: context.partials, prefix: node.n}; + Hogan.walk(node.nodes, ctx); + context.subs[node.n] = ctx.code; + if (!context.inPartial) { + context.code += 't.sub("' + esc(node.n) + '",c,p,i);'; + } + }, + + '\n': function(node, context) { + context.code += write('"\\n"' + (node.last ? '' : ' + i')); + }, + + '_v': function(node, context) { + context.code += 't.b(t.v(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));'; + }, + + '_t': function(node, context) { + context.code += write('"' + esc(node.text) + '"'); + }, + + '{': tripleStache, + + '&': tripleStache + } + + function tripleStache(node, context) { + context.code += 't.b(t.t(t.' + chooseMethod(node.n) + '("' + esc(node.n) + '",c,p,0)));'; + } + + function write(s) { + return 't.b(' + s + ');'; + } + + Hogan.walk = function(nodelist, context) { + var func; + for (var i = 0, l = nodelist.length; i < l; i++) { + func = Hogan.codegen[nodelist[i].tag]; + func && func(nodelist[i], context); + } + return context; + } + + Hogan.parse = function(tokens, text, options) { + options = options || {}; + return buildTree(tokens, '', [], options.sectionTags || []); + } + + Hogan.cache = {}; + + Hogan.cacheKey = function(text, options) { + return [text, !!options.asString, !!options.disableLambda, options.delimiters, !!options.modelGet].join('||'); + } + + Hogan.compile = function(text, options) { + options = options || {}; + var key = Hogan.cacheKey(text, options); + var template = this.cache[key]; + + if (template) { + var partials = template.partials; + for (var name in partials) { + delete partials[name].instance; + } + return template; + } + + template = this.generate(this.parse(this.scan(text, options.delimiters), text, options), text, options); + return this.cache[key] = template; + } +})(typeof exports !== 'undefined' ? exports : Hogan); diff --git a/lib/hogan.js b/lib/hogan.js new file mode 100755 index 0000000000..acc6709df1 --- /dev/null +++ b/lib/hogan.js @@ -0,0 +1,21 @@ +/* + * 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. + */ + +// This file is for use with Node.js. See dist/ for browser files. + +var Hogan = require('./compiler'); +Hogan.Template = require('./template').Template; +Hogan.template = Hogan.Template; +module.exports = Hogan; diff --git a/lib/template.js b/lib/template.js new file mode 100755 index 0000000000..5d2bb913ac --- /dev/null +++ b/lib/template.js @@ -0,0 +1,345 @@ +/* + * 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. + */ + +var Hogan = {}; + +(function (Hogan, useArrayBuffer) { + Hogan.Template = function (codeObj, text, compiler, options) { + codeObj = codeObj || {}; + this.r = codeObj.code || this.r; + this.c = compiler; + this.options = options || {}; + this.text = text || ''; + this.partials = codeObj.partials || {}; + this.subs = codeObj.subs || {}; + this.ib(); + } + + Hogan.Template.prototype = { + // render: replaced by generated code. + r: function (context, partials, indent) { return ''; }, + + // variable escaping + v: hoganEscape, + + // triple stache + t: coerceToString, + + render: function render(context, partials, indent) { + return this.ri([context], partials || {}, indent); + }, + + // render internal -- a hook for overrides that catches partials too + ri: function (context, partials, indent) { + return this.r(context, partials, indent); + }, + + // ensurePartial + ep: function(symbol, partials) { + var partial = this.partials[symbol]; + + // check to see that if we've instantiated this partial before + var template = partials[partial.name]; + if (partial.instance && partial.base == template) { + return partial.instance; + } + + if (typeof template == 'string') { + if (!this.c) { + throw new Error("No compiler available."); + } + template = this.c.compile(template, this.options); + } + + if (!template) { + return null; + } + + // We use this to check whether the partials dictionary has changed + this.partials[symbol].base = template; + + if (partial.subs) { + // Make sure we consider parent template now + if (this.activeSub === undefined) { + // Store parent template text in partials.stackText to perform substitutions in child templates correctly + partials.stackText = this.text; + } + template = createSpecializedPartial(template, partial.subs, partial.partials, + this.stackSubs, this.stackPartials, partials.stackText || this.text); + } + this.partials[symbol].instance = template; + + return template; + }, + + // tries to find a partial in the current scope and render it + rp: function(symbol, context, partials, indent) { + var partial = this.ep(symbol, partials); + if (!partial) { + return ''; + } + + return partial.ri(context, partials, indent); + }, + + // render a section + rs: function(context, partials, section) { + var tail = context[context.length - 1]; + + if (!isArray(tail)) { + section(context, partials, this); + return; + } + + for (var i = 0; i < tail.length; i++) { + context.push(tail[i]); + section(context, partials, this); + context.pop(); + } + }, + + // maybe start a section + s: function(val, ctx, partials, inverted, start, end, tags) { + var pass; + + if (isArray(val) && val.length === 0) { + return false; + } + + if (typeof val == 'function') { + val = this.ms(val, ctx, partials, inverted, start, end, tags); + } + + pass = !!val; + + if (!inverted && pass && ctx) { + ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]); + } + + return pass; + }, + + // find values with dotted names + d: function(key, ctx, partials, returnFound) { + var found, + names = key.split('.'), + val = this.f(names[0], ctx, partials, returnFound), + doModelGet = this.options.modelGet, + cx = null; + + if (key === '.' && isArray(ctx[ctx.length - 2])) { + val = ctx[ctx.length - 1]; + } else { + for (var i = 1; i < names.length; i++) { + found = findInScope(names[i], val, doModelGet); + if (found != null) { + cx = val; + val = found; + } else { + val = ''; + } + } + } + + if (returnFound && !val) { + return false; + } + + if (!returnFound && typeof val == 'function') { + ctx.push(cx); + val = this.mv(val, ctx, partials); + ctx.pop(); + } + + return val; + }, + + // find values with normal names + f: function(key, ctx, partials, returnFound) { + var val = false, + v = null, + found = false, + doModelGet = this.options.modelGet; + + for (var i = ctx.length - 1; i >= 0; i--) { + v = ctx[i]; + val = findInScope(key, v, doModelGet); + if (val != null) { + found = true; + break; + } + } + + if (!found) { + return (returnFound) ? false : ""; + } + + if (!returnFound && typeof val == 'function') { + val = this.mv(val, ctx, partials); + } + + return val; + }, + + // higher order templates + ls: function(func, cx, partials, text, tags) { + var oldTags = this.options.delimiters; + + this.options.delimiters = tags; + this.b(this.ct(coerceToString(func.call(cx, text)), cx, partials)); + this.options.delimiters = oldTags; + + return false; + }, + + // compile text + ct: function(text, cx, partials) { + if (this.options.disableLambda) { + throw new Error('Lambda features disabled.'); + } + return this.c.compile(text, this.options).render(cx, partials); + }, + + // template result buffering + b: (useArrayBuffer) ? function(s) { this.buf.push(s); } : + function(s) { this.buf += s; }, + + fl: (useArrayBuffer) ? function() { var r = this.buf.join(''); this.buf = []; return r; } : + function() { var r = this.buf; this.buf = ''; return r; }, + // init the buffer + ib: function () { + this.buf = (useArrayBuffer) ? [] : ''; + }, + + // method replace section + ms: function(func, ctx, partials, inverted, start, end, tags) { + var textSource, + cx = ctx[ctx.length - 1], + result = func.call(cx); + + if (typeof result == 'function') { + if (inverted) { + return true; + } else { + textSource = (this.activeSub && this.subsText[this.activeSub]) ? this.subsText[this.activeSub] : this.text; + return this.ls(result, cx, partials, textSource.substring(start, end), tags); + } + } + + return result; + }, + + // method replace variable + mv: function(func, ctx, partials) { + var cx = ctx[ctx.length - 1]; + var result = func.call(cx); + + if (typeof result == 'function') { + return this.ct(coerceToString(result.call(cx)), cx, partials); + } + + return result; + }, + + sub: function(name, context, partials, indent) { + var f = this.subs[name]; + if (f) { + this.activeSub = name; + f(context, partials, this, indent); + this.activeSub = false; + } + } + + }; + + //Find a key in an object + function findInScope(key, scope, doModelGet) { + var val, checkVal; + + if (scope && typeof scope == 'object') { + + if (scope[key] != null) { + val = scope[key]; + + // try lookup with get for backbone or similar model data + } else if (doModelGet && scope.get && typeof scope.get == 'function') { + val = scope.get(key); + } + } + + return val; + } + + function createSpecializedPartial(instance, subs, partials, stackSubs, stackPartials, childText) { + function PartialTemplate() {}; + PartialTemplate.prototype = instance; + function Substitutions() {}; + Substitutions.prototype = instance.subs; + var key; + var partial = new PartialTemplate(); + partial.subs = new Substitutions(); + partial.subsText = {}; //hehe. substext. + partial.ib(); + + stackSubs = stackSubs || {}; + partial.stackSubs = stackSubs; + for (key in subs) { + if (!stackSubs[key]) stackSubs[key] = subs[key]; + partial.subsText[key] = childText; + } + for (key in stackSubs) { + partial.subs[key] = stackSubs[key]; + } + + stackPartials = stackPartials || {}; + partial.stackPartials = stackPartials; + for (key in partials) { + if (!stackPartials[key]) stackPartials[key] = partials[key]; + } + for (key in stackPartials) { + partial.partials[key] = stackPartials[key]; + } + + return partial; + } + + var rAmp = /&/g, + rLt = //g, + rApos = /\'/g, + rQuot = /\"/g, + hChars = /[&<>\"\']/; + + function coerceToString(val) { + return String((val === null || val === undefined) ? '' : val); + } + + function hoganEscape(str) { + str = coerceToString(str); + return hChars.test(str) ? + str + .replace(rAmp, '&') + .replace(rLt, '<') + .replace(rGt, '>') + .replace(rApos, ''') + .replace(rQuot, '"') : + str; + } + + var isArray = Array.isArray || function(a) { + return Object.prototype.toString.call(a) === '[object Array]'; + }; + +})(typeof exports !== 'undefined' ? exports : Hogan); From 334af39726e2d79a33d36cfc4e8c3b877981d341 Mon Sep 17 00:00:00 2001 From: tombye Date: Mon, 17 Feb 2014 20:05:01 +0000 Subject: [PATCH 2/9] Add readdir package --- node_modules/readdir/.npmignore | 5 + node_modules/readdir/README.md | 62 ++++++ node_modules/readdir/lib/readdir.js | 202 ++++++++++++++++++ node_modules/readdir/package.json | 44 ++++ .../readdir/test/case_sensitive_dir/Abc.xsl | 0 .../readdir/test/case_sensitive_dir/aBC.xml | 0 .../readdir/test/example_dir/AAA/aaa.js | 0 .../readdir/test/example_dir/AAA/aaa.txt | 0 .../readdir/test/example_dir/BBB/bbb.js | 0 .../readdir/test/example_dir/BBB/bbb.txt | 0 .../readdir/test/example_dir/CCC/DDD/ddd.js | 0 .../readdir/test/example_dir/CCC/DDD/ddd.txt | 0 .../readdir/test/example_dir/CCC/ccc.js | 0 .../readdir/test/example_dir/CCC/ccc.txt | 0 node_modules/readdir/test/example_dir/abc.js | 0 node_modules/readdir/test/example_dir/abc.txt | 0 node_modules/readdir/test/test.js | 108 ++++++++++ 17 files changed, 421 insertions(+) create mode 100644 node_modules/readdir/.npmignore create mode 100644 node_modules/readdir/README.md create mode 100644 node_modules/readdir/lib/readdir.js create mode 100644 node_modules/readdir/package.json create mode 100644 node_modules/readdir/test/case_sensitive_dir/Abc.xsl create mode 100644 node_modules/readdir/test/case_sensitive_dir/aBC.xml create mode 100644 node_modules/readdir/test/example_dir/AAA/aaa.js create mode 100644 node_modules/readdir/test/example_dir/AAA/aaa.txt create mode 100644 node_modules/readdir/test/example_dir/BBB/bbb.js create mode 100644 node_modules/readdir/test/example_dir/BBB/bbb.txt create mode 100644 node_modules/readdir/test/example_dir/CCC/DDD/ddd.js create mode 100644 node_modules/readdir/test/example_dir/CCC/DDD/ddd.txt create mode 100644 node_modules/readdir/test/example_dir/CCC/ccc.js create mode 100644 node_modules/readdir/test/example_dir/CCC/ccc.txt create mode 100644 node_modules/readdir/test/example_dir/abc.js create mode 100644 node_modules/readdir/test/example_dir/abc.txt create mode 100644 node_modules/readdir/test/test.js diff --git a/node_modules/readdir/.npmignore b/node_modules/readdir/.npmignore new file mode 100644 index 0000000000..3b3ec52ff7 --- /dev/null +++ b/node_modules/readdir/.npmignore @@ -0,0 +1,5 @@ +.idea* +.git* +.DS_Store +Thumbs.db +node_modules diff --git a/node_modules/readdir/README.md b/node_modules/readdir/README.md new file mode 100644 index 0000000000..5ac7fe28ce --- /dev/null +++ b/node_modules/readdir/README.md @@ -0,0 +1,62 @@ +readdir.js +=========== + +A Node.js utility module to read the contents of a directory with support for Ant style filtering to easily order the results - particularly useful for any order specific build system. + + +Node.js +======= + +Install using npm `npm install readdir` then use with require: + + var directoryContents = require('readdir').readSync('some_path/'); + + +Usage +===== + +The `readSync( path, [filters, [options]])` method can accept a path that is either absolute or relative to the current working directory. + +Filters can be supplied as an array of strings that are Ant style expressions that any file found in the `path` directory must satisfy, use a `*` to signify any file in the current directory or `**` to signify any file in the current or any sub directory. If the filter contains a `/` then the file path must also include a path, so `*/*` would mean any file of a directory that is a direct sub-directory of the path directory and `*/**` would be any file that is in any subdirectory of the path directory. + +To select just one type of file, add a file extension suffix to the filter (eg: `**.js` to pick any JavaScript file in the path directory). + +If the options argument is supplied, it should be a number representing the sum of any options you want to apply: + +`ABSOLUTE_PATHS` changes the return value so that each item in the array is an absolute path instead of relative to the path directory. + +`CASE_SORT` sort the return array as a case sensitive sort + +`CASELESS_SORT` sort the return array as a case insensitive sort + +Examples +======== + +With filters: + + var readDir = require('readdir'); + + // an array of all JavaScript files in some_path/ + readDir.readSync( 'some_path/', ['**.js'] ); + +With ordering of results using filters: + + var readDir = require('readdir'); + + // an array of all JavaScript files in some_path/ with base.js first, then all core then anything else + readDir.readSync( 'some_path/', ['base.js', 'core/**.js', '**.js'] ); + +With options + + var readDir = require('readdir'); + + // an array of all files in some_path/ as absolute file paths + readDir.readSync( 'some_path/', null, readDir.ABSOLUTE_PATHS ); + + // an array of all files in some_path/ as absolute file paths sorted without case + readDir.readSync( 'some_path/', null, readDir.ABSOLUTE_PATHS + readDir.CASELESS_SORT ); + + + + + diff --git a/node_modules/readdir/lib/readdir.js b/node_modules/readdir/lib/readdir.js new file mode 100644 index 0000000000..20a70d7b4a --- /dev/null +++ b/node_modules/readdir/lib/readdir.js @@ -0,0 +1,202 @@ + +(function(exports) { + + "use strict"; + + var fs = require('fs'); + + /** + * For the supplied paths list, matches against the supplied filters and returns a new array of paths that + * are ordered as the list of filters would imply they should be. The filters can include * as a match-anything in + * one directory or ** for match any file in any directory. All filters are treated as an ends-with match. + * + * @param {String[]} paths + * @param {String[]} filters + * @return String[] + */ + function file_list_filter(paths, filters) { + var result = []; + filters.forEach(function(filter) { + var filterRegex = new RegExp('^' + + filter.replace(/\./g, '\\.') + .replace(/(\*?)(\*)(?!\*)/g, function(match, prefix) { + if(prefix == '*') { + return match; + } + return '[^\\/]*'; + }) + .replace(/\*\*/g, '\.*') + '$' + , 'i'); + + paths.forEach(function(path) { + if(result.indexOf(path) < 0 && path.match(filterRegex)) { + result.push(path); + } + }); + + }); + return result; + } + + /** + * Gets a flag that identifies whether the supplied path is a directory or a file, true when a directory. In the + * case that the file doesn't exist the result will be false. + * + * @param path + * @return {Boolean} + */ + function is_dir(path) { + try { + return fs.statSync(path).isDirectory(); + } + catch (e) { + return false; + } + } + + /** + * Given the name of the directory about to be traversed, checks whether it should be - allows for the automatic + * removal of "hidden" directories. + * + * @param {String} base + * @param {String} directoryName + * @param {Number} options + * @return {Boolean} + */ + function should_read_directory(base, directoryName, options) { + return !!(directoryName.charAt(0) != '.' || (exports.INCLUDE_HIDDEN & options)); + } + + /** + * Reads the supplied directory path and builds an array of files within the directory. This will work recursively + * on each sub directory found. The optional appendTo argument can be used to merge file paths onto an existing + * array, and is used internally for recursion. + * + * @param {String} dir + * @param {String[]} appendTo + * @param {Number} prefixLength + * @param {Number} options + */ + function read_dir(dir, appendTo, prefixLength, options) { + var contents = fs.readdirSync(dir), + result = appendTo || []; + + contents.forEach(function(itm) { + var newPath = dir + itm; + if(is_dir(newPath)) { + if(should_read_directory(dir, itm, options)) { + read_dir(newPath + '/', result, prefixLength, options); + if(exports.INCLUDE_DIRECTORIES & options) { + result.push(newPath.substring(prefixLength) + '/'); + } + } + } + else { + if(result.indexOf(newPath) < 0) { + result.push(newPath.substring(prefixLength)); + } + } + }); + + return result; + } + + /** + * Changes the values in the supplied paths array to be absolute URIs + * + * @param {String} prefix + * @param {String[]} paths + */ + function prepend_paths(prefix, paths) { + paths.forEach(function(path, index) { + paths[index] = prefix + path; + }); + } + + function sort_paths(paths, sorter) { + return paths.sort(sorter); + } + + function caseless_sort(pathA, pathB) { + var a = ('' + pathA).toLowerCase(), + b = ('' + pathB).toLowerCase(); + + if(a == b) { + return 0; + } + else { + return a > b ? 1 : -1; + } + } + + function case_sort(pathA, pathB) { + if(pathA == pathB) { + return 0; + } + else { + return pathA > pathB ? 1 : -1; + } + } + + /** + * + * @param {String} basePath + * @param {String[]} [includeFilters] + * @param {Number} [options] + */ + exports.readSync = function(basePath, includeFilters, options) { + var rootDir = basePath.replace(/\/$/, '') + '/', + allFiles = read_dir(rootDir, [], rootDir.length, options); + + if(Array.isArray(includeFilters)) { + allFiles = file_list_filter(allFiles, includeFilters); + } + + if(exports.ABSOLUTE_PATHS & options) { + prepend_paths(require('path').resolve(process.cwd(), basePath) + '/', allFiles); + } + + if(exports.CASELESS_SORT & options) { + allFiles = sort_paths(allFiles, caseless_sort); + } + + if(exports.CASE_SORT & options) { + allFiles = sort_paths(allFiles, case_sort); + } + + return allFiles; + }; + + exports.isDir = is_dir; + + /** + * Bitwise option for making the return paths absolute URIs instead of being from the supplied base path + * @type {Number} + */ + exports.ABSOLUTE_PATHS = 1; + + /** + * Bitwise option for making the return array sorted case insensitively + * @type {Number} + */ + exports.CASELESS_SORT = 2; + + /** + * Bitwise option for making the return array sorted case sensitively + * @type {Number} + */ + exports.CASE_SORT = 4; + + /** + * Bitwise option for making the return array sorted case sensitively + * @type {Number} + */ + exports.INCLUDE_DIRECTORIES = 8; + + /** + * Bitwise option for prevent the automatic removal of paths that start with a dot + * @type {Number} + */ + exports.INCLUDE_HIDDEN = 16; + +}(typeof module == 'undefined' ? (window.ReadDir = {}) : module.exports)); diff --git a/node_modules/readdir/package.json b/node_modules/readdir/package.json new file mode 100644 index 0000000000..3fa925d5cd --- /dev/null +++ b/node_modules/readdir/package.json @@ -0,0 +1,44 @@ +{ + "name": "readdir", + "description": "Reads a directory and return results with the ability to use Ant style file match patterns ", + "version": "0.0.6", + "author": { + "name": "Steve King", + "email": "steve@mydev.co" + }, + "contributors": [ + { + "name": "Steve King", + "email": "steve@mydev.co" + } + ], + "dependencies": {}, + "devDependencies": {}, + "keywords": [ + "directory", + "fs" + ], + "repository": { + "type": "git", + "url": "git://github.com/steveukx/readdir.js" + }, + "main": "lib/readdir", + "bin": {}, + "scripts": {}, + "config": {}, + "engines": { + "node": ">= 0.4.1" + }, + "readme": "readdir.js\n===========\n\nA Node.js utility module to read the contents of a directory with support for Ant style filtering to easily order the results - particularly useful for any order specific build system.\n\n\nNode.js\n=======\n\nInstall using npm `npm install readdir` then use with require:\n\n var directoryContents = require('readdir').readSync('some_path/');\n\n\nUsage\n=====\n\nThe `readSync( path, [filters, [options]])` method can accept a path that is either absolute or relative to the current working directory.\n\nFilters can be supplied as an array of strings that are Ant style expressions that any file found in the `path` directory must satisfy, use a `*` to signify any file in the current directory or `**` to signify any file in the current or any sub directory. If the filter contains a `/` then the file path must also include a path, so `*/*` would mean any file of a directory that is a direct sub-directory of the path directory and `*/**` would be any file that is in any subdirectory of the path directory.\n\nTo select just one type of file, add a file extension suffix to the filter (eg: `**.js` to pick any JavaScript file in the path directory).\n\nIf the options argument is supplied, it should be a number representing the sum of any options you want to apply:\n\n`ABSOLUTE_PATHS` changes the return value so that each item in the array is an absolute path instead of relative to the path directory.\n\n`CASE_SORT` sort the return array as a case sensitive sort\n\n`CASELESS_SORT` sort the return array as a case insensitive sort\n\nExamples\n========\n\nWith filters:\n\n var readDir = require('readdir');\n \n // an array of all JavaScript files in some_path/\n readDir.readSync( 'some_path/', ['**.js'] );\n\nWith ordering of results using filters:\n\n var readDir = require('readdir');\n \n // an array of all JavaScript files in some_path/ with base.js first, then all core then anything else\n readDir.readSync( 'some_path/', ['base.js', 'core/**.js', '**.js'] );\n\nWith options\n\n var readDir = require('readdir');\n \n // an array of all files in some_path/ as absolute file paths\n readDir.readSync( 'some_path/', null, readDir.ABSOLUTE_PATHS );\n\n // an array of all files in some_path/ as absolute file paths sorted without case\n readDir.readSync( 'some_path/', null, readDir.ABSOLUTE_PATHS + readDir.CASELESS_SORT );\n\n\n\n\n\n", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/steveukx/readdir.js/issues" + }, + "homepage": "https://github.com/steveukx/readdir.js", + "_id": "readdir@0.0.6", + "dist": { + "shasum": "7a24b532f730ccddbf017002de8ec2ccafa211b7" + }, + "_from": "readdir@0.0.6", + "_resolved": "https://registry.npmjs.org/readdir/-/readdir-0.0.6.tgz" +} diff --git a/node_modules/readdir/test/case_sensitive_dir/Abc.xsl b/node_modules/readdir/test/case_sensitive_dir/Abc.xsl new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/case_sensitive_dir/aBC.xml b/node_modules/readdir/test/case_sensitive_dir/aBC.xml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/AAA/aaa.js b/node_modules/readdir/test/example_dir/AAA/aaa.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/AAA/aaa.txt b/node_modules/readdir/test/example_dir/AAA/aaa.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/BBB/bbb.js b/node_modules/readdir/test/example_dir/BBB/bbb.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/BBB/bbb.txt b/node_modules/readdir/test/example_dir/BBB/bbb.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/CCC/DDD/ddd.js b/node_modules/readdir/test/example_dir/CCC/DDD/ddd.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/CCC/DDD/ddd.txt b/node_modules/readdir/test/example_dir/CCC/DDD/ddd.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/CCC/ccc.js b/node_modules/readdir/test/example_dir/CCC/ccc.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/CCC/ccc.txt b/node_modules/readdir/test/example_dir/CCC/ccc.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/abc.js b/node_modules/readdir/test/example_dir/abc.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/example_dir/abc.txt b/node_modules/readdir/test/example_dir/abc.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/node_modules/readdir/test/test.js b/node_modules/readdir/test/test.js new file mode 100644 index 0000000000..84c7154bcb --- /dev/null +++ b/node_modules/readdir/test/test.js @@ -0,0 +1,108 @@ + +var Assert = require('assert'); + +(function() { + var readSync = require('../lib/readdir.js').readSync; + + var everyFile = readSync('example_dir'); + Assert.deepEqual(everyFile, [ 'AAA/aaa.js', + 'AAA/aaa.txt', + 'abc.js', + 'abc.txt', + 'BBB/bbb.js', + 'BBB/bbb.txt', + 'CCC/ccc.js', + 'CCC/ccc.txt', + 'CCC/DDD/ddd.js', + 'CCC/DDD/ddd.txt' ], 'Not supplying a filter selects every file'); +}()); + +(function() { + var readSync = require('../lib/readdir.js').readSync; + + var everyFile = readSync('example_dir', ['**/**.js']); + Assert.deepEqual(everyFile, ['AAA/aaa.js', + 'BBB/bbb.js', + 'CCC/ccc.js', + 'CCC/DDD/ddd.js' ], 'double star slash requires sub-directory, js suffix requires file type'); +}()); + +(function() { + var readSync = require('../lib/readdir.js').readSync; + + var everyFile = readSync('example_dir', ['**.js']); + Assert.deepEqual(everyFile, [ 'AAA/aaa.js', + 'abc.js', + 'BBB/bbb.js', + 'CCC/ccc.js', + 'CCC/DDD/ddd.js' ], 'double star can have any number of sub-directories and a suffix requires file type'); +}()); + +(function() { + var readSync = require('../lib/readdir.js').readSync; + + var everyFile = readSync('example_dir', ['BBB/*']); + Assert.deepEqual(everyFile, ['BBB/bbb.js', + 'BBB/bbb.txt' ], 'path prefix requires that directory, trailing star allows any file type'); +}()); + +(function() { + var readSync = require('../lib/readdir.js').readSync; + + var everyFile = readSync(process.cwd() + '/example_dir/', ['CCC/*']); + Assert.deepEqual(everyFile, ['CCC/ccc.js', + 'CCC/ccc.txt'], 'Path prefix requires that directory, trailing single star ignores subsequent sub-directories'); +}()); + +(function() { + var readSync = require('../lib/readdir.js').readSync; + + var everyFile = readSync('../test/example_dir', ['CCC/**'], require('../lib/readdir.js').ABSOLUTE_PATHS), + cwd = process.cwd(); + + Assert.deepEqual(everyFile, [cwd + '/example_dir/CCC/ccc.js', + cwd + '/example_dir/CCC/ccc.txt', + cwd + '/example_dir/CCC/DDD/ddd.js', + cwd + '/example_dir/CCC/DDD/ddd.txt'], 'Double star will include sub-directories'); +}()); + +(function() { + var readSync = require('../lib/readdir.js').readSync; + + var everyFile = readSync('./example_dir', ['*.txt']); + Assert.deepEqual(everyFile, ['abc.txt'], 'Single star ignores sub-directories and filename is a suffix'); +}()); + +(function() { + var readSync = require('../lib/readdir.js').readSync, + everyFile; + + everyFile = readSync('./case_sensitive_dir', null, require('../lib/readdir.js').CASELESS_SORT); + Assert.deepEqual(everyFile, ['aBC.xml', 'Abc.xsl']); + + everyFile = readSync('./case_sensitive_dir', null, require('../lib/readdir.js').CASE_SORT); + Assert.deepEqual(everyFile, ['Abc.xsl', 'aBC.xml']); +}()); + +(function() { + var readSync = require('../lib/readdir.js').readSync, + everyFile; + + everyFile = readSync('./example_dir', null, require('../lib/readdir.js').INCLUDE_DIRECTORIES); + console.log(everyFile); + + Assert.deepEqual(everyFile, [ 'AAA/aaa.js', + 'AAA/aaa.txt', + 'AAA/', + 'abc.js', + 'abc.txt', + 'BBB/bbb.js', + 'BBB/bbb.txt', + 'BBB/', + 'CCC/ccc.js', + 'CCC/ccc.txt', + 'CCC/DDD/ddd.js', + 'CCC/DDD/ddd.txt', + 'CCC/DDD/', + 'CCC/' ], 'Not supplying a filter selects every file, directories are listed after their contents'); +}()); \ No newline at end of file From 2d3f80552f50bcf068c58b71ef96f21145ab89b5 Mon Sep 17 00:00:00 2001 From: tombye Date: Mon, 17 Feb 2014 20:50:27 +0000 Subject: [PATCH 3/9] Add Hogan-middleware Contains following changes to NPM version: * all file watching removed * compiles templates from additional views folder --- app.js | 11 ++----- lib/template-engine.js | 75 ++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 3 files changed, 78 insertions(+), 10 deletions(-) create mode 100755 lib/template-engine.js diff --git a/app.js b/app.js index 21cd632df0..94055bbbd4 100644 --- a/app.js +++ b/app.js @@ -1,24 +1,17 @@ var express = require('express'), - cons = require('consolidate'), app = express(), - mustacheRender = require("./lib/mustacheRender").mustacheRender, port = (process.env.PORT || 3000); // Application settings -app.engine('html', cons.mustache); +app.engine('html', require(__dirname + '/lib/template-engine.js').__express); app.set('view engine', 'html'); +app.set('vendorViews', __dirname + '/govuk/views'); app.set('views', __dirname + '/views'); // Middleware to serve static assets app.use('/public', express.static(__dirname + '/public')); app.use('/public', express.static(__dirname + '/govuk/public')); -// middleware to wrap mustache views in govuk template - -app.use(mustacheRender); - -// - var commonHead = ''; // routes diff --git a/lib/template-engine.js b/lib/template-engine.js new file mode 100755 index 0000000000..4e1b08046c --- /dev/null +++ b/lib/template-engine.js @@ -0,0 +1,75 @@ + +var Hogan = require(__dirname + '/hogan.js'); +var ReadDir = require('readdir'); +var Path = require('path'); +var FS = require('fs'); + +function TemplateEngine() { +} + +/** + * All active directory file system watches + * @type {fs.FSWatcher[]} + * @ignore + */ +TemplateEngine._watches = []; + +/** + * Called by the express server to get the content for a given template at the templatePath supplied. The templateData + * can contain any content from a configured route, and will be made available to the templates. + * + * Templates can include partials by name for any template also in the views directory, note that if sub-directories are + * used to create included partials, express will not necessarily recognise that file as a valid view path... you've been + * warned. + * + * @param {String} templatePath Path to the template + * @param {Object} templateData Data to give to the template + * @param {Function} next Callback to receive two arguments, an error object and the template result. + */ +TemplateEngine.__express = function(templatePath, templateData, next) { + var templateName = Path.basename(templatePath, Path.extname(templatePath)); + var templates = TemplateEngine._getTemplates([templateData.settings.views, templateData.settings.vendorViews]); + var output = null, error = null; + + try { + output = templates[templateName].render(templateData, templates); + } + catch (e) { + error = e; + } + finally { + next(error, output); + } +}; + +/** + * Stores an individual template based on the supplied path, the name of the template is the file's basename without + * the extension. + * + * @param {String} templatePath + */ +TemplateEngine._storeTemplate = function(templatePath) { + var templateName = Path.basename(templatePath, Path.extname(templatePath)); + TemplateEngine.__templates[templateName] = Hogan.compile(FS.readFileSync(templatePath, 'utf-8')); + + console.log('Stored template', templateName); +}; + +/** + * Gets all templates, when the template path hasn't yet been scanned it will be read synchronously to ensure there are + * always templates available. + * + * @param {Array} templatePaths + */ +TemplateEngine._getTemplates = function(templatePaths) { + if(!TemplateEngine.__templates) { + TemplateEngine.__templates = {}; + for (var i = 0, j = templatePaths.length; i < j; i++) { + ReadDir.readSync(templatePaths[i], ['**.html'], ReadDir.ABSOLUTE_PATHS) + .forEach(TemplateEngine._storeTemplate, TemplateEngine); + } + } + return TemplateEngine.__templates; +}; + +module.exports = TemplateEngine; diff --git a/package.json b/package.json index 58a81dfec7..c24508be6b 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "dependencies": { "express": "3.4.7", "consolidate": "0.x", - "mustache": "0.x", + "readdir": "0.0.6", "node-sass": "0.x", "govuk_frontend_toolkit": "0.41.0", "govuk_template_mustache": "0.5.1", From c479e6f68b26162da883fc3d49b036de80656386 Mon Sep 17 00:00:00 2001 From: tombye Date: Mon, 17 Feb 2014 21:15:23 +0000 Subject: [PATCH 4/9] Add Grunt task to convert template Converts to proposed version of mustache that supports inheritance. --- Gruntfile.js | 12 ++++++++++++ govuk/config.js | 15 +++++++++++++++ lib/template-conversion.js | 22 ++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 govuk/config.js create mode 100644 lib/template-conversion.js diff --git a/Gruntfile.js b/Gruntfile.js index a6ee2032d7..eb90da2daa 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -92,10 +92,22 @@ module.exports = function(grunt){ ].forEach(function (task) { grunt.loadNpmTasks(task); }); + + grunt.registerTask( + 'convert_template', + 'Converts the govuk_template to use mustache inheritance', + function () { + var script = require(__dirname + '/lib/template-conversion.js'); + + script.convert(); + grunt.log.writeln('govuk_template converted'); + } + ); grunt.registerTask('default', [ 'copy:govuk_template', 'copy:govuk_assets', + 'convert_template', 'copy:govuk_frontend_toolkit', 'replace', 'sass', diff --git a/govuk/config.js b/govuk/config.js new file mode 100644 index 0000000000..73605f51c9 --- /dev/null +++ b/govuk/config.js @@ -0,0 +1,15 @@ +module.exports = { + assetPath: "{{assetPath}}", + afterHeader: "{{$afterHeader}}{{/afterHeader}}", + bodyClasses: "{{$bodyClasses}}{{/bodyClasses}}", + bodyEnd: "{{$bodyEnd}}{{/bodyEnd}}", + content: "{{$content}}{{/content}}", + cookieMessage: "{{$cookieMessage}}{{/cookieMessage}}", + footerSupportLinks: "{{$footerSupportLinks}}{{/footerSupportLinks}}", + footerTop: "{{$footerTop}}{{/footerTop}}", + head: "{{$head}}{{/head}}", + headerClass: "{{$headerClass}}{{/headerClass}}", + insideHeader: "{{$insideHeader}}{{/insideHeader}}", + pageTitle: "{{$pageTitle}}GOV.UK - The best place to find government services and information{{/pageTitle}}", + propositionHeader: "{{$propositionHeader}}{{/propositionHeader}}" +}; diff --git a/lib/template-conversion.js b/lib/template-conversion.js new file mode 100644 index 0000000000..7e6323bbe2 --- /dev/null +++ b/lib/template-conversion.js @@ -0,0 +1,22 @@ +var Hogan = require(__dirname + '/hogan.js'), + fs = require('fs'), + path = require('path'), + govukDir = path.normalize(__dirname + '/../govuk'), + govukConfig = require(govukDir + '/config'), + compiledTemplate, + govukTemplate, + handleErr; + +handleErr = function (err) { + if (err) { + throw err; + } +}; + +module.exports = { + convert : function () { + govukTemplate = fs.readFileSync(govukDir + '/views/govuk_template.html', { encoding : 'utf-8' }); + compiledTemplate = Hogan.compile(govukTemplate); + fs.writeFileSync(govukDir + '/views/govuk_template.html', compiledTemplate.render(govukConfig), { encoding : 'utf-8' }); + } +}; From 7bd0eec1f08bd477856e7376d87dfc6715ea2a8f Mon Sep 17 00:00:00 2001 From: tombye Date: Sun, 23 Feb 2014 16:00:36 +0000 Subject: [PATCH 5/9] Add new version of template --- govuk/views/govuk_template.html | 62 ++++++++++++++++----------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/govuk/views/govuk_template.html b/govuk/views/govuk_template.html index 0d2cdddb03..902dee5d80 100644 --- a/govuk/views/govuk_template.html +++ b/govuk/views/govuk_template.html @@ -1,29 +1,29 @@ -{{{ topOfPage }}} + - {{ pageTitle }} + {{$pageTitle}}GOV.UK - The best place to find government services and information{{/pageTitle}} - - - - + + + + - + - + - + - + - + - + - + - + - {{{ head }}} + {{$head}}{{/head}} - + -