From 17a30518a21c2aa18a942f1fcdf24e89146ab7e9 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Tue, 9 Jan 2018 01:21:45 +0400 Subject: [PATCH 1/6] Make loader aware of "internal" partials As opposed to external partial which needs resolving, "internal" partial is defined in the same file (may be inline partial or failover content of unresolved partial block). If reference to it fails to resolve as external file, we need to check if the same file contains target partial --- index.js | 77 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/index.js b/index.js index cdb05560f..4b1935494 100644 --- a/index.js +++ b/index.js @@ -26,6 +26,23 @@ function getLoaderConfig(loaderContext) { return assign({}, config, query); } +/** + * Custom error constructor. + * + * @param {String} message + * @param {Object} data + * */ +function CustomError(message, data) { + this.name = 'CustomError'; + this.message = message; + this.stack = (new Error()).stack; + this.data = data; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, CustomError); + } +} +CustomError.prototype = new Error(); + module.exports = function(source) { if (this.cacheable) this.cacheable(); var loaderApi = this; @@ -136,6 +153,26 @@ module.exports = function(source) { hb.JavaScriptCompiler = MyJavaScriptCompiler; + // Define custom visitor for further template AST parsing + var Visitor = handlebars.Visitor; + function InternalBlocksVisitor() { + this.partialBlocks = []; + this.inlineBlocks = []; + } + + InternalBlocksVisitor.prototype = new Visitor(); + InternalBlocksVisitor.prototype.PartialBlockStatement = function(partial) { + this.partialBlocks.push(partial.name.original); + Visitor.prototype.PartialBlockStatement.call(this, partial); + }; + InternalBlocksVisitor.prototype.DecoratorBlock = function(partial) { + if (partial.path.original === 'inline') { + this.inlineBlocks.push(partial.params[0].value); + } + + Visitor.prototype.DecoratorBlock.call(this, partial); + }; + // This is an async loader var loaderAsyncCallback = this.async(); @@ -167,16 +204,23 @@ module.exports = function(source) { // Precompile template var template = ''; + // AST holder for current template + var ast = null; + + // Compile options + var opts = assign({ + knownHelpersOnly: !firstCompile, + // TODO: Remove these in next major release + preventIndent: !!query.preventIndent, + compat: !!query.compat + }, precompileOptions, { + knownHelpers: knownHelpers, + }); + try { if (source) { - template = hb.precompile(source, assign({ - knownHelpersOnly: !firstCompile, - // TODO: Remove these in next major release - preventIndent: !!query.preventIndent, - compat: !!query.compat - }, precompileOptions, { - knownHelpers: knownHelpers, - })); + ast = hb.parse(source, opts); + template = hb.precompile(ast, opts); } } catch (err) { return loaderAsyncCallback(err); @@ -261,7 +305,7 @@ module.exports = function(source) { (function tryExtension() { if (i >= extensions.length) { var errorMsg = util.format("Partial '%s' not found", request); - return callback(new Error(errorMsg)); + return callback(new CustomError(errorMsg, {request: request})); } var extension = extensions[i++]; @@ -286,7 +330,20 @@ module.exports = function(source) { } else { partialResolver(request, function(err, resolved){ if(err) { - return partialCallback(err); + var visitor = new InternalBlocksVisitor(); + var errPartialName = err.data.request.substring(err.data.request.lastIndexOf('/') + 1); + + visitor.accept(ast); + + if ( + visitor.inlineBlocks.indexOf(errPartialName !== -1) || + visitor.partialBlocks.indexOf(errPartialName !== -1) + ) { + return partialCallback(); + } else { + return partialCallback(err); + } + } foundPartials[partial] = resolved; needRecompile = true; From 86e9572b477bdb104c5a9bb011dab7d7b7b40f81 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Tue, 9 Jan 2018 01:34:59 +0400 Subject: [PATCH 2/6] Update changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33e6fae47..8a80c2907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,8 @@ ## [Unreleased] -### Added -- Your feature here! +### Fixed +- Fixed resolving of inline partials and partial blocks with failover content (#106, #135) ## [1.6.0] - 2017-09-01 ## From 35580ca74c60a42b0f40d1b1a335660a8c4ebe54 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Tue, 9 Jan 2018 02:22:01 +0400 Subject: [PATCH 3/6] Refactor --- index.js | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index 4b1935494..4b90c495b 100644 --- a/index.js +++ b/index.js @@ -26,23 +26,6 @@ function getLoaderConfig(loaderContext) { return assign({}, config, query); } -/** - * Custom error constructor. - * - * @param {String} message - * @param {Object} data - * */ -function CustomError(message, data) { - this.name = 'CustomError'; - this.message = message; - this.stack = (new Error()).stack; - this.data = data; - if (Error.captureStackTrace) { - Error.captureStackTrace(this, CustomError); - } -} -CustomError.prototype = new Error(); - module.exports = function(source) { if (this.cacheable) this.cacheable(); var loaderApi = this; @@ -305,7 +288,7 @@ module.exports = function(source) { (function tryExtension() { if (i >= extensions.length) { var errorMsg = util.format("Partial '%s' not found", request); - return callback(new CustomError(errorMsg, {request: request})); + return callback(new Error(errorMsg)); } var extension = extensions[i++]; @@ -331,13 +314,12 @@ module.exports = function(source) { partialResolver(request, function(err, resolved){ if(err) { var visitor = new InternalBlocksVisitor(); - var errPartialName = err.data.request.substring(err.data.request.lastIndexOf('/') + 1); visitor.accept(ast); if ( - visitor.inlineBlocks.indexOf(errPartialName !== -1) || - visitor.partialBlocks.indexOf(errPartialName !== -1) + visitor.inlineBlocks.indexOf(request !== -1) || + visitor.partialBlocks.indexOf(request !== -1) ) { return partialCallback(); } else { From d742f535c6160ffb7b2d43c1779873f5dd0bacff Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Tue, 9 Jan 2018 14:57:57 +0400 Subject: [PATCH 4/6] Fix bug --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 4b90c495b..afbd08489 100644 --- a/index.js +++ b/index.js @@ -318,8 +318,8 @@ module.exports = function(source) { visitor.accept(ast); if ( - visitor.inlineBlocks.indexOf(request !== -1) || - visitor.partialBlocks.indexOf(request !== -1) + visitor.inlineBlocks.indexOf(request) !== -1 || + visitor.partialBlocks.indexOf(request) !== -1 ) { return partialCallback(); } else { From 1e0433064b81d7295e342a2286416057decea9f7 Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Sun, 14 Jan 2018 17:11:46 +0400 Subject: [PATCH 5/6] Add tests for inline partial and partial block --- test/test.js | 14 ++++++++++++++ test/with-inline-partial.handlebars | 0 test/with-partial-block.handlebars | 3 +++ 3 files changed, 17 insertions(+) create mode 100644 test/with-inline-partial.handlebars create mode 100644 test/with-partial-block.handlebars diff --git a/test/test.js b/test/test.js index faba1e36e..600bfcf07 100644 --- a/test/test.js +++ b/test/test.js @@ -542,4 +542,18 @@ describe('handlebars-loader', function () { }); }); + it('should use failover content of the partial block if it refers to non-existent partial', function (done) { + testTemplate(loader, './with-partial-block.handlebars', {}, function (err, output, require) { + assert.ok(output, 'generated output'); + done(); + }); + }); + + it('should recognize and render inline partials', function (done) { + testTemplate(loader, './with-inline-partial.handlebars', {}, function (err, output, require) { + assert.ok(output, 'generated output'); + done(); + }); + }); + }); diff --git a/test/with-inline-partial.handlebars b/test/with-inline-partial.handlebars new file mode 100644 index 000000000..e69de29bb diff --git a/test/with-partial-block.handlebars b/test/with-partial-block.handlebars new file mode 100644 index 000000000..346929497 --- /dev/null +++ b/test/with-partial-block.handlebars @@ -0,0 +1,3 @@ +{{#> non-existent}} +
Failover
+{{/non-existent}} \ No newline at end of file From f984f28fe961a77f967ccb5c8b1856ab860ac44e Mon Sep 17 00:00:00 2001 From: andreyvolokitin Date: Sun, 14 Jan 2018 17:12:47 +0400 Subject: [PATCH 6/6] Add inline partial --- test/with-inline-partial.handlebars | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/with-inline-partial.handlebars b/test/with-inline-partial.handlebars index e69de29bb..f5049d153 100644 --- a/test/with-inline-partial.handlebars +++ b/test/with-inline-partial.handlebars @@ -0,0 +1,5 @@ +{{#*inline "printFoo"}} + Foo +{{/inline}} + +{{> printFoo}} \ No newline at end of file