2017-08-13 04:24:38 +00:00
|
|
|
import 'dart:async';
|
|
|
|
import 'dart:mirrors';
|
2021-06-21 06:33:18 +00:00
|
|
|
import 'package:angel3_framework/angel3_framework.dart';
|
2019-04-11 16:19:12 +00:00
|
|
|
import 'package:file/file.dart';
|
2017-08-13 04:24:38 +00:00
|
|
|
import 'package:markdown/markdown.dart';
|
|
|
|
|
2021-06-20 12:37:20 +00:00
|
|
|
final RegExp _braces = RegExp(r'@?{{(((\\})|([^}]))+)}}');
|
2017-08-13 04:24:38 +00:00
|
|
|
|
|
|
|
/// Configures an [Angel] instance to render Markdown templates from the specified [viewsDirectory].
|
|
|
|
///
|
|
|
|
/// The default [extension] is `.md`. To search for a different file extension, provide a new one.
|
|
|
|
/// By default, an [extensionSet] is provided that renders Github-flavored Markdown. This can also be overridden.
|
|
|
|
///
|
|
|
|
/// In many cases, Markdown content will be rendered within a larger [template] that styles the entire website.
|
|
|
|
/// To wrap generated Markdown content in a template, provide a function that accepts a generated HTML String,
|
|
|
|
/// and returns a String, or a `Future<String>`.
|
|
|
|
AngelConfigurer markdown(
|
|
|
|
Directory viewsDirectory, {
|
2021-06-21 15:04:36 +00:00
|
|
|
String? extension,
|
|
|
|
ExtensionSet? extensionSet,
|
|
|
|
FutureOr<String> Function(String content, Map<String, dynamic> locals)?
|
2021-06-20 12:37:20 +00:00
|
|
|
template,
|
2017-08-13 04:24:38 +00:00
|
|
|
}) {
|
|
|
|
extension ??= '.md';
|
2019-04-11 16:19:12 +00:00
|
|
|
extensionSet ??= ExtensionSet.gitHubWeb;
|
2017-08-13 04:24:38 +00:00
|
|
|
|
|
|
|
return (Angel app) async {
|
2021-06-21 15:04:36 +00:00
|
|
|
app.viewGenerator = (String name, [Map<String, dynamic>? locals]) async {
|
2019-04-11 16:19:12 +00:00
|
|
|
var file = viewsDirectory.childFile(
|
2021-06-21 15:04:36 +00:00
|
|
|
viewsDirectory.fileSystem.path.setExtension(name, extension!));
|
2017-08-13 04:24:38 +00:00
|
|
|
var contents = await file.readAsString();
|
|
|
|
|
|
|
|
contents = contents.replaceAllMapped(_braces, (m) {
|
2021-06-21 15:04:36 +00:00
|
|
|
var text = m[0]!;
|
2017-08-13 04:24:38 +00:00
|
|
|
|
|
|
|
if (text.startsWith('@')) {
|
|
|
|
// Raw braces
|
|
|
|
return text.substring(1);
|
|
|
|
} else {
|
2021-06-21 15:04:36 +00:00
|
|
|
var expr = m[1]!;
|
2017-08-13 04:24:38 +00:00
|
|
|
var split = expr.split('.');
|
|
|
|
var root = split[0];
|
|
|
|
|
2021-06-20 12:37:20 +00:00
|
|
|
if (locals?.containsKey(root) != true) {
|
|
|
|
throw UnimplementedError(
|
2017-08-13 04:24:38 +00:00
|
|
|
'Expected a local named "$root", but none was provided. Expression text: "$text"');
|
2021-06-20 12:37:20 +00:00
|
|
|
}
|
2017-08-13 04:24:38 +00:00
|
|
|
|
2021-06-21 15:04:36 +00:00
|
|
|
return _resolveDotNotation(split, locals![root]).toString();
|
2017-08-13 04:24:38 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
var html = markdownToHtml(contents, extensionSet: extensionSet);
|
|
|
|
if (template != null) html = await template(html, locals ?? {});
|
|
|
|
return html;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-06-20 12:37:20 +00:00
|
|
|
dynamic _resolveDotNotation(List<String> split, target) {
|
2017-08-13 04:24:38 +00:00
|
|
|
if (split.length == 1) return target;
|
|
|
|
|
2021-06-20 12:37:20 +00:00
|
|
|
var mirror = reflect(target);
|
2017-08-13 04:24:38 +00:00
|
|
|
|
2021-06-20 12:37:20 +00:00
|
|
|
for (var i = 1; i < split.length; i++) {
|
|
|
|
mirror = mirror.getField(Symbol(split[i]));
|
2017-08-13 04:24:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return mirror.reflectee;
|
|
|
|
}
|