Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #108 side effect #131

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions Gruntfile.js
Expand Up @@ -172,6 +172,15 @@ module.exports = function(grunt) {
'tmp/amd_namespace.js': ['test/fixtures/amd.html']
}
},
amd_namespace_with_dots: {
options: {
amd: ['handlebars', 'handlebars.helpers'],
namespace: 'JST.foo.templates'
},
files: {
'tmp/amd_namespace_with_dots.js': ['test/fixtures/amd.html']
}
},
amd_partials_use_namespace: {
options: {
amd: ['handlebars'],
Expand Down
279 changes: 142 additions & 137 deletions tasks/handlebars.js
@@ -1,140 +1,140 @@
/*
* grunt-contrib-handlebars
* http://gruntjs.com/
*
* Copyright (c) 2015 Tim Branyen, contributors
* Licensed under the MIT license.
*/

'use strict';
var chalk = require('chalk');
var nsdeclare = require('nsdeclare');

module.exports = function(grunt) {
var _ = grunt.util._;

// content conversion for templates
var defaultProcessContent = function(content) { return content; };

// AST processing for templates
var defaultProcessAST = function(ast) { return ast; };

// filename conversion for templates
var defaultProcessName = function(name) { return name; };

// filename conversion for partials
var defaultProcessPartialName = function(filepath) {
var pieces = _.last(filepath.split('/')).split('.');
var name = _(pieces).without(_.last(pieces)).join('.'); // strips file extension
if (name.charAt(0) === '_') {
name = name.substr(1, name.length); // strips leading _ character
}
return name;
};

var extractGlobalNamespace = function(nsDeclarations) {
// Extract global namespace from any existing namespace declaration.
// The purpose of this method is too fix an issue with AMD when using namespace as a function where the
// nsInfo.namespace will contains the last namespace, not the global namespace.

var declarations = _.keys(nsDeclarations);

// no declaration found
if (!declarations.length) {
return '';
}

// In case only one namespace has been declared it will only return it.
if (declarations.length === 1) {
return declarations[0];
} else {
// we only need to take any declaration to extract the global namespace.
// Another option might be find the shortest declaration which is the global one.
var matches = declarations[0].match(/(this\[[^\[]+\])/g);
return matches[0];
}
};

grunt.registerMultiTask('handlebars', 'Compile handlebars templates and partials.', function() {
var options = this.options({
namespace: 'JST',
separator: grunt.util.linefeed + grunt.util.linefeed,
wrapped: true,
amd: false,
commonjs: false,
knownHelpers: [],
knownHelpersOnly: false
});

// assign regex for partials directory detection
var partialsPathRegex = options.partialsPathRegex || /./;

// assign regex for partial detection
var isPartialRegex = options.partialRegex || /^_/;

// assign transformation functions
var processContent = options.processContent || defaultProcessContent;
var processName = options.processName || defaultProcessName;
var processPartialName = options.processPartialName || defaultProcessPartialName;
var processAST = options.processAST || defaultProcessAST;
var useNamespace = options.namespace !== false;

// assign compiler options
var compilerOptions = options.compilerOptions || {};
var filesCount = 0;

this.files.forEach(function(f) {
var declarations = [];
var partials = [];
var templates = [];

// Namespace info for current template
var nsInfo;

// Map of already declared namespace parts
var nsDeclarations = {};

// nsdeclare options when fetching namespace info
var nsDeclareOptions = {response: 'details', declared: nsDeclarations};

// Just get the namespace info for a given template
var getNamespaceInfo = _.memoize(function(filepath) {
if (!useNamespace) {return undefined;}
if (_.isFunction(options.namespace)) {
return nsdeclare(options.namespace(filepath), nsDeclareOptions);
} else {
return nsdeclare(options.namespace, nsDeclareOptions);
}
});

// iterate files, processing partials and templates separately
f.src.filter(function(filepath) {
// Warn on and remove invalid source files (if nonull was set).
if (!grunt.file.exists(filepath)) {
grunt.log.warn('Source file "' + filepath + '" not found.');
return false;
} else {
return true;
}
})
.forEach(function(filepath) {
var src = processContent(grunt.file.read(filepath), filepath);

var Handlebars = require('handlebars');
var ast, compiled, filename;
try {
// parse the handlebars template into it's AST
ast = processAST(Handlebars.parse(src));
compiled = Handlebars.precompile(ast, compilerOptions);

// if configured to, wrap template in Handlebars.template call
if (options.wrapped === true) {
compiled = 'Handlebars.template(' + compiled + ')';
}
} catch (e) {
grunt.log.error(e);
grunt.fail.warn('Handlebars failed to compile ' + filepath + '.');
}
* grunt-contrib-handlebars
* http://gruntjs.com/
*
* Copyright (c) 2015 Tim Branyen, contributors
* Licensed under the MIT license.
*/
'use strict';
var chalk = require('chalk');
var nsdeclare = require('nsdeclare');
module.exports = function(grunt) {
var _ = grunt.util._;
// content conversion for templates
var defaultProcessContent = function(content) { return content; };
// AST processing for templates
var defaultProcessAST = function(ast) { return ast; };
// filename conversion for templates
var defaultProcessName = function(name) { return name; };
// filename conversion for partials
var defaultProcessPartialName = function(filepath) {
var pieces = _.last(filepath.split('/')).split('.');
var name = _(pieces).without(_.last(pieces)).join('.'); // strips file extension
if (name.charAt(0) === '_') {
name = name.substr(1, name.length); // strips leading _ character
}
return name;
};
var extractGlobalNamespace = function(nsDeclarations) {
// Extract global namespace from any existing namespace declaration.
// The purpose of this method is to fix an issue with AMD when using namespace as a function where the
// nsInfo.namespace will contains the last namespace, not the global namespace.
var declarations = _.keys(nsDeclarations);
// no declaration found
if (!declarations.length) {
return '';
}
// In case only one namespace has been declared it will only return it.
if (declarations.length === 1) {
return declarations[0];
}
else {
// we only need to take any declaration to extract the global namespace.
// Another option might be find the shortest declaration which is the global one.
var matches = declarations[0].match(/(this\[[^\[]+\])/g);
return matches[0];
}
};
grunt.registerMultiTask('handlebars', 'Compile handlebars templates and partials.', function() {
var options = this.options({
namespace: 'JST',
separator: grunt.util.linefeed + grunt.util.linefeed,
wrapped: true,
amd: false,
commonjs: false,
knownHelpers: [],
knownHelpersOnly: false
});
// assign regex for partials directory detection
var partialsPathRegex = options.partialsPathRegex || /./;
// assign regex for partial detection
var isPartialRegex = options.partialRegex || /^_/;
// assign transformation functions
var processContent = options.processContent || defaultProcessContent;
var processName = options.processName || defaultProcessName;
var processPartialName = options.processPartialName || defaultProcessPartialName;
var processAST = options.processAST || defaultProcessAST;
var useNamespace = options.namespace !== false;
// assign compiler options
var compilerOptions = options.compilerOptions || {};
var filesCount = 0;
this.files.forEach(function(f) {
var partials = [];
var templates = [];
// Namespace info for current template
var nsInfo;
// Map of already declared namespace parts
var nsDeclarations = {};
// nsdeclare options when fetching namespace info
var nsDeclareOptions = {response: 'details', declared: nsDeclarations};
// Just get the namespace info for a given template
var getNamespaceInfo = _.memoize(function(filepath) {
if (!useNamespace) {return undefined;}
if (_.isFunction(options.namespace)) {
return nsdeclare(options.namespace(filepath), nsDeclareOptions);
} else {
return nsdeclare(options.namespace, nsDeclareOptions);
}
});
// iterate files, processing partials and templates separately
f.src.filter(function(filepath) {
// Warn on and remove invalid source files (if nonull was set).
if (!grunt.file.exists(filepath)) {
grunt.log.warn('Source file "' + filepath + '" not found.');
return false;
} else {
return true;
}
})
.forEach(function(filepath) {
var src = processContent(grunt.file.read(filepath), filepath);
var Handlebars = require('handlebars');
var ast, compiled, filename;
try {
// parse the handlebars template into it's AST
ast = processAST(Handlebars.parse(src));
compiled = Handlebars.precompile(ast, compilerOptions);
// if configured to, wrap template in Handlebars.template call
if (options.wrapped === true) {
compiled = 'Handlebars.template(' + compiled + ')';
}
} catch (e) {
grunt.log.error(e);
grunt.fail.warn('Handlebars failed to compile ' + filepath + '.');
}

// register partial or add template to namespace
if (partialsPathRegex.test(filepath) && isPartialRegex.test(_.last(filepath.split('/')))) {
Expand Down Expand Up @@ -209,7 +209,12 @@ module.exports = function(grunt) {
if (useNamespace) {
// Namespace has not been explicitly set to false; the AMD
// wrapper will return the object containing the template.
output.push('return ' + extractGlobalNamespace(nsDeclarations) + ';');

// if namespace is a function iterating over file for search global/root namespace
// instead find the declared namespace formatted and allowing namespace with dots
var namespace = _.isFunction(options.namespace) ?
extractGlobalNamespace(nsDeclarations) : getNamespaceInfo().namespace;
output.push('return ' + namespace + ';');
}
output.push('});');
}
Expand Down
13 changes: 13 additions & 0 deletions test/expected/amd_namespace_with_dots.js
@@ -0,0 +1,13 @@
define(['handlebars', 'handlebars.helpers'], function(Handlebars) {

this["JST"] = this["JST"] || {};
this["JST"]["foo"] = this["JST"]["foo"] || {};
this["JST"]["foo"]["templates"] = this["JST"]["foo"]["templates"] || {};

this["JST"]["foo"]["templates"]["test/fixtures/amd.html"] = Handlebars.template({"compiler":[6,">= 2.0.0-beta.1"],"main":function(depth0,helpers,partials,data) {
return "<section class=\"main-app\">\n <h1>Some title</h1>\n <p>I've been compiled with amd support</p>\n</section>";
},"useData":true});

return this["JST"]["foo"]["templates"];

});
9 changes: 9 additions & 0 deletions test/handlebars_test.js
Expand Up @@ -162,6 +162,15 @@ exports.handlebars = {
test.done();
});
},
amd_namespace_with_dots: function(test) {
test.expect(1);

filesAreEqual('amd_namespace_with_dots.js', function(actual, expected) {
test.equal(actual, expected, 'should wrap everything with an AMD define block and have a ' +
'custom module name with dots keeped.');
test.done();
});
},
amd_partials_use_namespace: function(test) {
test.expect(1);

Expand Down