1.0.3
This commit is contained in:
parent
ececca6ec8
commit
1cc960044e
15 changed files with 61 additions and 39 deletions
|
@ -1,3 +1,8 @@
|
|||
# 1.0.3
|
||||
* Fix a scanner bug that prevented proper parsing of HTML nodes
|
||||
followed by free text.
|
||||
* Don't trim `<textarea>` content.
|
||||
|
||||
# 1.0.2
|
||||
* Use `package:dart2_constant`.
|
||||
* Upgrade `package:symbol_table`.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export 'src/ast/ast.dart';
|
||||
export 'src/text/parser.dart';
|
||||
export 'src/text/scanner.dart';
|
||||
export 'src/renderer.dart';
|
||||
export 'src/renderer.dart';
|
||||
|
|
|
@ -15,4 +15,4 @@ export 'member.dart';
|
|||
export 'new.dart';
|
||||
export 'number.dart';
|
||||
export 'string.dart';
|
||||
export 'token.dart';
|
||||
export 'token.dart';
|
||||
|
|
|
@ -2,4 +2,4 @@ import 'package:source_span/source_span.dart';
|
|||
|
||||
abstract class AstNode {
|
||||
FileSpan get span;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,6 @@ class Attribute extends AstNode {
|
|||
@override
|
||||
FileSpan get span {
|
||||
if (equals == null) return nameNode.span;
|
||||
return nameNode.span
|
||||
.expand(equals?.span ?? nequ.span)
|
||||
.expand(value.span);
|
||||
return nameNode.span.expand(equals?.span ?? nequ.span).expand(value.span);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ class Call extends Expression {
|
|||
.expand(rParen.span);
|
||||
}
|
||||
|
||||
List computePositional(SymbolTable scope) => arguments.map((e) => e.compute(scope)).toList();
|
||||
List computePositional(SymbolTable scope) =>
|
||||
arguments.map((e) => e.compute(scope)).toList();
|
||||
|
||||
Map<Symbol, dynamic> computeNamed(SymbolTable scope) {
|
||||
return namedArguments.fold<Map<Symbol, dynamic>>({}, (out, a) {
|
||||
|
|
|
@ -24,4 +24,4 @@ class Negation extends Expression {
|
|||
compute(SymbolTable scope) {
|
||||
return !(expression.compute(scope));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ class Identifier extends Expression {
|
|||
|
||||
@override
|
||||
compute(SymbolTable scope) {
|
||||
switch(name) {
|
||||
switch (name) {
|
||||
case 'null':
|
||||
return null;
|
||||
case 'true':
|
||||
|
@ -20,14 +20,11 @@ class Identifier extends Expression {
|
|||
default:
|
||||
var symbol = scope.resolve(name);
|
||||
if (symbol == null) {
|
||||
if (scope.resolve('!strict!')?.value == false)
|
||||
return null;
|
||||
if (scope.resolve('!strict!')?.value == false) return null;
|
||||
throw new ArgumentError(
|
||||
'The name "$name" does not exist in this scope.');
|
||||
}
|
||||
return scope
|
||||
.resolve(name)
|
||||
.value;
|
||||
return scope.resolve(name).value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,4 +58,4 @@ enum TokenType {
|
|||
hex,
|
||||
string,
|
||||
question,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,12 +50,12 @@ class Renderer {
|
|||
|
||||
for (var error in errors) {
|
||||
var type =
|
||||
error.severity == JaelErrorSeverity.warning ? 'warning' : 'error';
|
||||
error.severity == JaelErrorSeverity.warning ? 'warning' : 'error';
|
||||
buf
|
||||
..writeln('<li>')
|
||||
..indent()
|
||||
..writeln(
|
||||
'<b>$type:</b> ${error.span.start.toolString}: ${error.message}')
|
||||
..writeln('<b>$type:</b> ${error.span.start.toolString}: ${error
|
||||
.message}')
|
||||
..writeln('<br>')
|
||||
..writeln(
|
||||
'<span style="color: red;">' +
|
||||
|
@ -79,7 +79,8 @@ class Renderer {
|
|||
///
|
||||
/// If [strictResolution] is `false` (default: `true`), then undefined identifiers will return `null`
|
||||
/// instead of throwing.
|
||||
void render(Document document, CodeBuffer buffer, SymbolTable scope, {bool strictResolution: true}) {
|
||||
void render(Document document, CodeBuffer buffer, SymbolTable scope,
|
||||
{bool strictResolution: true}) {
|
||||
scope.create('!strict!', value: strictResolution != false);
|
||||
|
||||
if (document.doctype != null) buffer.writeln(document.doctype.span.text);
|
||||
|
@ -148,8 +149,8 @@ class Renderer {
|
|||
|
||||
for (int i = 0; i < element.children.length; i++) {
|
||||
var child = element.children.elementAt(i);
|
||||
renderElementChild(
|
||||
child, buffer, childScope, html5, i, element.children.length);
|
||||
renderElementChild(element, child, buffer, childScope, html5, i,
|
||||
element.children.length);
|
||||
}
|
||||
|
||||
buffer.writeln();
|
||||
|
@ -228,7 +229,7 @@ class Renderer {
|
|||
for (int i = 0; i < element.children.length; i++) {
|
||||
var child = element.children.elementAt(i);
|
||||
renderElementChild(
|
||||
child, buffer, scope, html5, i, element.children.length);
|
||||
element, child, buffer, scope, html5, i, element.children.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +251,8 @@ class Renderer {
|
|||
if (comparison == value) {
|
||||
for (int i = 0; i < child.children.length; i++) {
|
||||
var c = child.children.elementAt(i);
|
||||
renderElementChild(c, buffer, scope, html5, i, child.children.length);
|
||||
renderElementChild(
|
||||
element, c, buffer, scope, html5, i, child.children.length);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -263,15 +265,15 @@ class Renderer {
|
|||
if (defaultCase != null) {
|
||||
for (int i = 0; i < defaultCase.children.length; i++) {
|
||||
var child = defaultCase.children.elementAt(i);
|
||||
renderElementChild(
|
||||
child, buffer, scope, html5, i, defaultCase.children.length);
|
||||
renderElementChild(element, child, buffer, scope, html5, i,
|
||||
defaultCase.children.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void renderElementChild(ElementChild child, CodeBuffer buffer,
|
||||
void renderElementChild(Element parent, ElementChild child, CodeBuffer buffer,
|
||||
SymbolTable scope, bool html5, int index, int total) {
|
||||
if (child is Text) {
|
||||
if (child is Text && parent?.tagName != 'textarea') {
|
||||
if (index == 0)
|
||||
buffer.write(child.span.text.trimLeft());
|
||||
else if (index == total - 1)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
library jael.src.text.parselet;
|
||||
|
||||
import '../../ast/ast.dart';
|
||||
import '../parser.dart';
|
||||
part 'infix.dart';
|
||||
|
@ -11,4 +12,4 @@ abstract class PrefixParselet {
|
|||
abstract class InfixParselet {
|
||||
int get precedence;
|
||||
Expression parse(Parser parser, Expression left, Token token);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -108,10 +108,15 @@ class _Scanner implements Scanner {
|
|||
|
||||
var ch = _scanner.readChar();
|
||||
|
||||
if (ch == $lt && !_scanner.isDone) {
|
||||
if (ch == $lt) {
|
||||
// && !_scanner.isDone) {
|
||||
if (_scanner.matches('/')) {
|
||||
// If we reached "</", backtrack and break into HTML
|
||||
|
||||
openTags.removeFirst();
|
||||
_scanner.position--;
|
||||
state = _ScannerState.html;
|
||||
break;
|
||||
} else if (_scanner.matches(_id)) {
|
||||
// Also break when we reach <foo.
|
||||
//
|
||||
// HOWEVER, that is also JavaScript. So we must
|
||||
|
@ -164,7 +169,8 @@ class _Scanner implements Scanner {
|
|||
|
||||
do {
|
||||
// Only continue if we find a left bracket
|
||||
if (true) {// || _scanner.matches('<') || _scanner.matches('{{')) {
|
||||
if (true) {
|
||||
// || _scanner.matches('<') || _scanner.matches('{{')) {
|
||||
var potential = <Token>[];
|
||||
|
||||
while (true) {
|
||||
|
@ -207,9 +213,20 @@ class _Scanner implements Scanner {
|
|||
}
|
||||
} else if (token.type == TokenType.gt) {
|
||||
// Only pop the bracket if we're at foo>, </foo> or foo/>
|
||||
|
||||
if (brackets.isNotEmpty && brackets.first.type == TokenType.slash)
|
||||
if (brackets.isNotEmpty && brackets.first.type == TokenType.slash) {
|
||||
// </foo>
|
||||
brackets.removeFirst();
|
||||
|
||||
// Now, ONLY continue parsing HTML if the next character is '<'.
|
||||
var replay = _scanner.state;
|
||||
_scanner.scan(_whitespace);
|
||||
|
||||
if (!_scanner.matches('<')) {
|
||||
_scanner.state = replay;
|
||||
state = _ScannerState.freeText;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//else if (_scanner.matches('>')) brackets.removeFirst();
|
||||
else if (brackets.isNotEmpty &&
|
||||
brackets.first.type == TokenType.lt) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: jael
|
||||
version: 1.0.2
|
||||
version: 1.0.3
|
||||
description: A simple server-side HTML templating engine for Dart.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/jael/tree/master/jael
|
||||
|
|
|
@ -84,7 +84,7 @@ main() {
|
|||
Pokémon
|
||||
</h1>
|
||||
Darkrai - Dark
|
||||
<img>
|
||||
<img/>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
@ -250,13 +250,15 @@ main() {
|
|||
print(buf);
|
||||
|
||||
expect(
|
||||
buf.toString(),
|
||||
buf.toString().replaceAll('\n', '').replaceAll(' ', '').trim(),
|
||||
'''
|
||||
<div>
|
||||
<img src="<SCARY XSS>">
|
||||
<MORE SCARY XSS>
|
||||
</div>
|
||||
'''
|
||||
.replaceAll('\n', '')
|
||||
.replaceAll(' ', '')
|
||||
.trim());
|
||||
});
|
||||
|
||||
|
|
|
@ -83,10 +83,9 @@ main() {
|
|||
<script aria-label="script">
|
||||
window.alert('a string');
|
||||
</script>
|
||||
''',
|
||||
'''.trim(),
|
||||
sourceUrl: 'test.jl',
|
||||
)
|
||||
.tokens;
|
||||
).tokens;
|
||||
tokens.forEach(print);
|
||||
|
||||
expect(tokens, hasLength(11));
|
||||
|
|
Loading…
Reference in a new issue