2017-09-30 23:01:30 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:angel_framework/angel_framework.dart';
|
|
|
|
import 'package:code_buffer/code_buffer.dart';
|
|
|
|
import 'package:file/file.dart';
|
|
|
|
import 'package:jael/jael.dart';
|
|
|
|
import 'package:jael_preprocessor/jael_preprocessor.dart';
|
|
|
|
import 'package:symbol_table/symbol_table.dart';
|
|
|
|
|
2017-10-01 03:14:44 +00:00
|
|
|
/// Configures an Angel server to use Jael to render templates.
|
|
|
|
///
|
|
|
|
/// To enable "minified" output, you need to override the [createBuffer] function,
|
|
|
|
/// to instantiate a [CodeBuffer] that emits no spaces or line breaks.
|
2017-10-02 16:12:51 +00:00
|
|
|
///
|
|
|
|
/// To apply additional transforms to parsed documents, provide a set of [patch] functions.
|
2017-09-30 23:01:30 +00:00
|
|
|
AngelConfigurer jael(Directory viewsDirectory,
|
2017-10-02 16:12:51 +00:00
|
|
|
{String fileExtension, bool cacheViews: false, Iterable<Patcher> patch, CodeBuffer createBuffer()}) {
|
2017-10-01 03:14:44 +00:00
|
|
|
var cache = <String, Document>{};
|
2017-09-30 23:01:30 +00:00
|
|
|
fileExtension ??= '.jl';
|
|
|
|
createBuffer ??= () => new CodeBuffer();
|
|
|
|
|
|
|
|
return (Angel app) async {
|
|
|
|
app.viewGenerator = (String name, [Map locals]) async {
|
|
|
|
var errors = <JaelError>[];
|
2017-10-01 03:14:44 +00:00
|
|
|
Document processed;
|
|
|
|
|
|
|
|
if (cacheViews == true && cache.containsKey(name)) {
|
|
|
|
processed = cache[name];
|
|
|
|
} else {
|
|
|
|
var file = viewsDirectory.childFile(name + fileExtension);
|
|
|
|
var contents = await file.readAsString();
|
|
|
|
var doc =
|
|
|
|
parseDocument(contents, sourceUrl: file.uri, onError: errors.add);
|
|
|
|
processed = doc;
|
|
|
|
|
|
|
|
try {
|
2017-10-02 16:12:51 +00:00
|
|
|
processed = await resolve(doc, viewsDirectory, patch: patch, onError: errors.add);
|
2017-10-01 03:14:44 +00:00
|
|
|
} catch (_) {
|
|
|
|
// Ignore these errors, so that we can show syntax errors.
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cacheViews == true) {
|
|
|
|
cache[name] = processed;
|
|
|
|
}
|
2017-09-30 23:01:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var buf = createBuffer();
|
|
|
|
var scope = new SymbolTable(values: locals ?? {});
|
|
|
|
|
|
|
|
if (errors.isEmpty) {
|
|
|
|
try {
|
|
|
|
const Renderer().render(processed, buf, scope);
|
|
|
|
return buf.toString();
|
|
|
|
} on JaelError catch (e) {
|
|
|
|
errors.add(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
buf
|
|
|
|
..writeln('<!DOCTYPE html>')
|
|
|
|
..writeln('<html lang="en">')
|
|
|
|
..indent()
|
|
|
|
..writeln('<head>')
|
|
|
|
..indent()
|
|
|
|
..writeln(
|
|
|
|
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
|
|
)
|
|
|
|
..writeln('<title>${errors.length} Error(s)</title>')
|
|
|
|
..outdent()
|
|
|
|
..writeln('</head>')
|
|
|
|
..writeln('<body>')
|
|
|
|
..writeln('<h1>${errors.length} Error(s)</h1>')
|
|
|
|
..writeln('<ul>')
|
|
|
|
..indent();
|
|
|
|
|
|
|
|
for (var error in errors) {
|
|
|
|
var type =
|
|
|
|
error.severity == JaelErrorSeverity.warning ? 'warning' : 'error';
|
|
|
|
buf
|
|
|
|
..writeln('<li>')
|
|
|
|
..indent()
|
|
|
|
..writeln(
|
|
|
|
'<b>$type:</b> ${error.span.start.toolString}: ${error.message}')
|
|
|
|
..writeln('<br>')
|
|
|
|
..writeln(
|
|
|
|
'<span style="color: red;">' +
|
|
|
|
HTML_ESCAPE
|
|
|
|
.convert(error.span.highlight(color: false))
|
|
|
|
.replaceAll('\n', '<br>') +
|
|
|
|
'</span>',
|
|
|
|
)
|
|
|
|
..outdent()
|
|
|
|
..writeln('</li>');
|
|
|
|
}
|
|
|
|
|
|
|
|
buf
|
|
|
|
..outdent()
|
|
|
|
..writeln('</ul>')
|
|
|
|
..writeln('</body>')
|
|
|
|
..writeln('</html>');
|
|
|
|
|
|
|
|
return buf.toString();
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|