Fixes #35 Add support for <compiler-options> tag

This commit is contained in:
Phillip Gates-Idem 2015-02-17 18:57:00 -05:00
parent d2956f1787
commit c9b062da45
8 changed files with 87 additions and 23 deletions

View File

@ -1154,7 +1154,17 @@ World</textarea</div>
The following options are available to control whitespace removal:
__Option 1)__ Disable whitespace removal using the `c-whitespace` attribute:
__Option 1)__ Disable whitespace removal using the `compiler-options` tag:
```html
<compiler-options whitespace="preserve" />
<div>
<img src="foo.jpg">
<img src="foo.jpg">
</div>
```
__Option 2)__ Disable whitespace removal using the `c-whitespace` attribute:
```html
<div c-whitespace="preserve">
@ -1163,13 +1173,13 @@ __Option 1)__ Disable whitespace removal using the `c-whitespace` attribute:
</div>
```
__Option 2)__ Disable _all_ whitespace removal by changing a compiler option
__Option 3)__ Disable _all_ whitespace removal by changing a compiler option
```javascript
require('marko/compiler').defaultOptions.preserveWhitespace = true;
```
__Option 3)__ Control whitespace removal for specific tags
__Option 4)__ Control whitespace removal for specific tags
```javascript
require('marko/compiler').defaultOptions.preserveWhitespace = {
@ -1179,7 +1189,7 @@ require('marko/compiler').defaultOptions.preserveWhitespace = {
};
```
__Option 4)__ Configured a custom tag to preserve whitespace
__Option 5)__ Configured a custom tag to preserve whitespace
Adding the `"preserve-whitespace": true` property to a tag definition will result in the Marko compiler preserving whitespace wherever that tag is encountered in a template.

View File

@ -44,8 +44,17 @@ function ParseTreeBuilder(taglibs) {
this.charProps = null;
this.nsStack = [];
this.compilerOptions = undefined;
}
var COMPILER_ATTRIBUTE_HANDLERS = {
'whitespace': function(attr, compilerOptions) {
if (attr.value === 'preserve') {
compilerOptions.preserveWhitespace = true;
}
}
};
ParseTreeBuilder.prototype = {
createPos: function(line, column) {
if (arguments.length === 1) {
@ -76,6 +85,10 @@ ParseTreeBuilder.prototype = {
this.parentNode = null;
this.nsStack = [];
// Put the compiler options into the rootNode so that
// TemplateCompiler has access to these
rootNode.compilerOptions = this.compilerOptions;
return rootNode;
},
@ -94,6 +107,24 @@ ParseTreeBuilder.prototype = {
},
handleStartElement: function(el, attributes) {
var self = this;
if (el.localName === 'compiler-options') {
attributes.forEach(function (attr) {
var attrLocalName = attr.localName;
var attrPrefix = attr.prefix;
var handler;
if (attrPrefix || ((handler = COMPILER_ATTRIBUTE_HANDLERS[attrLocalName]) === undefined)) {
var attrName = attrPrefix;
attrName = (attrName) ? attrName + ':' + attrLocalName : attrLocalName;
throw new Error('Invalid Marko compiler option: ' + attrName + ', Allowed: ' + Object.keys(COMPILER_ATTRIBUTE_HANDLERS));
}
handler(attr, self.compilerOptions || (self.compilerOptions = {}));
}, this);
return;
}
this.prevTextNode = null;
var namespaceMappings = this.nsStack.length ? Object.create(this.nsStack[this.nsStack.length-1]) : {};
@ -120,7 +151,7 @@ ParseTreeBuilder.prototype = {
return '';
}
}
var elNS = getNS(el);
var elementNode = new ElementNode(
@ -133,7 +164,7 @@ ParseTreeBuilder.prototype = {
if (this.parentNode) {
this.parentNode.appendChild(elementNode);
} else {
elementNode.setRoot(true);
if (!elNS && el.localName === 'template') {
@ -149,11 +180,15 @@ ParseTreeBuilder.prototype = {
var attrPrefix = attr.prefix;
elementNode.setAttributeNS(attrNS, attrLocalName, attr.value, attrPrefix);
}, this);
this.parentNode = elementNode;
},
handleEndElement: function() {
handleEndElement: function(elementName) {
if (elementName === 'compiler-options') {
return;
}
this.prevTextNode = null;
this.parentNode = this.parentNode.parentNode;
this.nsStack.pop();
@ -164,4 +199,4 @@ ParseTreeBuilder.prototype = {
}
};
module.exports = ParseTreeBuilder;
module.exports = ParseTreeBuilder;

View File

@ -89,7 +89,7 @@ ParseTreeBuilderHtml.prototype = {
_this.handleCharacters(decodeEntities(text));
},
onclosetag: function(name){
_this.handleEndElement();
_this.handleEndElement(name);
}
}, parserOptions);
parser.write(src);
@ -102,4 +102,4 @@ ParseTreeBuilderHtml.prototype = {
require('raptor-util').inherit(ParseTreeBuilderHtml, require('./ParseTreeBuilder'));
module.exports = ParseTreeBuilderHtml;
module.exports = ParseTreeBuilderHtml;

View File

@ -11,9 +11,9 @@ ParseTreeBuilderXml.prototype = {
getPos: function() {
var parser = this.parser;
var filePath = this.filePath;
var line = parser.line + 1;
return {
line: line,
column: parser.column,
@ -21,12 +21,12 @@ ParseTreeBuilderXml.prototype = {
toString: function() {
return this.filePath + ":" + this.line + ":" + this.column;
}
};
},
doParse: function (src, filePath) {
this.filePath = filePath;
var parser = this.parser = sax.parser(true /*strict*/, {
trim: false,
@ -36,12 +36,12 @@ ParseTreeBuilderXml.prototype = {
});
var _this = this;
extend(parser, {
onerror: function(e) {
throw e;
},
ontext: function(text) {
text = text.replace(/\r\n|\r/g, "\n");
_this.handleCharacters(text);
@ -51,7 +51,7 @@ ParseTreeBuilderXml.prototype = {
text = text.replace(/\r\n|\r/g, "\n");
_this.handleCharacters(text);
},
onopentag: function (node) {
var el = {
namespace: node.uri,
@ -72,10 +72,10 @@ ParseTreeBuilderXml.prototype = {
_this.handleStartElement(el, attributes);
},
onclosetag: function () {
_this.handleEndElement();
onclosetag: function (name) {
_this.handleEndElement(name);
},
oncomment: function (comment) {
@ -88,4 +88,4 @@ ParseTreeBuilderXml.prototype = {
require('raptor-util').inherit(ParseTreeBuilderXml, require('./ParseTreeBuilder'));
module.exports = ParseTreeBuilderXml;
module.exports = ParseTreeBuilderXml;

View File

@ -25,6 +25,7 @@ var ok = require('assert').ok;
var attributeParser = require('./attribute-parser');
var expressionParser = require('./expression-parser');
var inherit = require('raptor-util/inherit');
var extend = require('raptor-util/extend');
var _Node = require('./Node');
var ElementNode = require('./ElementNode');
var TextNode = require('./TextNode');
@ -115,6 +116,12 @@ TemplateCompiler.prototype = {
* First build the parse tree for the tempate
*/
rootNode = parser.parse(src, filePath, this.taglibs);
if (rootNode.compilerOptions) {
// compiler options were set in the template so use those here
this.options = extend(extend({}, this.options), rootNode.compilerOptions);
}
//Build a parse tree from the input XML
templateBuilder = new TemplateBuilder(this, filePath, rootNode);
//The templateBuilder object is need to manage the compiled JavaScript output

View File

@ -147,6 +147,10 @@ describe('marko/marko' , function() {
testRender("test-project/html-templates/whitespace3.marko", {}, done);
});
it("should preserve whitespace using <compiler-options>", function(done) {
testRender("test-project/html-templates/whitespace4.marko", {}, done);
});
it("should handle whitespace correctly for mixed text and element children", function(done) {
testRender("test-project/html-templates/whitespace-inline-elements.marko", {}, done);
});

View File

@ -0,0 +1,4 @@
<compiler-options whitespace="preserve" />
A
B
C

View File

@ -0,0 +1,4 @@
A
B
C