diff --git a/compiler/HtmlJsParser.js b/compiler/HtmlJsParser.js
index c963b66a9..406d1c08e 100644
--- a/compiler/HtmlJsParser.js
+++ b/compiler/HtmlJsParser.js
@@ -2,6 +2,10 @@
var htmljs = require('htmljs-parser');
class HtmlJsParser {
+ constructor(options) {
+ this.ignorePlaceholders = options && options.ignorePlaceholders === true;
+ }
+
parse(src, handlers) {
var listeners = {
onText(event) {
@@ -54,12 +58,11 @@ class HtmlJsParser {
// Document type: ""
- handlers.handleCharacters('');
+ handlers.handleDocumentType(event.value);
},
onDeclaration(event) {
- // Declaration (e.g. )
- handlers.handleCharacters('' + event.value + '?>');
+ handlers.handleDeclaration(event.value);
},
onComment(event) {
@@ -78,6 +81,7 @@ class HtmlJsParser {
};
var parser = this.parser = htmljs.createParser(listeners, {
+ ignorePlaceholders: this.ignorePlaceholders,
isOpenTagOnly: function(tagName) {
return handlers.isOpenTagOnly(tagName);
}
diff --git a/compiler/Parser.js b/compiler/Parser.js
index 421854008..4ed908b87 100644
--- a/compiler/Parser.js
+++ b/compiler/Parser.js
@@ -69,7 +69,7 @@ function mergeShorthandClassNames(el, shorthandClassNames, context) {
}
class Parser {
- constructor(parserImpl) {
+ constructor(parserImpl, options) {
ok(parserImpl, '"parserImpl" is required');
this.parserImpl = parserImpl;
@@ -77,6 +77,8 @@ class Parser {
this.prevTextNode = null;
this.stack = null;
+ this.raw = options && options.raw === true;
+
// The context gets provided when parse is called
// but we store it as part of the object so that the handler
// methods have access
@@ -133,27 +135,31 @@ class Parser {
argument = argument.value;
}
- if (tagNameExpression) {
- tagName = builder.parseExpression(tagNameExpression);
- } else if (tagName === 'marko-compiler-options') {
- attributes.forEach(function (attr) {
- let attrName = attr.name;
- let handler = COMPILER_ATTRIBUTE_HANDLERS[attrName];
+ var raw = this.raw;
- if (!handler) {
- context.addError({
- code: 'ERR_INVALID_COMPILER_OPTION',
- message: 'Invalid Marko compiler option of "' + attrName + '". Allowed: ' + Object.keys(COMPILER_ATTRIBUTE_HANDLERS).join(', '),
- pos: el.pos,
- node: el
- });
- return;
- }
+ if (!raw) {
+ if (tagNameExpression) {
+ tagName = builder.parseExpression(tagNameExpression);
+ } else if (tagName === 'marko-compiler-options') {
+ attributes.forEach(function (attr) {
+ let attrName = attr.name;
+ let handler = COMPILER_ATTRIBUTE_HANDLERS[attrName];
- handler(attr, context);
- });
+ if (!handler) {
+ context.addError({
+ code: 'ERR_INVALID_COMPILER_OPTION',
+ message: 'Invalid Marko compiler option of "' + attrName + '". Allowed: ' + Object.keys(COMPILER_ATTRIBUTE_HANDLERS).join(', '),
+ pos: el.pos,
+ node: el
+ });
+ return;
+ }
- return;
+ handler(attr, context);
+ });
+
+ return;
+ }
}
this.prevTextNode = null;
@@ -183,11 +189,14 @@ class Parser {
}
if (valid) {
- attrValue = replacePlaceholderEscapeFuncs(parsedExpression, context);
+ if (raw) {
+ attrValue = parsedExpression;
+ } else {
+ attrValue = replacePlaceholderEscapeFuncs(parsedExpression, context);
+ }
} else {
attrValue = null;
}
-
}
var attrDef = {
@@ -205,7 +214,14 @@ class Parser {
})
};
- var node = this.context.createNodeForEl(elDef);
+ var node;
+
+ if (raw) {
+ node = builder.htmlElement(elDef);
+ node.pos = elDef.pos;
+ } else {
+ node = this.context.createNodeForEl(elDef);
+ }
if (attributeParseErrors.length) {
@@ -214,15 +230,31 @@ class Parser {
});
}
- if (el.shorthandClassNames) {
- mergeShorthandClassNames(node, el.shorthandClassNames, context);
- }
- if (el.shorthandId) {
- if (node.hasAttribute('id')) {
- context.addError(node, 'A shorthand ID cannot be used in conjunction with the "id" attribute');
- } else {
- node.setAttributeValue('id', builder.parseExpression(el.shorthandId.value));
+ // TODO Retain the shorthand class names and IDs in raw mode
+ if (raw) {
+ if (el.shorthandId) {
+ let parsed = builder.parseExpression(el.shorthandId.value);
+ node.rawShorthandId = parsed.value;
+ }
+
+ if (el.shorthandClassNames) {
+ node.rawShorthandClassNames = el.shorthandClassNames.map((className) => {
+ let parsed = builder.parseExpression(className.value);
+ return parsed.value;
+ });
+ }
+ } else {
+ if (el.shorthandClassNames) {
+ mergeShorthandClassNames(node, el.shorthandClassNames, context);
+ }
+
+ if (el.shorthandId) {
+ if (node.hasAttribute('id')) {
+ context.addError(node, 'A shorthand ID cannot be used in conjunction with the "id" attribute');
+ } else {
+ node.setAttributeValue('id', builder.parseExpression(el.shorthandId.value));
+ }
}
}
@@ -252,12 +284,30 @@ class Parser {
var preserveComment = this.context.isPreserveComments() ||
isIEConditionalComment(comment);
- if (preserveComment) {
+ if (this.raw || preserveComment) {
var commentNode = builder.htmlComment(builder.literal(comment));
this.parentNode.appendChild(commentNode);
}
}
+ handleDeclaration(value) {
+ this.prevTextNode = null;
+
+ var builder = this.context.builder;
+
+ var declarationNode = builder.declaration(builder.literal(value));
+ this.parentNode.appendChild(declarationNode);
+ }
+
+ handleDocumentType(value) {
+ this.prevTextNode = null;
+
+ var builder = this.context.builder;
+
+ var docTypeNode = builder.documentType(builder.literal(value));
+ this.parentNode.appendChild(docTypeNode);
+ }
+
handleBodyTextPlaceholder(expression, escape) {
this.prevTextNode = null;
var builder = this.context.builder;
diff --git a/compiler/index.js b/compiler/index.js
index 073398cf4..90541ede4 100644
--- a/compiler/index.js
+++ b/compiler/index.js
@@ -6,8 +6,16 @@ var Parser = require('./Parser');
var HtmlJsParser = require('./HtmlJsParser');
var Builder = require('./Builder');
var extend = require('raptor-util/extend');
+var CompileContext = require('./CompileContext');
var NODE_ENV = process.env.NODE_ENV;
var defaultParser = new Parser(new HtmlJsParser());
+var rawParser = new Parser(
+ new HtmlJsParser({
+ ignorePlaceholders: true
+ }),
+ {
+ raw: true
+ });
var defaultOptions = {
/**
@@ -133,9 +141,15 @@ function clearCaches() {
exports.taglibLoader.clearCache();
}
+function parseRaw(templateSrc, filename) {
+ var context = new CompileContext(templateSrc, filename, Builder.DEFAULT_BUILDER);
+ return rawParser.parse(templateSrc, context);
+}
+
exports.createBuilder = createBuilder;
exports.compileFile = compileFile;
exports.compile = compile;
+exports.parseRaw = parseRaw;
exports.defaultOptions = defaultOptions;
exports.checkUpToDate = checkUpToDate;
exports.getLastModified = getLastModified;