From cd50ba47a75384dde891463ed058d1965736cd3b Mon Sep 17 00:00:00 2001 From: stevenhao Date: Mon, 19 Jun 2017 12:33:28 -0700 Subject: [PATCH 01/25] boilerplate refactor wip --- .../boilerplate-generator-v2.js | 97 +++++++++++++++++++ .../boilerplate_web_template.js | 88 +++++++++++++++++ packages/boilerplate-generator/package.js | 14 +++ 3 files changed, 199 insertions(+) create mode 100644 packages/boilerplate-generator/boilerplate-generator-v2.js create mode 100644 packages/boilerplate-generator/boilerplate_web_template.js diff --git a/packages/boilerplate-generator/boilerplate-generator-v2.js b/packages/boilerplate-generator/boilerplate-generator-v2.js new file mode 100644 index 00000000000..a82b686ed45 --- /dev/null +++ b/packages/boilerplate-generator/boilerplate-generator-v2.js @@ -0,0 +1,97 @@ +var fs = Npm.require('fs'); +var path = Npm.require('path'); + +// Copied from webapp_server +var readUtf8FileSync = function (filename) { + return Meteor.wrapAsync(fs.readFile)(filename, 'utf8'); +}; + +Boilerplate = function (arch, manifest, options) { + var self = this; + options = options || {}; + self.template = _getTemplate(arch); + self.baseData = null; + + self._generateBoilerplateFromManifest( + manifest, + options + ); +}; + +// The 'extraData' argument can be used to extend 'self.baseData'. Its +// purpose is to allow you to specify data that you might not know at +// the time that you construct the Boilerplate object. (e.g. it is used +// by 'webapp' to specify data that is only known at request-time). +Boilerplate.prototype.toHTML = function (extraData) { + var self = this; + + if (! self.baseData || ! self.template) + throw new Error('Boilerplate did not instantiate correctly.'); + + return "\n" + self.template(_.extend(self.baseData, extraData)); +}; + +// XXX Exported to allow client-side only changes to rebuild the boilerplate +// without requiring a full server restart. +// Produces an HTML string with given manifest and boilerplateSource. +// Optionally takes urlMapper in case urls from manifest need to be prefixed +// or rewritten. +// Optionally takes pathMapper for resolving relative file system paths. +// Optionally allows to override fields of the data context. +Boilerplate.prototype._generateBoilerplateFromManifest = + function (manifest, options) { + var self = this; + // map to the identity by default + var urlMapper = options.urlMapper || _.identity; + var pathMapper = options.pathMapper || _.identity; + + var boilerplateBaseData = { + css: [], + js: [], + head: '', + body: '', + meteorManifest: JSON.stringify(manifest) + }; + + // allow the caller to extend the default base data + _.extend(boilerplateBaseData, options.baseDataExtension); + + _.each(manifest, function (item) { + var urlPath = urlMapper(item.url); + var itemObj = { url: urlPath }; + + if (options.inline) { + itemObj.scriptContent = readUtf8FileSync( + pathMapper(item.path)); + itemObj.inline = true; + } + + if (item.type === 'css' && item.where === 'client') { + boilerplateBaseData.css.push(itemObj); + } + if (item.type === 'js' && item.where === 'client' && + // Dynamic JS modules should not be loaded eagerly in the + // initial HTML of the app. + ! item.path.startsWith('dynamic/')) { + boilerplateBaseData.js.push(itemObj); + } + if (item.type === 'head') { + boilerplateBaseData.head = + readUtf8FileSync(pathMapper(item.path)); + } + if (item.type === 'body') { + boilerplateBaseData.body = + readUtf8FileSync(pathMapper(item.path)); + } + }); + self.baseData = boilerplateBaseData; +}; + +var _getTemplate = _.memoize(function (arch) { + if (arch === 'web.browser') { + return Boilerplate_Web_Browser_Template; + } else if (arch === 'web.cordova') { + } else { + throw new Error('Unsupported arch: ' + arch); + } +}); diff --git a/packages/boilerplate-generator/boilerplate_web_template.js b/packages/boilerplate-generator/boilerplate_web_template.js new file mode 100644 index 00000000000..48ae351d427 --- /dev/null +++ b/packages/boilerplate-generator/boilerplate_web_template.js @@ -0,0 +1,88 @@ +// Template function for rendering the boilerplate html +// Replicates the template defined in boilerplate_web.browser.html +// XXX Does not necessarily preserve formatting (e.g. additionalStaticJs newlines) +// Arguments: root : { htmlAttributes, css : [{ url }], bundledJsCssUrlRewriteHook : Function, head, dynamicHead, body, dynamicBody, inlineScriptsAllowed, additionalStaticJs, meteorRuntimeConfig } + +Boilerplate_Web_Browser_Template = function(root) { + return [].concat( + + // XXX is htmlAttributes ever anything but {}? + // may just be a generic Blaze/Spacebars thing. + function props1(htmlAttributes) { + return [ + ['' + ]; + }(root.htmlAttributes), + + [ + '' + ], + + function each1(css) { + var bundledJsCssUrlRewriteHook = root.bundledJsCssUrlRewriteHook; + return _.map(css, function(obj) { + var url = obj.url; + return ' '; + }); + }(root.css), + + [ + root.head, + root.dynamicHead, + '', + '', + root.body, + root.dynamicBody, + '' + ], + + function if1(inlineScriptsAllowed) { + var meteorRuntimeConfig = root.meteorRuntimeConfig; + var rootUrlPathPrefix = root.rootUrlPathPrefix; + + if (inlineScriptsAllowed) { + return [ + '' // XXX add 2 spaces to fix indentation + ]; + } else { + return [ + '' // XXX add 2 spaces to fix indentation + ]; + } + }(root.inlineScriptsAllowed), + + [''], + + function each2(js) { + var bundledJsCssUrlRewriteHook = root.bundledJsCssUrlRewriteHook; + return _.map(js, function(obj) { + var url = obj.url; + return ' '; + }); + }(root.js), + + function each3(additionalStaticJs) { + var inlineScriptsAllowed = root.inlineScriptsAllowed; + var rootUrlPathPrefix = root.rootUrlPathPrefix; + return _.map(additionalStaticJs, function(obj) { + var contents = obj.contents; + var pathname = obj.pathname; + if (inlineScriptsAllowed) { + return ' '; + } else { + return " "; + } + }); + }(root.additionalStaticJs), + + [ + '', '', + '', + '' + ] + ).join('\n'); // undefined is treated as empty string (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join) +} diff --git a/packages/boilerplate-generator/package.js b/packages/boilerplate-generator/package.js index 3faf0a4e51b..1be1c0c9cee 100644 --- a/packages/boilerplate-generator/package.js +++ b/packages/boilerplate-generator/package.js @@ -3,6 +3,7 @@ Package.describe({ version: '1.1.0' }); +if(false){ Package.onUse(function (api) { api.use([ 'underscore', @@ -22,3 +23,16 @@ Package.onUse(function (api) { 'boilerplate_web.cordova.html' ], 'server'); }); +} else { + +Package.onUse(function (api) { + api.use([ + 'underscore', + ], 'server'); + api.addFiles([ + 'boilerplate_web_template.js', + 'boilerplate-generator-v2.js' + ], 'server'); + api.export(['Boilerplate'], 'server'); +}); +} From e4fdd29d9d2a1d6f0852f23af0481c55b47154b9 Mon Sep 17 00:00:00 2001 From: stevenhao Date: Mon, 19 Jun 2017 12:49:18 -0700 Subject: [PATCH 02/25] rename files --- ...tor-v2.js => boilerplate-generator-old.js} | 31 +++++++++++------- .../boilerplate-generator.js | 32 +++++++------------ packages/boilerplate-generator/package.js | 7 ++-- 3 files changed, 36 insertions(+), 34 deletions(-) rename packages/boilerplate-generator/{boilerplate-generator-v2.js => boilerplate-generator-old.js} (75%) diff --git a/packages/boilerplate-generator/boilerplate-generator-v2.js b/packages/boilerplate-generator/boilerplate-generator-old.js similarity index 75% rename from packages/boilerplate-generator/boilerplate-generator-v2.js rename to packages/boilerplate-generator/boilerplate-generator-old.js index a82b686ed45..22e06e98f01 100644 --- a/packages/boilerplate-generator/boilerplate-generator-v2.js +++ b/packages/boilerplate-generator/boilerplate-generator-old.js @@ -11,9 +11,11 @@ Boilerplate = function (arch, manifest, options) { options = options || {}; self.template = _getTemplate(arch); self.baseData = null; + self.func = null; - self._generateBoilerplateFromManifest( + self._generateBoilerplateFromManifestAndSource( manifest, + self.template, options ); }; @@ -25,10 +27,12 @@ Boilerplate = function (arch, manifest, options) { Boilerplate.prototype.toHTML = function (extraData) { var self = this; - if (! self.baseData || ! self.template) + if (! self.baseData || ! self.func) throw new Error('Boilerplate did not instantiate correctly.'); - return "\n" + self.template(_.extend(self.baseData, extraData)); + return "\n" + + Blaze.toHTML(Blaze.With(_.extend(self.baseData, extraData), + self.func)); }; // XXX Exported to allow client-side only changes to rebuild the boilerplate @@ -38,8 +42,8 @@ Boilerplate.prototype.toHTML = function (extraData) { // or rewritten. // Optionally takes pathMapper for resolving relative file system paths. // Optionally allows to override fields of the data context. -Boilerplate.prototype._generateBoilerplateFromManifest = - function (manifest, options) { +Boilerplate.prototype._generateBoilerplateFromManifestAndSource = + function (manifest, boilerplateSource, options) { var self = this; // map to the identity by default var urlMapper = options.urlMapper || _.identity; @@ -84,14 +88,19 @@ Boilerplate.prototype._generateBoilerplateFromManifest = readUtf8FileSync(pathMapper(item.path)); } }); + var boilerplateRenderCode = SpacebarsCompiler.compile( + boilerplateSource, { isBody: true }); + + // Note that we are actually depending on eval's local environment capture + // so that UI and HTML are visible to the eval'd code. + // XXX the template we are evaluating relies on the fact that UI is globally + // available. + global.UI = UI; + self.func = eval(boilerplateRenderCode); self.baseData = boilerplateBaseData; }; var _getTemplate = _.memoize(function (arch) { - if (arch === 'web.browser') { - return Boilerplate_Web_Browser_Template; - } else if (arch === 'web.cordova') { - } else { - throw new Error('Unsupported arch: ' + arch); - } + var filename = 'boilerplate_' + arch + '.html'; + return Assets.getText(filename); }); diff --git a/packages/boilerplate-generator/boilerplate-generator.js b/packages/boilerplate-generator/boilerplate-generator.js index 22e06e98f01..ac3f77ccee3 100644 --- a/packages/boilerplate-generator/boilerplate-generator.js +++ b/packages/boilerplate-generator/boilerplate-generator.js @@ -11,11 +11,9 @@ Boilerplate = function (arch, manifest, options) { options = options || {}; self.template = _getTemplate(arch); self.baseData = null; - self.func = null; - self._generateBoilerplateFromManifestAndSource( + self._generateBoilerplateFromManifest( manifest, - self.template, options ); }; @@ -27,12 +25,10 @@ Boilerplate = function (arch, manifest, options) { Boilerplate.prototype.toHTML = function (extraData) { var self = this; - if (! self.baseData || ! self.func) + if (! self.baseData || ! self.template) throw new Error('Boilerplate did not instantiate correctly.'); - return "\n" + - Blaze.toHTML(Blaze.With(_.extend(self.baseData, extraData), - self.func)); + return "\n" + self.template(_.extend(self.baseData, extraData)); }; // XXX Exported to allow client-side only changes to rebuild the boilerplate @@ -42,8 +38,8 @@ Boilerplate.prototype.toHTML = function (extraData) { // or rewritten. // Optionally takes pathMapper for resolving relative file system paths. // Optionally allows to override fields of the data context. -Boilerplate.prototype._generateBoilerplateFromManifestAndSource = - function (manifest, boilerplateSource, options) { +Boilerplate.prototype._generateBoilerplateFromManifest = + function (manifest, options) { var self = this; // map to the identity by default var urlMapper = options.urlMapper || _.identity; @@ -88,19 +84,15 @@ Boilerplate.prototype._generateBoilerplateFromManifestAndSource = readUtf8FileSync(pathMapper(item.path)); } }); - var boilerplateRenderCode = SpacebarsCompiler.compile( - boilerplateSource, { isBody: true }); - - // Note that we are actually depending on eval's local environment capture - // so that UI and HTML are visible to the eval'd code. - // XXX the template we are evaluating relies on the fact that UI is globally - // available. - global.UI = UI; - self.func = eval(boilerplateRenderCode); self.baseData = boilerplateBaseData; }; var _getTemplate = _.memoize(function (arch) { - var filename = 'boilerplate_' + arch + '.html'; - return Assets.getText(filename); + if (arch === 'web.browser') { + return Boilerplate_Web_Browser_Template; + } else if (arch === 'web.cordova') { + throw new Error('Cordova template not implemented'); + } else { + throw new Error('Unsupported arch: ' + arch); + } }); diff --git a/packages/boilerplate-generator/package.js b/packages/boilerplate-generator/package.js index 1be1c0c9cee..3e1535e2375 100644 --- a/packages/boilerplate-generator/package.js +++ b/packages/boilerplate-generator/package.js @@ -3,7 +3,8 @@ Package.describe({ version: '1.1.0' }); -if(false){ +var USE_OLD_BOILERPLATE_GENERATOR = true; +if(USE_OLD_BOILERPLATE_GENERATOR){ Package.onUse(function (api) { api.use([ 'underscore', @@ -12,7 +13,7 @@ Package.onUse(function (api) { 'htmljs', 'ui', ], 'server'); - api.addFiles(['boilerplate-generator.js'], 'server'); + api.addFiles(['boilerplate-generator-old.js'], 'server'); api.export(['Boilerplate'], 'server'); // These are spacebars templates, but we process them manually with the // spacebars compiler rather than letting the 'templating' package (which @@ -31,7 +32,7 @@ Package.onUse(function (api) { ], 'server'); api.addFiles([ 'boilerplate_web_template.js', - 'boilerplate-generator-v2.js' + 'boilerplate-generator.js' ], 'server'); api.export(['Boilerplate'], 'server'); }); From ad2601e2c3f2639d97780156edf4e2899a50ed7e Mon Sep 17 00:00:00 2001 From: stevenhao Date: Mon, 19 Jun 2017 12:57:39 -0700 Subject: [PATCH 03/25] make switching between old/new easier --- packages/boilerplate-generator/boilerplate_web_template.js | 4 +++- packages/boilerplate-generator/package.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/boilerplate-generator/boilerplate_web_template.js b/packages/boilerplate-generator/boilerplate_web_template.js index 48ae351d427..79dfa2b4db3 100644 --- a/packages/boilerplate-generator/boilerplate_web_template.js +++ b/packages/boilerplate-generator/boilerplate_web_template.js @@ -83,6 +83,8 @@ Boilerplate_Web_Browser_Template = function(root) { '', '', '', '' - ] + ], + + ['', ''] // to help distinguishes old generator from new [just for testing] ).join('\n'); // undefined is treated as empty string (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join) } diff --git a/packages/boilerplate-generator/package.js b/packages/boilerplate-generator/package.js index 3e1535e2375..5829d49292b 100644 --- a/packages/boilerplate-generator/package.js +++ b/packages/boilerplate-generator/package.js @@ -4,6 +4,7 @@ Package.describe({ }); var USE_OLD_BOILERPLATE_GENERATOR = true; +USE_OLD_BOILERPLATE_GENERATOR = false; // uncomment this line to use the new boilerplate generator if(USE_OLD_BOILERPLATE_GENERATOR){ Package.onUse(function (api) { api.use([ From 4365d8627720a3ba42621f8413925dbf93050835 Mon Sep 17 00:00:00 2001 From: stevenhao Date: Wed, 21 Jun 2017 12:09:51 -0700 Subject: [PATCH 04/25] refactor and modernize boilerplate-generator --- .../boilerplate-generator.js | 99 +++++++++---------- .../boilerplate_web_browser_template.js | 70 +++++++++++++ .../boilerplate_web_cordova_template.js | 83 ++++++++++++++++ .../boilerplate_web_template.js | 90 ----------------- packages/boilerplate-generator/package.js | 35 +------ 5 files changed, 206 insertions(+), 171 deletions(-) create mode 100644 packages/boilerplate-generator/boilerplate_web_browser_template.js create mode 100644 packages/boilerplate-generator/boilerplate_web_cordova_template.js delete mode 100644 packages/boilerplate-generator/boilerplate_web_template.js diff --git a/packages/boilerplate-generator/boilerplate-generator.js b/packages/boilerplate-generator/boilerplate-generator.js index ac3f77ccee3..70b0de0e764 100644 --- a/packages/boilerplate-generator/boilerplate-generator.js +++ b/packages/boilerplate-generator/boilerplate-generator.js @@ -1,51 +1,46 @@ -var fs = Npm.require('fs'); -var path = Npm.require('path'); +import BoilerplateWebBrowserTemplate from './boilerplate_web_browser_template'; +import BoilerplateWebCordovaTemplate from './boilerplate_web_cordova_template'; +const fs = Npm.require('fs'); +const path = Npm.require('path'); // Copied from webapp_server -var readUtf8FileSync = function (filename) { - return Meteor.wrapAsync(fs.readFile)(filename, 'utf8'); -}; - -Boilerplate = function (arch, manifest, options) { - var self = this; - options = options || {}; - self.template = _getTemplate(arch); - self.baseData = null; +const readUtf8FileSync = filename => Meteor.wrapAsync(fs.readFile)(filename, 'utf8'); - self._generateBoilerplateFromManifest( - manifest, - options - ); -}; +export class Boilerplate { + constructor (arch, manifest, options) { + options = options || {}; + this.template = _getTemplate(arch); + this.baseData = null; -// The 'extraData' argument can be used to extend 'self.baseData'. Its -// purpose is to allow you to specify data that you might not know at -// the time that you construct the Boilerplate object. (e.g. it is used -// by 'webapp' to specify data that is only known at request-time). -Boilerplate.prototype.toHTML = function (extraData) { - var self = this; + this._generateBoilerplateFromManifest( + manifest, + options + ); + } - if (! self.baseData || ! self.template) - throw new Error('Boilerplate did not instantiate correctly.'); + // The 'extraData' argument can be used to extend 'self.baseData'. Its + // purpose is to allow you to specify data that you might not know at + // the time that you construct the Boilerplate object. (e.g. it is used + // by 'webapp' to specify data that is only known at request-time). + toHTML (extraData) { + if (!this.baseData || !this.template) + throw new Error('Boilerplate did not instantiate correctly.'); - return "\n" + self.template(_.extend(self.baseData, extraData)); -}; + return "\n" + this.template(_.extend(this.baseData, extraData)); + } -// XXX Exported to allow client-side only changes to rebuild the boilerplate -// without requiring a full server restart. -// Produces an HTML string with given manifest and boilerplateSource. -// Optionally takes urlMapper in case urls from manifest need to be prefixed -// or rewritten. -// Optionally takes pathMapper for resolving relative file system paths. -// Optionally allows to override fields of the data context. -Boilerplate.prototype._generateBoilerplateFromManifest = - function (manifest, options) { - var self = this; - // map to the identity by default - var urlMapper = options.urlMapper || _.identity; - var pathMapper = options.pathMapper || _.identity; + // XXX Exported to allow client-side only changes to rebuild the boilerplate + // without requiring a full server restart. + // Produces an HTML string with given manifest and boilerplateSource. + // Optionally takes urlMapper in case urls from manifest need to be prefixed + // or rewritten. + // Optionally takes pathMapper for resolving relative file system paths. + // Optionally allows to override fields of the data context. + _generateBoilerplateFromManifest (manifest, options) { + const urlMapper = options.urlMapper || _.identity; + const pathMapper = options.pathMapper || _.identity; - var boilerplateBaseData = { + const boilerplateBaseData = { css: [], js: [], head: '', @@ -56,9 +51,9 @@ Boilerplate.prototype._generateBoilerplateFromManifest = // allow the caller to extend the default base data _.extend(boilerplateBaseData, options.baseDataExtension); - _.each(manifest, function (item) { - var urlPath = urlMapper(item.url); - var itemObj = { url: urlPath }; + _.each(manifest, item => { + const urlPath = urlMapper(item.url); + const itemObj = { url: urlPath }; if (options.inline) { itemObj.scriptContent = readUtf8FileSync( @@ -70,9 +65,9 @@ Boilerplate.prototype._generateBoilerplateFromManifest = boilerplateBaseData.css.push(itemObj); } if (item.type === 'js' && item.where === 'client' && - // Dynamic JS modules should not be loaded eagerly in the - // initial HTML of the app. - ! item.path.startsWith('dynamic/')) { + // Dynamic JS modules should not be loaded eagerly in the + // initial HTML of the app. + !item.path.startsWith('dynamic/')) { boilerplateBaseData.js.push(itemObj); } if (item.type === 'head') { @@ -84,14 +79,18 @@ Boilerplate.prototype._generateBoilerplateFromManifest = readUtf8FileSync(pathMapper(item.path)); } }); - self.baseData = boilerplateBaseData; + this.baseData = boilerplateBaseData; + } }; -var _getTemplate = _.memoize(function (arch) { +// Returns a template function that, when called, produces the boilerplate +// html as a string. +const _getTemplate = _.memoize(arch => { + arch = 'web.cordova'; if (arch === 'web.browser') { - return Boilerplate_Web_Browser_Template; + return BoilerplateWebBrowserTemplate; } else if (arch === 'web.cordova') { - throw new Error('Cordova template not implemented'); + return BoilerplateWebCordovaTemplate; } else { throw new Error('Unsupported arch: ' + arch); } diff --git a/packages/boilerplate-generator/boilerplate_web_browser_template.js b/packages/boilerplate-generator/boilerplate_web_browser_template.js new file mode 100644 index 00000000000..921fc9a0abb --- /dev/null +++ b/packages/boilerplate-generator/boilerplate_web_browser_template.js @@ -0,0 +1,70 @@ +// Template function for rendering the boilerplate html for browsers +// Replicates the template defined in boilerplate_web.browser.html +// Arguments: root : { htmlAttributes, css : [{ url }], bundledJsCssUrlRewriteHook : Function, head, dynamicHead, body, dynamicBody, inlineScriptsAllowed, additionalStaticJs, meteorRuntimeConfig } + +export default function(manifest) { + const root = manifest; + // XXX do we need to do some validation on the properties of root? + return [].concat( + [ + ' + _.template(' <%= attrName %>="<%- attrValue %>"')({ + attrName: key, + attrValue: value + }) + ).join('') + '>', + '' + ], + + _.map(root.css, ({url}) => + _.template(' ')({ + href: root.bundledJsCssUrlRewriteHook(url) + }) + ), + + [ + root.head, + root.dynamicHead, + '', + '', + root.body, + root.dynamicBody, + '', + _.template(root.inlineScriptsAllowed + ? ' ' + : ' ' + )({ + contents: _.template('__meteor_runtime_config__ = JSON.parse(decodeURIComponent(<%= conf %>))')({ + conf: root.meteorRuntimeConfig + }), + src: root.rootUrlPathPrefix + '/meteor_runtime_config.js' + }), + '' + ], + + _.map(root.js, ({url}) => + _.template(' ')({ + src: root.bundledJsCssUrlRewriteHook(url) + }) + ), + + _.map(root.additionalStaticJs, ({contents, pathname}) => ( + _.template(root.inlineScriptsAllowed + ? ' ' + : ' ' + )({ + contents: contents, + src: root.rootUrlPathPrefix + pathname + }) + )), + + [ + '', '', + '', + '' + ], + + ['', ''] + ).join('\n'); +} + diff --git a/packages/boilerplate-generator/boilerplate_web_cordova_template.js b/packages/boilerplate-generator/boilerplate_web_cordova_template.js new file mode 100644 index 00000000000..d1b3455535f --- /dev/null +++ b/packages/boilerplate-generator/boilerplate_web_cordova_template.js @@ -0,0 +1,83 @@ +// Template function for rendering the boilerplate html for cordova +// Replicates the template defined in boilerplate_web.cordova.html +// Arguments: root : { htmlAttributes, css : [{ url }], bundledJsCssUrlRewriteHook : Function, head, dynamicHead, body, dynamicBody, inlineScriptsAllowed, additionalStaticJs, meteorRuntimeConfig } + +function escapeAttr(attr) { + // XXX Technically, we only need to escape ['<', '"'] in attribute mode + // For simplicity, we fall back onto _.template's html escaping, and escape ['&', '<', '>', '"', "'"] regardless of context + // Most browsers seem to handle 'over-escaping' gracefully + return _.template('<%- attr %>')({ attr }); +} + +function escapeURL(url) { + // XXX Do we need to do anything here? It's possible that the URL may be escaped already + return url; +} + +export default function(manifest) { + const root = manifest; + // XXX do we need to do some validation on the properties of root? + return [].concat( + [ + ' + _.template(' <%= attrName %>="<%= attrValue %>"')({ + attrName: key, + attrValue: escapeAttr(value) + }) + ).join('') + '>', + '' + ], + + _.map(root.css, ({url}) => + _.template(' ')({ + href: escapeAttr(escapeURL(root.bundledJsCssUrlRewriteHook(url))) + }) + ), + + [ + root.head, + root.dynamicHead, + '', + '', + root.body, + root.dynamicBody, + '', + _.template(root.inlineScriptsAllowed + ? ' ' + : ' ' + )({ + // maybe using another template here is overkill... + contents: _.template('__meteor_runtime_config__ = JSON.parse(decodeURIComponent(<%= conf %>))')({ + conf: root.meteorRuntimeConfig // XXX why is this a URI? + }), + src: escapeAttr(escapeURL(root.rootUrlPathPrefix + '/meteor_runtime_config.js')) + }), + '' + ], + + _.map(root.js, ({url}) => + _.template(' ')({ + src: escapeAttr(escapeURL(root.bundledJsCssUrlRewriteHook(url))) + }) + ), + + _.map(root.additionalStaticJs, ({contents, pathname}) => ( + _.template(root.inlineScriptsAllowed + ? ' ' + : ' ' + )({ + contents: contents, + src: escapeAttr(escapeURL(root.rootUrlPathPrefix + pathname)) + }) + )), + + [ + '', '', + '', + '' + ], + + ['', ''] // to help distinguish old generator from new + ).join('\n'); +} + diff --git a/packages/boilerplate-generator/boilerplate_web_template.js b/packages/boilerplate-generator/boilerplate_web_template.js deleted file mode 100644 index 79dfa2b4db3..00000000000 --- a/packages/boilerplate-generator/boilerplate_web_template.js +++ /dev/null @@ -1,90 +0,0 @@ -// Template function for rendering the boilerplate html -// Replicates the template defined in boilerplate_web.browser.html -// XXX Does not necessarily preserve formatting (e.g. additionalStaticJs newlines) -// Arguments: root : { htmlAttributes, css : [{ url }], bundledJsCssUrlRewriteHook : Function, head, dynamicHead, body, dynamicBody, inlineScriptsAllowed, additionalStaticJs, meteorRuntimeConfig } - -Boilerplate_Web_Browser_Template = function(root) { - return [].concat( - - // XXX is htmlAttributes ever anything but {}? - // may just be a generic Blaze/Spacebars thing. - function props1(htmlAttributes) { - return [ - ['' - ]; - }(root.htmlAttributes), - - [ - '' - ], - - function each1(css) { - var bundledJsCssUrlRewriteHook = root.bundledJsCssUrlRewriteHook; - return _.map(css, function(obj) { - var url = obj.url; - return ' '; - }); - }(root.css), - - [ - root.head, - root.dynamicHead, - '', - '', - root.body, - root.dynamicBody, - '' - ], - - function if1(inlineScriptsAllowed) { - var meteorRuntimeConfig = root.meteorRuntimeConfig; - var rootUrlPathPrefix = root.rootUrlPathPrefix; - - if (inlineScriptsAllowed) { - return [ - '' // XXX add 2 spaces to fix indentation - ]; - } else { - return [ - '' // XXX add 2 spaces to fix indentation - ]; - } - }(root.inlineScriptsAllowed), - - [''], - - function each2(js) { - var bundledJsCssUrlRewriteHook = root.bundledJsCssUrlRewriteHook; - return _.map(js, function(obj) { - var url = obj.url; - return ' '; - }); - }(root.js), - - function each3(additionalStaticJs) { - var inlineScriptsAllowed = root.inlineScriptsAllowed; - var rootUrlPathPrefix = root.rootUrlPathPrefix; - return _.map(additionalStaticJs, function(obj) { - var contents = obj.contents; - var pathname = obj.pathname; - if (inlineScriptsAllowed) { - return ' '; - } else { - return " "; - } - }); - }(root.additionalStaticJs), - - [ - '', '', - '', - '' - ], - - ['', ''] // to help distinguishes old generator from new [just for testing] - ).join('\n'); // undefined is treated as empty string (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join) -} diff --git a/packages/boilerplate-generator/package.js b/packages/boilerplate-generator/package.js index 5829d49292b..c42201f4475 100644 --- a/packages/boilerplate-generator/package.js +++ b/packages/boilerplate-generator/package.js @@ -3,38 +3,11 @@ Package.describe({ version: '1.1.0' }); -var USE_OLD_BOILERPLATE_GENERATOR = true; -USE_OLD_BOILERPLATE_GENERATOR = false; // uncomment this line to use the new boilerplate generator -if(USE_OLD_BOILERPLATE_GENERATOR){ -Package.onUse(function (api) { +Package.onUse(api => { + api.use('ecmascript'); api.use([ 'underscore', - 'spacebars-compiler', - 'spacebars', - 'htmljs', - 'ui', ], 'server'); - api.addFiles(['boilerplate-generator-old.js'], 'server'); - api.export(['Boilerplate'], 'server'); - // These are spacebars templates, but we process them manually with the - // spacebars compiler rather than letting the 'templating' package (which - // isn't fully supported on the server yet) handle it. That also means that - // they don't contain the outer "