fixed reporting errors in child templates
This commit is contained in:
@@ -99,7 +99,7 @@ open class SimpleErrorReporter: ErrorReporter {
|
|||||||
|
|
||||||
return TemplateSyntaxError(reason: (error as? TemplateSyntaxError)?.reason ?? "\(error)",
|
return TemplateSyntaxError(reason: (error as? TemplateSyntaxError)?.reason ?? "\(error)",
|
||||||
lexeme: (error as? TemplateSyntaxError)?.lexeme,
|
lexeme: (error as? TemplateSyntaxError)?.lexeme,
|
||||||
template: context.template,
|
template: (error as? TemplateSyntaxError)?.template ?? context.template,
|
||||||
parentError: (error as? TemplateSyntaxError)?.parentError
|
parentError: (error as? TemplateSyntaxError)?.parentError
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
class BlockContext {
|
class BlockContext {
|
||||||
class var contextKey: String { return "block_context" }
|
class var contextKey: String { return "block_context" }
|
||||||
|
|
||||||
var blocks: [String: [BlockNode]]
|
// contains mapping of block names to their nodes and templates where they are defined
|
||||||
|
var blocks: [String: [(BlockNode, Template?)]]
|
||||||
|
|
||||||
init(blocks: [String: BlockNode]) {
|
init(blocks: [String: (BlockNode, Template?)]) {
|
||||||
self.blocks = [:]
|
self.blocks = blocks.mapValues({ [$0] })
|
||||||
blocks.forEach { (key, value) in
|
|
||||||
self.blocks[key] = [value]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func push(_ block: BlockNode, forKey blockName: String) {
|
func pushBlock(_ block: BlockNode, named blockName: String, definedIn template: Template?) {
|
||||||
if var blocks = blocks[blockName] {
|
if var blocks = blocks[blockName] {
|
||||||
blocks.append(block)
|
blocks.append((block, template))
|
||||||
self.blocks[blockName] = blocks
|
self.blocks[blockName] = blocks
|
||||||
} else {
|
} else {
|
||||||
self.blocks[blockName] = [block]
|
self.blocks[blockName] = [(block, template)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pop(_ blockName: String) -> BlockNode? {
|
func popBlock(named blockName: String) -> (node: BlockNode, template: Template?)? {
|
||||||
if var blocks = blocks[blockName] {
|
if var blocks = blocks[blockName] {
|
||||||
let block = blocks.removeFirst()
|
let block = blocks.removeFirst()
|
||||||
if blocks.isEmpty {
|
if blocks.isEmpty {
|
||||||
@@ -87,28 +85,33 @@ class ExtendsNode : NodeType {
|
|||||||
throw TemplateSyntaxError("'\(self.templateName)' could not be resolved as a string")
|
throw TemplateSyntaxError("'\(self.templateName)' could not be resolved as a string")
|
||||||
}
|
}
|
||||||
|
|
||||||
let template = try context.environment.loadTemplate(name: templateName)
|
let baseTemplate = try context.environment.loadTemplate(name: templateName)
|
||||||
|
let template = context.environment.template
|
||||||
|
|
||||||
let blockContext: BlockContext
|
let blockContext: BlockContext
|
||||||
if let context = context[BlockContext.contextKey] as? BlockContext {
|
if let _blockContext = context[BlockContext.contextKey] as? BlockContext {
|
||||||
blockContext = context
|
blockContext = _blockContext
|
||||||
|
for (name, block) in blocks {
|
||||||
for (key, value) in blocks {
|
blockContext.pushBlock(block, named: name, definedIn: template)
|
||||||
blockContext.push(value, forKey: key)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
blockContext = BlockContext(blocks: blocks)
|
blockContext = BlockContext(blocks: blocks.mapValues({ ($0, template) }))
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
return try context.environment.pushTemplate(template, token: token) {
|
// pushes base template and renders it's content
|
||||||
|
// block_context contains all blocks from child templates
|
||||||
|
return try context.environment.pushTemplate(baseTemplate, token: token) {
|
||||||
try context.push(dictionary: [BlockContext.contextKey: blockContext]) {
|
try context.push(dictionary: [BlockContext.contextKey: blockContext]) {
|
||||||
return try template.render(context)
|
return try baseTemplate.render(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
if let parentError = error as? TemplateSyntaxError {
|
// if error template is already set (see catch in BlockNode)
|
||||||
throw TemplateSyntaxError(reason: parentError.reason, parentError: parentError)
|
// and it happend in the same template as current template
|
||||||
|
// there is no need to wrap it in another error
|
||||||
|
if let error = error as? TemplateSyntaxError, error.template !== context.environment.template {
|
||||||
|
throw TemplateSyntaxError(reason: error.reason, parentError: error)
|
||||||
} else {
|
} else {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -142,15 +145,31 @@ class BlockNode : NodeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func render(_ context: Context) throws -> String {
|
func render(_ context: Context) throws -> String {
|
||||||
if let blockContext = context[BlockContext.contextKey] as? BlockContext, let node = blockContext.pop(name) {
|
if let blockContext = context[BlockContext.contextKey] as? BlockContext, let child = blockContext.popBlock(named: name) {
|
||||||
|
// node is a block node from child template that extends this node (has the same name)
|
||||||
let newContext: [String: Any]
|
let newContext: [String: Any]
|
||||||
newContext = [
|
newContext = [
|
||||||
BlockContext.contextKey: blockContext,
|
BlockContext.contextKey: blockContext,
|
||||||
|
// render current node so that it's content can be used as part of node that extends it
|
||||||
"block": ["super": try self.render(context)]
|
"block": ["super": try self.render(context)]
|
||||||
]
|
]
|
||||||
|
// render extension node
|
||||||
|
do {
|
||||||
return try context.push(dictionary: newContext) {
|
return try context.push(dictionary: newContext) {
|
||||||
return try node.render(context)
|
return try child.node.render(context)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// child node belongs to child template, which is currently not on top of stack
|
||||||
|
// so we need to use node's template to report errors, not current template
|
||||||
|
// unless it's already set
|
||||||
|
if var error = error as? TemplateSyntaxError {
|
||||||
|
error.template = error.template ?? child.template
|
||||||
|
error.lexeme = error.lexeme ?? child.node.token
|
||||||
|
|
||||||
|
throw error
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -228,20 +228,10 @@ func testEnvironment() {
|
|||||||
var error = expectedSyntaxError(token: "include \"invalid-include.html\"", template: template, description: "Unknown filter 'unknown'")
|
var error = expectedSyntaxError(token: "include \"invalid-include.html\"", template: template, description: "Unknown filter 'unknown'")
|
||||||
error.parentError = parentError
|
error.parentError = parentError
|
||||||
|
|
||||||
|
|
||||||
try expect(environment.renderTemplate(string: template.templateString, context: ["target": "World"])).toThrow(error)
|
try expect(environment.renderTemplate(string: template.templateString, context: ["target": "World"])).toThrow(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
$0.it("reports syntax error in extended template") {
|
|
||||||
let template = try environment.loadTemplate(name: "invalid-child-super.html")
|
|
||||||
let baseTemplate = try environment.loadTemplate(name: "invalid-base.html")
|
|
||||||
|
|
||||||
let parentError = expectedSyntaxError(token: "target|unknown", template: baseTemplate, description: "Unknown filter 'unknown'")
|
|
||||||
var error = expectedSyntaxError(token: "extends \"invalid-base.html\"", template: template, description: "Unknown filter 'unknown'")
|
|
||||||
error.parentError = parentError
|
|
||||||
|
|
||||||
try expect(environment.render(template: template, context: ["target": "World"])).toThrow(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
$0.it("reports runtime error in included template") {
|
$0.it("reports runtime error in included template") {
|
||||||
var environment = environment
|
var environment = environment
|
||||||
let filterExtension = Extension()
|
let filterExtension = Extension()
|
||||||
@@ -260,23 +250,58 @@ func testEnvironment() {
|
|||||||
try expect(environment.renderTemplate(string: template.templateString, context: ["target": "World"])).toThrow(error)
|
try expect(environment.renderTemplate(string: template.templateString, context: ["target": "World"])).toThrow(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
$0.it("reports runtime error in extended template") {
|
$0.it("reports syntax error in base template") {
|
||||||
var environment = environment
|
let template = try environment.loadTemplate(name: "invalid-child-super.html")
|
||||||
let filterExtension = Extension()
|
let baseTemplate = try environment.loadTemplate(name: "invalid-base.html")
|
||||||
filterExtension.registerFilter("throw", filter: { (_: Any?) in
|
|
||||||
throw TemplateSyntaxError("filter error")
|
|
||||||
})
|
|
||||||
environment.extensions += [filterExtension]
|
|
||||||
|
|
||||||
let template = try environment.loadTemplate(name: "invalid-runtime-child-super.html")
|
let parentError = expectedSyntaxError(token: "target|unknown", template: baseTemplate, description: "Unknown filter 'unknown'")
|
||||||
let baseTemplate = try environment.loadTemplate(name: "invalid-runtime-base.html")
|
var error = expectedSyntaxError(token: "extends \"invalid-base.html\"", template: template, description: "Unknown filter 'unknown'")
|
||||||
|
|
||||||
let parentError = expectedSyntaxError(token: "target|throw", template: baseTemplate, description: "filter error")
|
|
||||||
var error = expectedSyntaxError(token: "extends \"invalid-runtime-base.html\"", template: template, description: "filter error")
|
|
||||||
error.parentError = parentError
|
error.parentError = parentError
|
||||||
|
|
||||||
try expect(environment.render(template: template, context: ["target": "World"])).toThrow(error)
|
try expect(environment.render(template: template, context: ["target": "World"])).toThrow(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$0.it("reports runtime error in base template") {
|
||||||
|
var environment = environment
|
||||||
|
let filterExtension = Extension()
|
||||||
|
filterExtension.registerFilter("unknown", filter: { (_: Any?) in
|
||||||
|
throw TemplateSyntaxError("filter error")
|
||||||
|
})
|
||||||
|
environment.extensions += [filterExtension]
|
||||||
|
|
||||||
|
let template = try environment.loadTemplate(name: "invalid-child-super.html")
|
||||||
|
let baseTemplate = try environment.loadTemplate(name: "invalid-base.html")
|
||||||
|
|
||||||
|
let parentError = expectedSyntaxError(token: "target|unknown", template: baseTemplate, description: "filter error")
|
||||||
|
var error = expectedSyntaxError(token: "extends \"invalid-base.html\"", template: template, description: "filter error")
|
||||||
|
error.parentError = parentError
|
||||||
|
|
||||||
|
try expect(environment.render(template: template, context: ["target": "World"])).toThrow(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
$0.it("reports syntax error in child template") {
|
||||||
|
let template = Template.init(templateString: "{% extends \"base.html\" %}\n" +
|
||||||
|
"{% block body %}Child {{ target|unknown }}{% endblock %}", environment: environment, name: nil)
|
||||||
|
let error = expectedSyntaxError(token: "target|unknown", template: template, description: "Unknown filter 'unknown'")
|
||||||
|
|
||||||
|
try expect(environment.render(template: template, context: ["target": "World"])).toThrow(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
$0.it("reports runtime error in child template") {
|
||||||
|
var environment = environment
|
||||||
|
let filterExtension = Extension()
|
||||||
|
filterExtension.registerFilter("unknown", filter: { (_: Any?) in
|
||||||
|
throw TemplateSyntaxError("filter error")
|
||||||
|
})
|
||||||
|
environment.extensions += [filterExtension]
|
||||||
|
|
||||||
|
let template = Template.init(templateString: "{% extends \"base.html\" %}\n" +
|
||||||
|
"{% block body %}Child {{ target|unknown }}{% endblock %}", environment: environment, name: nil)
|
||||||
|
let error = expectedSyntaxError(token: "{{ target|unknown }}", template: template, description: "filter error")
|
||||||
|
|
||||||
|
try expect(environment.render(template: template, context: ["target": "World"])).toThrow(error)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user