import 'dart:async'; import 'dart:mirrors'; import 'package:angel3_framework/angel3_framework.dart'; import 'package:file/file.dart'; import 'package:markdown/markdown.dart'; final RegExp _braces = RegExp(r'@?{{(((\\})|([^}]))+)}}'); /// 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`. AngelConfigurer markdown( Directory viewsDirectory, { String extension, ExtensionSet extensionSet, FutureOr Function(String content, Map locals) template, }) { extension ??= '.md'; extensionSet ??= ExtensionSet.gitHubWeb; return (Angel app) async { app.viewGenerator = (String name, [Map locals]) async { var file = viewsDirectory.childFile( viewsDirectory.fileSystem.path.setExtension(name, extension)); var contents = await file.readAsString(); contents = contents.replaceAllMapped(_braces, (m) { var text = m[0]; if (text.startsWith('@')) { // Raw braces return text.substring(1); } else { var expr = m[1]; var split = expr.split('.'); var root = split[0]; if (locals?.containsKey(root) != true) { throw UnimplementedError( 'Expected a local named "$root", but none was provided. Expression text: "$text"'); } return _resolveDotNotation(split, locals[root]).toString(); } }); var html = markdownToHtml(contents, extensionSet: extensionSet); if (template != null) html = await template(html, locals ?? {}); return html; }; }; } dynamic _resolveDotNotation(List split, target) { if (split.length == 1) return target; var mirror = reflect(target); for (var i = 1; i < split.length; i++) { mirror = mirror.getField(Symbol(split[i])); } return mirror.reflectee; }