mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
Record error for invalid attributes
This commit is contained in:
parent
ef95ce6055
commit
6ecb5b32dd
@ -419,9 +419,18 @@ class Generator {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
addError(message) {
|
addError(message, code) {
|
||||||
|
ok('"message" is required');
|
||||||
|
|
||||||
var node = this._currentNode;
|
var node = this._currentNode;
|
||||||
this.context.addError(node, message);
|
|
||||||
|
if (typeof message === 'object') {
|
||||||
|
var errorInfo = message;
|
||||||
|
errorInfo.node = node;
|
||||||
|
this.context.addError(errorInfo);
|
||||||
|
} else {
|
||||||
|
this.context.addError({node, code, message});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,51 @@ var deresolve = require('./util/deresolve');
|
|||||||
var UniqueVars = require('./util/UniqueVars');
|
var UniqueVars = require('./util/UniqueVars');
|
||||||
var PosInfo = require('./util/PosInfo');
|
var PosInfo = require('./util/PosInfo');
|
||||||
|
|
||||||
|
class CompileError {
|
||||||
|
constructor(errorInfo, context) {
|
||||||
|
this.context = context;
|
||||||
|
this.node = errorInfo.node;
|
||||||
|
this.message = errorInfo.message;
|
||||||
|
this.code = errorInfo.code;
|
||||||
|
|
||||||
|
var pos = errorInfo.pos;
|
||||||
|
var endPos = errorInfo.endPos;
|
||||||
|
|
||||||
|
if (pos == null) {
|
||||||
|
pos = this.node && this.node.pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endPos == null) {
|
||||||
|
endPos = this.node && this.node.endPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos != null) {
|
||||||
|
pos = context.getPosInfo(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endPos != null) {
|
||||||
|
endPos = context.getPosInfo(endPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pos = pos;
|
||||||
|
this.endPos = endPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
var pos = this.pos;
|
||||||
|
if (pos) {
|
||||||
|
pos = '[' + pos + '] ';
|
||||||
|
} else {
|
||||||
|
pos = '';
|
||||||
|
}
|
||||||
|
var str = pos + this.message;
|
||||||
|
if (this.node) {
|
||||||
|
str += ' (' + this.node.toString() + ')';
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class CompileContext {
|
class CompileContext {
|
||||||
constructor(src, filename, builder) {
|
constructor(src, filename, builder) {
|
||||||
ok(typeof src === 'string', '"src" string is required');
|
ok(typeof src === 'string', '"src" string is required');
|
||||||
@ -25,6 +70,7 @@ class CompileContext {
|
|||||||
this._uniqueVars = new UniqueVars();
|
this._uniqueVars = new UniqueVars();
|
||||||
this._srcCharProps = null;
|
this._srcCharProps = null;
|
||||||
this._flags = {};
|
this._flags = {};
|
||||||
|
this._errors = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getPosInfo(pos) {
|
getPosInfo(pos) {
|
||||||
@ -46,8 +92,16 @@ class CompileContext {
|
|||||||
return this._flags.hasOwnProperty(name);
|
return this._flags.hasOwnProperty(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
addError(node, message) {
|
addError(errorInfo) {
|
||||||
throw new Error('addError() not fully implemented. Error: ' + message); // TODO
|
this._errors.push(new CompileError(errorInfo, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
hasErrors() {
|
||||||
|
return this._errors.length !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getErrors() {
|
||||||
|
return this._errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
getRequirePath(targetFilename) {
|
getRequirePath(targetFilename) {
|
||||||
|
|||||||
@ -84,6 +84,19 @@ class Compiler {
|
|||||||
var codeGenerator = new CodeGenerator(context);
|
var codeGenerator = new CodeGenerator(context);
|
||||||
codeGenerator.generateCode(transformedAST);
|
codeGenerator.generateCode(transformedAST);
|
||||||
|
|
||||||
|
if (context.hasErrors()) {
|
||||||
|
var errors = context.getErrors();
|
||||||
|
|
||||||
|
var message = 'An error occurred while trying to compile template at path "' + filename + '". Error(s) in template:\n';
|
||||||
|
for (var i = 0, len = errors.length; i < len; i++) {
|
||||||
|
let error = errors[i];
|
||||||
|
message += (i + 1) + ') ' + error.toString() + '\n';
|
||||||
|
}
|
||||||
|
var error = new Error(message);
|
||||||
|
error.errors = errors;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
var compiledSrc = codeGenerator.getCode();
|
var compiledSrc = codeGenerator.getCode();
|
||||||
return compiledSrc;
|
return compiledSrc;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,10 +27,7 @@ class HtmlJsParser {
|
|||||||
},
|
},
|
||||||
|
|
||||||
onopentag(event) {
|
onopentag(event) {
|
||||||
var tagName = event.tagName;
|
handlers.handleStartElement(event);
|
||||||
var argument = event.argument;
|
|
||||||
var attributes = event.attributes;
|
|
||||||
handlers.handleStartElement({tagName, argument, attributes});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onclosetag(event) {
|
onclosetag(event) {
|
||||||
@ -60,10 +57,6 @@ class HtmlJsParser {
|
|||||||
|
|
||||||
parser.parse(src);
|
parser.parse(src);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPos() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = HtmlJsParser;
|
module.exports = HtmlJsParser;
|
||||||
@ -1,5 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var ok = require('assert').ok;
|
var ok = require('assert').ok;
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
var COMPILER_ATTRIBUTE_HANDLERS = {
|
var COMPILER_ATTRIBUTE_HANDLERS = {
|
||||||
whitespace: function(attr, compilerOptions) {
|
whitespace: function(attr, compilerOptions) {
|
||||||
@ -25,6 +26,14 @@ function parseExpression(expression) {
|
|||||||
return expression;
|
return expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTaglibPath(taglibPath) {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return path.relative(process.cwd(), taglibPath);
|
||||||
|
} else {
|
||||||
|
return taglibPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Parser {
|
class Parser {
|
||||||
constructor(parserImpl) {
|
constructor(parserImpl) {
|
||||||
@ -135,8 +144,8 @@ class Parser {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var taglibLookup = context.taglibLookup;
|
||||||
var tagDef = context.taglibLookup.getTag(tagName);
|
var tagDef = taglibLookup.getTag(tagName);
|
||||||
if (tagDef) {
|
if (tagDef) {
|
||||||
var nodeFactoryFunc = tagDef.getNodeFactory();
|
var nodeFactoryFunc = tagDef.getNodeFactory();
|
||||||
if (nodeFactoryFunc) {
|
if (nodeFactoryFunc) {
|
||||||
@ -150,6 +159,24 @@ class Parser {
|
|||||||
|
|
||||||
node.pos = el.pos;
|
node.pos = el.pos;
|
||||||
|
|
||||||
|
// Validate the attributes
|
||||||
|
attributes.forEach((attr) => {
|
||||||
|
let attrName = attr.name;
|
||||||
|
let attrDef = taglibLookup.getAttribute(tagName, attrName);
|
||||||
|
if (!attrDef) {
|
||||||
|
if (tagDef) {
|
||||||
|
// var isAttrForTaglib = compiler.taglibs.isTaglib(attrUri);
|
||||||
|
//Tag doesn't allow dynamic attributes
|
||||||
|
context.addError({
|
||||||
|
node: node,
|
||||||
|
message: 'The tag "' + tagName + '" in taglib "' + getTaglibPath(tagDef.taglibId) + '" does not support attribute "' + attrName + '"'
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.parentNode.appendChild(node);
|
this.parentNode.appendChild(node);
|
||||||
|
|
||||||
this.stack.push({
|
this.stack.push({
|
||||||
|
|||||||
@ -39,7 +39,6 @@ class HtmlElement extends Node {
|
|||||||
let attrName = attr.name;
|
let attrName = attr.name;
|
||||||
let attrValue = attr.value;
|
let attrValue = attr.value;
|
||||||
|
|
||||||
|
|
||||||
if (attr.isLiteralValue()) {
|
if (attr.isLiteralValue()) {
|
||||||
var literalValue = attrValue.value;
|
var literalValue = attrValue.value;
|
||||||
if (typeof literalValue === 'boolean') {
|
if (typeof literalValue === 'boolean') {
|
||||||
@ -137,6 +136,11 @@ class HtmlElement extends Node {
|
|||||||
callback.call(thisObj, attributes[i]);
|
callback.call(thisObj, attributes[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
var tagName = this.tagName;
|
||||||
|
return '<' + tagName + '>';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = HtmlElement;
|
module.exports = HtmlElement;
|
||||||
@ -3,6 +3,7 @@ var Container = require('./Container');
|
|||||||
var ArrayContainer = require('./ArrayContainer');
|
var ArrayContainer = require('./ArrayContainer');
|
||||||
var ok = require('assert').ok;
|
var ok = require('assert').ok;
|
||||||
var extend = require('raptor-util/extend');
|
var extend = require('raptor-util/extend');
|
||||||
|
var inspect = require('util').inspect;
|
||||||
|
|
||||||
class Node {
|
class Node {
|
||||||
constructor(type) {
|
constructor(type) {
|
||||||
@ -56,7 +57,7 @@ class Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
return JSON.stringify(this, null, 2);
|
return inspect(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
@ -75,6 +76,17 @@ class Node {
|
|||||||
isDetached() {
|
isDetached() {
|
||||||
return this.container == null;
|
return this.container == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by the Node.js require('util').inspect function.
|
||||||
|
* We default to inspecting on the simplified version
|
||||||
|
* of this node that is the same version we use when
|
||||||
|
* serializing to JSON.
|
||||||
|
*/
|
||||||
|
inspect(depth, opts) {
|
||||||
|
// We inspect in the simplified version of this object t
|
||||||
|
return this.toJSON();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Node;
|
module.exports = Node;
|
||||||
@ -11,6 +11,10 @@ function autoTest(name, dir, run, options) {
|
|||||||
var expectedPath = path.join(dir, 'expected' + compareExtension);
|
var expectedPath = path.join(dir, 'expected' + compareExtension);
|
||||||
|
|
||||||
var actual = run(dir);
|
var actual = run(dir);
|
||||||
|
if (actual === '$PASS$') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var actualJSON = isJSON ? JSON.stringify(actual, null, 2) : null;
|
var actualJSON = isJSON ? JSON.stringify(actual, null, 2) : null;
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
|
|||||||
@ -24,7 +24,6 @@ describe('compiler/codegen', function() {
|
|||||||
var main = require(path.join(dir, 'index.js'));
|
var main = require(path.join(dir, 'index.js'));
|
||||||
var generateCodeFunc = main;
|
var generateCodeFunc = main;
|
||||||
var ast = generateCodeFunc(builder);
|
var ast = generateCodeFunc(builder);
|
||||||
|
|
||||||
var generator = createGenerator();
|
var generator = createGenerator();
|
||||||
generator.generateCode(ast);
|
generator.generateCode(ast);
|
||||||
return generator.getCode();
|
return generator.getCode();
|
||||||
|
|||||||
3
test/fixtures/marko-taglib.json
vendored
3
test/fixtures/marko-taglib.json
vendored
@ -2,5 +2,8 @@
|
|||||||
"<test-declared-attributes>": {
|
"<test-declared-attributes>": {
|
||||||
"renderer": "./taglib/test-declared-attributes/renderer.js",
|
"renderer": "./taglib/test-declared-attributes/renderer.js",
|
||||||
"@name": "string"
|
"@name": "string"
|
||||||
|
},
|
||||||
|
"<test-invalid-attr>": {
|
||||||
|
"@foo": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,10 +2,12 @@ var expect = require('chai').expect;
|
|||||||
|
|
||||||
exports.templateData = {};
|
exports.templateData = {};
|
||||||
|
|
||||||
exports.options = {
|
exports.checkError = function(e) {
|
||||||
handleCompileError: function(e) {
|
expect(Array.isArray(e.errors)).to.equal(true);
|
||||||
expect(e.toString()).to.contain('template.marko:1:0');
|
expect(e.errors.length).to.equal(1);
|
||||||
expect(e.toString()).to.contain('The tag "test-invalid-attr" in taglib');
|
|
||||||
expect(e.toString()).to.contain('does not support attribute "invalid"');
|
var message = e.toString();
|
||||||
}
|
expect(message).to.contain('template.marko:1:0');
|
||||||
|
expect(message).to.contain('The tag "test-invalid-attr" in taglib');
|
||||||
|
expect(message).to.contain('does not support attribute "invalid"');
|
||||||
};
|
};
|
||||||
@ -13,11 +13,30 @@ describe('render', function() {
|
|||||||
function run(dir) {
|
function run(dir) {
|
||||||
var templatePath = path.join(dir, 'template.marko');
|
var templatePath = path.join(dir, 'template.marko');
|
||||||
var mainPath = path.join(dir, 'test.js');
|
var mainPath = path.join(dir, 'test.js');
|
||||||
var template = marko.load(templatePath);
|
|
||||||
var main = require(mainPath);
|
var main = require(mainPath);
|
||||||
var templateData = main.templateData || {};
|
|
||||||
var html = template.renderSync(templateData);
|
if (main.checkError) {
|
||||||
return html;
|
var e;
|
||||||
|
|
||||||
|
try {
|
||||||
|
marko.load(templatePath);
|
||||||
|
} catch(_e) {
|
||||||
|
e = _e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!e) {
|
||||||
|
throw new Error('Error expected');
|
||||||
|
}
|
||||||
|
|
||||||
|
main.checkError(e);
|
||||||
|
return '$PASS$';
|
||||||
|
} else {
|
||||||
|
var template = marko.load(templatePath);
|
||||||
|
var templateData = main.templateData || {};
|
||||||
|
var html = template.renderSync(templateData);
|
||||||
|
return html;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
compareExtension: '.html'
|
compareExtension: '.html'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user