underscore-template-loader
Advanced tools
Comparing version 0.4.1 to 0.5.0
150
index.js
@@ -1,7 +0,1 @@ | ||
var path = require('path'); | ||
var fs = require('fs'); | ||
var url = require("url"); | ||
var loaderUtils = require('loader-utils'); | ||
var attributeParser = require('./parser'); | ||
try { | ||
@@ -14,40 +8,29 @@ var _ = require('underscore'); | ||
module.exports = function() { | ||
var includeRegex = /<@include\s+([\/\w\.]*?[\w]+\.[\w]+)>/g; | ||
var path = require('path'); | ||
var loaderUtils = require('loader-utils'); | ||
// Returns a template file content | ||
var readFile = function(filepath, root) { | ||
var self = readFile; | ||
self.buffer = self.buffer || {}; | ||
// Parsers | ||
var attributeParser = require('./lib/attributeParser'); | ||
var macroParser = require('./lib/macroParser'); | ||
if (filepath in self.buffer) { | ||
return self.buffer[filepath]; | ||
} | ||
// Extendable arguments | ||
var macros = _.extend({}, require('./lib/macros')); | ||
var content = readContent(fs.readFileSync(path.join(root, filepath), 'utf8'), root); | ||
self.buffer[filepath] = content; | ||
return self.buffer[filepath]; | ||
}; | ||
return function(content) { | ||
this.cacheable && this.cacheable(); | ||
var callback = this.async(); | ||
// Parses an external file content | ||
var readContent = function(content, root) { | ||
var matches = includeRegex.exec(content); | ||
// Default arguments | ||
var root, | ||
parseMacros = true, | ||
attributes = ['img:src']; | ||
while (matches != null) { | ||
var file = loaderUtils.urlToRequest(matches[1]); | ||
var rawContent = readFile(path.basename(file), path.join(root, path.dirname(file))); | ||
content = content.replace(matches[0], rawContent); | ||
matches = includeRegex.exec(content); | ||
} | ||
return content; | ||
}; | ||
return function(content) { | ||
// Parse arguments | ||
var query = loaderUtils.parseQuery(this.query); | ||
var root = query.root; | ||
var attributes = ['img:src']; | ||
if (_.isObject(query)) { | ||
root = query.root; | ||
// Apply template settings | ||
_.each(_.pick(query, 'interpolate', 'escape', 'evaluate', 'attributes', 'prependFilenameComment'), function(value, key) { | ||
_.each(_.pick(query, 'interpolate', 'escape', 'evaluate'), function(value, key) { | ||
_.templateSettings[key] = new RegExp(value, 'g'); | ||
@@ -61,77 +44,46 @@ }); | ||
// Set include regex | ||
if (query.includeRegex !== undefined) { | ||
includeRegex = new RegExp(query.includeRegex, 'g'); | ||
// Parse / ignore macros | ||
if (query.parseMacros !== undefined) { | ||
parseMacros = !!query.parseMacros; | ||
} | ||
} | ||
// Generates a random string for further proccessing | ||
var randomIdent = function() { | ||
return "@@@URL" + Math.random() + "@@@"; | ||
}; | ||
// Obtain external resource links | ||
var links = attributeParser(content, function(tag, attr) { | ||
return attributes.indexOf(tag + ':' + attr) >= 0; | ||
}); | ||
links.reverse(); | ||
// Parse external resources | ||
var data = {}; | ||
content = [content]; | ||
links.forEach(function(link) { | ||
// Ignore absolute paths | ||
if (/^\//.exec(link.value) && root == false) { | ||
return; | ||
// Prepend a html comment with the filename in it | ||
if (query.prependFilenameComment) { | ||
var filename = loaderUtils.getRemainingRequest(this); | ||
var filenameRelative = path.relative(query.prependFilenameComment, filename); | ||
content = "\n<!-- " + filenameRelative + " -->\n" + content; | ||
} | ||
} | ||
if (!loaderUtils.isUrlRequest(link.value, root)) { | ||
return; | ||
} | ||
// Include additional macros | ||
if (_.isObject(this.options.macros)) { | ||
_.extend(macros, this.options.macros); | ||
} | ||
var uri = url.parse(link.value); | ||
if (uri.hash !== null && uri.hash !== undefined) { | ||
uri.hash = null; | ||
link.value = uri.format(); | ||
link.length = link.value.length; | ||
} | ||
// Parse macros | ||
if (parseMacros) { | ||
var macrosContext = macroParser(content, function (macro) { | ||
return _.isFunction(macros[macro]); | ||
}, 'MACRO'); | ||
content = macrosContext.replaceMatches(content); | ||
} | ||
do { | ||
var ident = randomIdent(); | ||
} while (data[ident]); | ||
// Parse attributes | ||
var attributesContext = attributeParser(content, function (tag, attr) { | ||
return attributes.indexOf(tag + ':' + attr) != -1; | ||
}, 'ATTRIBUTE', root); | ||
content = attributesContext.replaceMatches(content); | ||
data[ident] = link.value; | ||
var x = content.pop(); | ||
content.push(x.substr(link.start + link.length)); | ||
content.push(ident); | ||
content.push(x.substr(0, link.start)); | ||
}); | ||
content.reverse(); | ||
content = content.join(''); | ||
// Compile template | ||
var source = _.template(content).source; | ||
this.cacheable && this.cacheable(); | ||
var callback = this.async(); | ||
// Read file content | ||
content = readContent(content, this.context); | ||
// Prepend a html comment with the filename in it | ||
if (query.prependFilenameComment) { | ||
var filename = loaderUtils.getRemainingRequest(this); | ||
var filenameRelative = path.relative(query.prependFilenameComment, filename); | ||
content = "\n<!-- " + filenameRelative + " -->\n" + content; | ||
// Resolve macros | ||
if (parseMacros) { | ||
source = macrosContext.resolveMacros(source, macros); | ||
} | ||
// Replace random generated strings with require | ||
var source = _.template(content).source; | ||
content = source.replace(/@@@URL[0-9\.]+@@@/g, function(match) { | ||
if (!data[match]) { | ||
return match; | ||
} | ||
// Resolve attributes | ||
source = attributesContext.resolveAttributes(source); | ||
return "' + require(" + JSON.stringify(loaderUtils.urlToRequest(data[match], root)) + ") + '"; | ||
}); | ||
callback(null, "module.exports = " + content + ";"); | ||
callback(null, "module.exports = " + source + ";"); | ||
}; | ||
@@ -138,0 +90,0 @@ }(); |
{ | ||
"name": "underscore-template-loader", | ||
"version": "0.4.1", | ||
"version": "0.5.0", | ||
"description": "An Underscore and Lodash template loader for Webpack", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
310
README.md
@@ -6,36 +6,38 @@ underscore-template-loader | ||
<br/> | ||
<br> | ||
###Installation | ||
<br/> | ||
<br> | ||
```bash | ||
$ npm install underscore-template-loader | ||
npm install underscore-template-loader | ||
``` | ||
<br> | ||
Make sure you have the `underscore` or `lodash` package installed. | ||
<br/> | ||
<br> | ||
###Usage | ||
<br/> | ||
<br> | ||
```javascript | ||
module.exports = { | ||
//... | ||
loaders: [ | ||
//... | ||
{ test: /\.html$/, loader: "underscore-template-loader" } | ||
] | ||
module: { | ||
loaders: [ | ||
{ test: /\.html$/, loader: "underscore-template-loader" } | ||
] | ||
}, | ||
}; | ||
``` | ||
<br/> | ||
<br> | ||
####Loading templates | ||
<br/> | ||
<br> | ||
```html | ||
<!-- file: hello.html --> | ||
<!-- File: hello.html --> | ||
<p>Hello <%=name%></p> | ||
``` | ||
<br> | ||
```javascript | ||
@@ -46,23 +48,28 @@ var compiled = require('./hello.html'); | ||
<br/> | ||
<br> | ||
####Prepending filename comment | ||
<br> | ||
When debugging a large single page app with the DevTools, it's often hard to find the template that contains a bug. With the following config a HTML comment is prepended to the template with the relative path in it (e.g. `<!-- view/user/edit.html -->`). | ||
<br> | ||
```javascript | ||
module.exports = { | ||
//... | ||
loaders: [ | ||
//... | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
prependFilenameComment: __dirname, | ||
module: { | ||
loaders: [ | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
prependFilenameComment: __dirname, | ||
} | ||
} | ||
} | ||
] | ||
] | ||
} | ||
}; | ||
``` | ||
<br/> | ||
<br> | ||
####Template settings | ||
@@ -74,67 +81,102 @@ | ||
//... | ||
loaders: [ | ||
//... | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
interpolate : '\\{\\[(.+?)\\]\\}', | ||
evaluate: '\\{%([\\s\\S]+?)%\\}', | ||
escape : '\\{\\{(.+?)\\}\\}' | ||
} | ||
} | ||
] | ||
module: { | ||
loaders: [ | ||
//... | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
interpolate : '\\{\\[(.+?)\\]\\}', | ||
evaluate: '\\{%([\\s\\S]+?)%\\}', | ||
escape : '\\{\\{(.+?)\\}\\}' | ||
} | ||
} | ||
] | ||
} | ||
}; | ||
``` | ||
<br/> | ||
####Include tag | ||
<br> | ||
####Images | ||
<br/> | ||
<br> | ||
In order to load images you must install either the *file-loader* or the *url-loader* package. | ||
<br> | ||
```javascript | ||
module.exports = { | ||
//... | ||
module: { | ||
loaders: [ | ||
{ test: /\.html$/, loader: "underscore-template-loader" }, | ||
{ test: /\.jpg$/, loader: "file-loader" }, | ||
{ test: /\.png$/, loader: "url-loader?mimetype=image/png" }, | ||
] | ||
} | ||
}; | ||
``` | ||
<br> | ||
```html | ||
<!-- file: main.html --> | ||
<p>Hello, <@include message.html></p> | ||
<!-- Require image using file-loader --> | ||
<img src="img/portrait.jpg"> | ||
<!-- Require image using url-loader --> | ||
<img src="img/icon.png"> | ||
``` | ||
<br> | ||
Images with an absolute path are not translated unless a `root` argument is defined | ||
<br> | ||
```html | ||
<!-- file: message.html --> | ||
<em>how are you?</em> | ||
<!-- Using root = undefined => no translation --> | ||
<img src="/not_translated.jpg"> | ||
<!-- Using root = 'images' => require('images/image.jpg') --> | ||
<img src="/image.jpg"> | ||
``` | ||
<br/> | ||
Include tag can be overrided through the *includeRegex* argument. | ||
<br> | ||
In order to deactivate image processing define `attributes` as an empty array. | ||
<br/> | ||
```javascript | ||
module.exports = { | ||
//... | ||
loaders: [ | ||
//... | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
includeRegex: '<#include\\s+([\\/\\w\\.]*?[\\w]+\\.[\\w]+)>' | ||
} | ||
} | ||
] | ||
module: { | ||
loaders: [ | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
attributes: [] | ||
} | ||
} | ||
] | ||
} | ||
}; | ||
``` | ||
<br/> | ||
####Images | ||
<br> | ||
You could also add which attributes need to be processed in the form of pairs *tag:attribute*. | ||
<br/> | ||
In order to load images you must install either the *file-loader* or the *url-loader* packages. | ||
```javascript | ||
module.exports = { | ||
//... | ||
loaders: [ | ||
//... | ||
{ test: /\.html/, loader: "underscore-template-loader" }, | ||
{ test: /\.jpg$/, loader: "file-loader" }, | ||
{ test: /\.png$/, loader: "url-loader?mimetype=image/png" }, | ||
] | ||
module: { | ||
loaders: [ | ||
//... | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
attributes: ['img:src', 'x-img:src'] | ||
} | ||
} | ||
] | ||
} | ||
}; | ||
@@ -144,71 +186,125 @@ ``` | ||
<br> | ||
###Macros | ||
<br> | ||
Macros allow additional features like including templates or inserting custom text in compiled templates. | ||
<br> | ||
####The *require* macro | ||
<br> | ||
The `require` macro expects a path to a underscore template. The macro is then translated into a webpack require expression that evaluates the template using the same arguments. | ||
<br> | ||
```html | ||
<!-- Require image using file-loader --> | ||
<img src="img/portrait.jpg"> | ||
<h4>Profile</h4> | ||
<!-- Require image using url-loader --> | ||
<img src="img/icon.png"> | ||
Name: <strong><%=name%></strong> | ||
<br> | ||
Surname: <strong><%=surname%></strong> | ||
<div class="profile-details"> | ||
@require('profile-details.html') | ||
</div> | ||
``` | ||
<br/> | ||
Images with an absolute path are not translated unless a *root* argument is defined | ||
<br> | ||
####The *include* macro | ||
<br> | ||
While the `require` macro expects a resource that returns a function, the `include` macro can be used for resources that return plain text. For example, we can include text loaded through the `html-loader` directly in our template. | ||
```html | ||
<!-- Using root = undefined => no translation --> | ||
<img src="/not_translated.jpg"> | ||
<div class="wiki"> | ||
<h3>Introduction</h3> | ||
@include('intro.htm') | ||
<h3>Authors</h3> | ||
@include('authors.htm') | ||
</div> | ||
``` | ||
<!-- Using root = 'images' => require('images/image.jpg') --> | ||
<img src="/image.jpg"> | ||
<br> | ||
####'br' and 'nl' | ||
<br> | ||
The `br` and `nl` macros insert a `<br>` tag and a new line respectively. They accept a optional argument with the amount of strings to insert. | ||
```html | ||
<p>Lorem ipsum</p> | ||
@br(3) | ||
<p>Sit amet</p> | ||
@nl() | ||
``` | ||
<br> | ||
In order to deactivate image processing define *attributes* as an empty array. | ||
####Custom macros | ||
<br> | ||
We can include additional macros by defining them in the webpack configuration file. Remember that the value returned by a macro is inserted as plain javascript, so in order to insert a custom text we need to use nested quotes. For example, let's say that we want a macro that includes a copyright string in our template. | ||
<br> | ||
```javascript | ||
// File: webpack.config.js | ||
module.exports = { | ||
//... | ||
loaders: [ | ||
//... | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
attributes: [] | ||
} | ||
// ... | ||
module: { | ||
loaders: { | ||
// ... | ||
{ test: /\.html$/, loader: "underscore-template-loader" }, | ||
} | ||
] | ||
}; | ||
}, | ||
macros: { | ||
'copyright': function () { | ||
return "'<p>Copyright FakeCorp 2014 - 2015</p>'"; | ||
} | ||
} | ||
} | ||
``` | ||
<br/> | ||
<br> | ||
We then invoke this macro from within the template as usual. | ||
You could also add which attributes need to be processed in the form of pairs *tag:attribute*. | ||
<br> | ||
```html | ||
<footer> | ||
@copyright() | ||
</footer> | ||
``` | ||
<br> | ||
####Disabling macros | ||
<br> | ||
You can disable macros if you are a bit unsure about their usage or just simply want faster processing. This is achieved by setting the `parseMacros` options to false. | ||
<br> | ||
```javascript | ||
module.exports = { | ||
//... | ||
loaders: [ | ||
//... | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
attributes: ['img:src', 'x-img:src'] | ||
} | ||
// ... | ||
module: { | ||
loaders: { | ||
// ... | ||
{ | ||
test: /\.html$/, | ||
loader: "underscore-template-loader", | ||
query: { | ||
parseMacros: false | ||
} | ||
}, | ||
} | ||
] | ||
}; | ||
} | ||
} | ||
``` | ||
<br/> | ||
<br> | ||
####Known issues | ||
<br/> | ||
* Trying to use different template settings (interpolate, escape, evaluate) for different extensions. Underscore/Lodash template settings are defined globally. | ||
* Attributes not parsed on included templates (needs fix). | ||
<br> | ||
* Trying to use different template settings (interpolate, escape, evaluate) for different extensions. Underscore / Lodash template settings are defined globally. | ||
<br/> | ||
<br> | ||
###License | ||
Released under the MIT license. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
19355
8
305
307
0
1