Added html_builder package
This commit is contained in:
parent
7f365c1e8c
commit
c2fc88728b
17 changed files with 2443 additions and 2 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -19,3 +19,11 @@ doc/api/
|
|||
*.js_
|
||||
*.js.deps
|
||||
*.js.map
|
||||
|
||||
## VsCode
|
||||
.vscode/
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.metals/
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, dart-backend
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
|
11
README.md
11
README.md
|
@ -1 +1,10 @@
|
|||
# server-utilities
|
||||
# Belatuk Common Utilities
|
||||
|
||||
## About
|
||||
|
||||
This repository contains the common utility packages required for developing backend framework.
|
||||
|
||||
## Available Packages
|
||||
|
||||
* html_builder
|
||||
|
12
packages/html_builder/AUTHORS.md
Normal file
12
packages/html_builder/AUTHORS.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
Primary Authors
|
||||
===============
|
||||
|
||||
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||
|
||||
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||
code base to support NNBD.
|
||||
|
||||
* __[Tobe O](thosakwe@gmail.com)__
|
||||
|
||||
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||
is no longer involved with the project.
|
36
packages/html_builder/CHANGELOG.md
Normal file
36
packages/html_builder/CHANGELOG.md
Normal file
|
@ -0,0 +1,36 @@
|
|||
# Change Log
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Upgraded from `pendantic` to `lints` linter
|
||||
* Removed deprecated parameters
|
||||
* Published as `belatuk_html_builder` package
|
||||
|
||||
## 2.0.3
|
||||
|
||||
* Added an example
|
||||
* Updated README
|
||||
|
||||
## 2.0.2
|
||||
|
||||
* Run `dartfmt -w .`
|
||||
|
||||
## 2.0.1
|
||||
|
||||
* Added pedantic dart rules
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Migrated to work with Dart SDK 2.12.x NNBD
|
||||
|
||||
## 1.0.4
|
||||
|
||||
* Added `rebuild`, `rebuildRecursive`, and `NodeBuilder`.
|
||||
|
||||
## 1.0.3
|
||||
|
||||
* Dart 2 ready!
|
||||
|
||||
## 1.0.2
|
||||
|
||||
* Changed `h` and the `Node` constructor to take `Iterable`s of children, instead of just `List`s.
|
29
packages/html_builder/LICENSE
Normal file
29
packages/html_builder/LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
113
packages/html_builder/README.md
Normal file
113
packages/html_builder/README.md
Normal file
|
@ -0,0 +1,113 @@
|
|||
# Betaluk Html Builder
|
||||
|
||||
[![version](https://img.shields.io/badge/pub-v3.0.0-brightgreen)](https://pub.dartlang.org/packages/belatuk_html_builder)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![License](https://img.shields.io/github/license/dukefirehawk/angel)](https://github.com/dart-backend/belatuk-common-utilities/packages/html_builder/LICENSE)
|
||||
|
||||
**Replacement of `package:html_builder` with breaking changes to support NNBD.**
|
||||
|
||||
This package builds HTML AST's and renders them to HTML. It can be used as an internal DSL, i.e. for a templating engine.
|
||||
|
||||
## Installation
|
||||
|
||||
In your `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
belatuk_html_builder: ^2.0.0
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```dart
|
||||
import 'package:belatuk_html_builder/belatuk_html_builder.dart';
|
||||
|
||||
void main() {
|
||||
// Akin to React.createElement(...);
|
||||
var $el = h('my-element', p: {}, c: []);
|
||||
|
||||
// Attributes can be plain Strings.
|
||||
h('foo', p: {
|
||||
'bar': 'baz'
|
||||
});
|
||||
|
||||
// Null attributes do not appear.
|
||||
h('foo', p: {
|
||||
'does-not-appear': null
|
||||
});
|
||||
|
||||
// If an attribute is a bool, then it will only appear if its value is true.
|
||||
h('foo', p: {
|
||||
'appears': true,
|
||||
'does-not-appear': false
|
||||
});
|
||||
|
||||
// Or, a String or Map.
|
||||
h('foo', p: {
|
||||
'style': 'background-color: white; color: red;'
|
||||
});
|
||||
|
||||
h('foo', p: {
|
||||
'style': {
|
||||
'background-color': 'white',
|
||||
'color': 'red'
|
||||
}
|
||||
});
|
||||
|
||||
// Or, a String or Iterable.
|
||||
h('foo', p: {
|
||||
'class': 'a b'
|
||||
});
|
||||
|
||||
h('foo', p: {
|
||||
'class': ['a', 'b']
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Standard HTML5 elements:
|
||||
|
||||
```dart
|
||||
import 'package:belatuk_html_builder/elements.dart';
|
||||
|
||||
void main() {
|
||||
var $dom = html(lang: 'en', c: [
|
||||
head(c: [
|
||||
title(c: [text('Hello, world!')])
|
||||
]),
|
||||
body(c: [
|
||||
h1(c: [text('Hello, world!')]),
|
||||
p(c: [text('Ok')])
|
||||
])
|
||||
]);
|
||||
}
|
||||
```
|
||||
|
||||
Rendering to HTML:
|
||||
|
||||
```dart
|
||||
String html = StringRenderer().render($dom);
|
||||
```
|
||||
|
||||
Example with the [Angel3](https://pub.dev/packages/angel3_framework) backend framework,
|
||||
which has [dedicated html_builder support](https://github.com/dukefirehawk/angel/tree/html):
|
||||
|
||||
```dart
|
||||
import 'dart:io';
|
||||
import 'package:belatuk_framework/belatuk_framework.dart';
|
||||
import 'package:belatuk_html_builder/elements.dart';
|
||||
|
||||
configureViews(Angel app) async {
|
||||
app.get('/foo/:id', (req, res) async {
|
||||
var foo = await app.service('foo').read(req.params['id']);
|
||||
return html(c: [
|
||||
head(c: [
|
||||
title(c: [text(foo.name)])
|
||||
]),
|
||||
body(c: [
|
||||
h1(c: [text(foo.name)])
|
||||
])
|
||||
]);
|
||||
});
|
||||
}
|
||||
```
|
1
packages/html_builder/analysis_options.yaml
Normal file
1
packages/html_builder/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
include: package:lints/recommended.yaml
|
15
packages/html_builder/example/main.dart
Normal file
15
packages/html_builder/example/main.dart
Normal file
|
@ -0,0 +1,15 @@
|
|||
import 'package:belatuk_html_builder/elements.dart';
|
||||
|
||||
void main() {
|
||||
var dom = html(lang: 'en', c: [
|
||||
head(c: [
|
||||
title(c: [text('Hello, world!')])
|
||||
]),
|
||||
body(c: [
|
||||
h1(c: [text('Hello, world!')]),
|
||||
p(c: [text('Ok')])
|
||||
])
|
||||
]);
|
||||
|
||||
print(dom);
|
||||
}
|
4
packages/html_builder/lib/belatuk_html_builder.dart
Normal file
4
packages/html_builder/lib/belatuk_html_builder.dart
Normal file
|
@ -0,0 +1,4 @@
|
|||
export 'src/mutations.dart';
|
||||
export 'src/node.dart';
|
||||
export 'src/node_builder.dart';
|
||||
export 'src/renderer.dart';
|
1841
packages/html_builder/lib/elements.dart
Normal file
1841
packages/html_builder/lib/elements.dart
Normal file
File diff suppressed because it is too large
Load diff
20
packages/html_builder/lib/src/mutations.dart
Normal file
20
packages/html_builder/lib/src/mutations.dart
Normal file
|
@ -0,0 +1,20 @@
|
|||
import 'node.dart';
|
||||
import 'node_builder.dart';
|
||||
|
||||
/// Returns a function that rebuilds an arbitrary [Node] by applying the [transform] to it.
|
||||
Node Function(Node) rebuild(NodeBuilder Function(NodeBuilder) transform,
|
||||
{bool selfClosing = false}) {
|
||||
return (node) =>
|
||||
transform(NodeBuilder.from(node)).build(selfClosing: selfClosing);
|
||||
}
|
||||
|
||||
/// Applies [f] to all children of this node, recursively.
|
||||
///
|
||||
/// Use this alongside [rebuild].
|
||||
Node Function(Node) rebuildRecursive(Node Function(Node) f) {
|
||||
Node _build(Node node) {
|
||||
return NodeBuilder.from(f(node)).mapChildren(_build).build();
|
||||
}
|
||||
|
||||
return _build;
|
||||
}
|
63
packages/html_builder/lib/src/node.dart
Normal file
63
packages/html_builder/lib/src/node.dart
Normal file
|
@ -0,0 +1,63 @@
|
|||
import 'package:collection/collection.dart';
|
||||
|
||||
/// Shorthand function to generate a new [Node].
|
||||
Node h(String tagName,
|
||||
[Map<String, dynamic> attributes = const {},
|
||||
Iterable<Node> children = const []]) =>
|
||||
Node(tagName, attributes, children);
|
||||
|
||||
/// Represents an HTML node.
|
||||
class Node {
|
||||
final String tagName;
|
||||
final Map<String, dynamic> attributes = {};
|
||||
final List<Node> children = [];
|
||||
|
||||
Node(this.tagName,
|
||||
[Map<String, dynamic> attributes = const {},
|
||||
Iterable<Node> children = const []]) {
|
||||
this
|
||||
..attributes.addAll(attributes)
|
||||
..children.addAll(children);
|
||||
}
|
||||
|
||||
Node._selfClosing(this.tagName,
|
||||
[Map<String, dynamic> attributes = const {}]) {
|
||||
this.attributes.addAll(attributes);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(other) {
|
||||
return other is Node &&
|
||||
other.tagName == tagName &&
|
||||
const ListEquality<Node>().equals(other.children, children) &&
|
||||
const MapEquality<String, dynamic>()
|
||||
.equals(other.attributes, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a self-closing tag, i.e. `<br>`.
|
||||
class SelfClosingNode extends Node {
|
||||
/*
|
||||
@override
|
||||
final String tagName;
|
||||
|
||||
@override
|
||||
final Map<String, dynamic> attributes = {};
|
||||
*/
|
||||
|
||||
@override
|
||||
List<Node> get children => List<Node>.unmodifiable([]);
|
||||
|
||||
SelfClosingNode(tagName, [Map<String, dynamic> attributes = const {}])
|
||||
: super._selfClosing(tagName, attributes);
|
||||
}
|
||||
|
||||
/// Represents a text node.
|
||||
class TextNode extends Node {
|
||||
final String text;
|
||||
|
||||
TextNode(this.text) : super(':text');
|
||||
|
||||
@override
|
||||
bool operator ==(other) => other is TextNode && other.text == text;
|
||||
}
|
108
packages/html_builder/lib/src/node_builder.dart
Normal file
108
packages/html_builder/lib/src/node_builder.dart
Normal file
|
@ -0,0 +1,108 @@
|
|||
import 'node.dart';
|
||||
|
||||
/// Helper class to build nodes.
|
||||
class NodeBuilder {
|
||||
final String tagName;
|
||||
final Map<String, dynamic> attributes;
|
||||
final Iterable<Node> children;
|
||||
Node? _existing;
|
||||
|
||||
NodeBuilder(this.tagName,
|
||||
{this.attributes = const {}, this.children = const []});
|
||||
|
||||
/// Creates a [NodeBuilder] that just spits out an already-existing [Node].
|
||||
factory NodeBuilder.existing(Node existingNode) =>
|
||||
NodeBuilder(existingNode.tagName).._existing = existingNode;
|
||||
|
||||
factory NodeBuilder.from(Node node) => NodeBuilder(node.tagName,
|
||||
attributes: Map<String, dynamic>.from(node.attributes),
|
||||
children: List<Node>.from(node.children));
|
||||
|
||||
/// Builds the node.
|
||||
Node build({bool selfClosing = false}) =>
|
||||
_existing ??
|
||||
(selfClosing
|
||||
? SelfClosingNode(tagName, attributes)
|
||||
: Node(tagName, attributes, children));
|
||||
|
||||
/// Produce a modified copy of this builder.
|
||||
NodeBuilder change(
|
||||
{String? tagName,
|
||||
Map<String, dynamic>? attributes,
|
||||
Iterable<Node>? children}) {
|
||||
return NodeBuilder(tagName ?? this.tagName,
|
||||
attributes: attributes ?? this.attributes,
|
||||
children: children ?? this.children);
|
||||
}
|
||||
|
||||
NodeBuilder changeTagName(String tagName) => change(tagName: tagName);
|
||||
|
||||
NodeBuilder changeAttributes(Map<String, dynamic> attributes) =>
|
||||
change(attributes: attributes);
|
||||
|
||||
NodeBuilder changeChildren(Iterable<Node> children) =>
|
||||
change(children: children);
|
||||
|
||||
NodeBuilder changeAttributesMapped(
|
||||
Map<String, dynamic> Function(Map<String, dynamic>) f) {
|
||||
var map = Map<String, dynamic>.from(attributes);
|
||||
return changeAttributes(f(map));
|
||||
}
|
||||
|
||||
NodeBuilder changeChildrenMapped(Iterable<Node> Function(List<Node>) f) {
|
||||
var list = List<Node>.from(children);
|
||||
return changeChildren(f(list));
|
||||
}
|
||||
|
||||
NodeBuilder mapChildren(Node Function(Node) f) =>
|
||||
changeChildrenMapped((list) => list.map(f));
|
||||
|
||||
NodeBuilder mapAttributes(
|
||||
MapEntry<String, dynamic> Function(String, dynamic) f) =>
|
||||
changeAttributesMapped((map) => map.map(f));
|
||||
|
||||
NodeBuilder setAttribute(String name, dynamic value) =>
|
||||
changeAttributesMapped((map) => map..[name] = value);
|
||||
|
||||
NodeBuilder addChild(Node child) =>
|
||||
changeChildrenMapped((list) => list..add(child));
|
||||
|
||||
NodeBuilder removeChild(Node child) =>
|
||||
changeChildrenMapped((list) => list..remove(child));
|
||||
|
||||
NodeBuilder removeAttribute(String name) =>
|
||||
changeAttributesMapped((map) => map..remove(name));
|
||||
|
||||
NodeBuilder setId(String id) => setAttribute('id', id);
|
||||
|
||||
NodeBuilder setClassName(String className) =>
|
||||
setAttribute('class', className);
|
||||
|
||||
NodeBuilder setClasses(Iterable<String> classes) =>
|
||||
setClassName(classes.join(' '));
|
||||
|
||||
NodeBuilder setClassesMapped(Iterable<String> Function(List<String>) f) {
|
||||
var clazz = attributes['class'];
|
||||
var classes = <String>[];
|
||||
|
||||
if (clazz is String) {
|
||||
classes.addAll(clazz.split(' '));
|
||||
} else if (clazz is Iterable) {
|
||||
classes.addAll(clazz.map((s) => s.toString()));
|
||||
}
|
||||
|
||||
return setClasses(f(classes));
|
||||
}
|
||||
|
||||
NodeBuilder addClass(String className) => setClassesMapped(
|
||||
(classes) => classes.contains(className) ? classes : classes
|
||||
..add(className));
|
||||
|
||||
NodeBuilder removeClass(String className) =>
|
||||
setClassesMapped((classes) => classes..remove(className));
|
||||
|
||||
NodeBuilder toggleClass(String className) =>
|
||||
setClassesMapped((classes) => classes.contains(className)
|
||||
? (classes..remove(className))
|
||||
: (classes..add(className)));
|
||||
}
|
136
packages/html_builder/lib/src/renderer.dart
Normal file
136
packages/html_builder/lib/src/renderer.dart
Normal file
|
@ -0,0 +1,136 @@
|
|||
import 'node.dart';
|
||||
|
||||
/// An object that can render a DOM tree into another representation, i.e. a `String`.
|
||||
abstract class Renderer<T> {
|
||||
/// Renders a DOM tree into another representation.
|
||||
T render(Node rootNode);
|
||||
}
|
||||
|
||||
/// Renders a DOM tree into a HTML string.
|
||||
abstract class StringRenderer implements Renderer<String> {
|
||||
/// Initializes a new [StringRenderer].
|
||||
///
|
||||
/// If [html5] is not `false` (default: `true`), then self-closing elements will be rendered with a slash before the last angle bracket, ex. `<br />`.
|
||||
/// If [pretty] is `true` (default), then [whitespace] (default: `' '`) will be inserted between nodes.
|
||||
/// You can also provide a [doctype] (default: `html`).
|
||||
factory StringRenderer(
|
||||
{bool html5 = true,
|
||||
bool pretty = true,
|
||||
String doctype = 'html',
|
||||
String whitespace = ' '}) =>
|
||||
pretty == true
|
||||
? _PrettyStringRendererImpl(
|
||||
html5: html5 != false, doctype: doctype, whitespace: whitespace)
|
||||
: _StringRendererImpl(html5: html5 != false, doctype: doctype);
|
||||
}
|
||||
|
||||
class _StringRendererImpl implements StringRenderer {
|
||||
final String? doctype;
|
||||
final bool? html5;
|
||||
|
||||
_StringRendererImpl({this.html5, this.doctype});
|
||||
|
||||
void _renderInto(Node node, StringBuffer buf) {
|
||||
if (node is TextNode) {
|
||||
buf.write(node.text);
|
||||
} else {
|
||||
buf.write('<${node.tagName}');
|
||||
|
||||
node.attributes.forEach((k, v) {
|
||||
if (v == true) {
|
||||
buf.write(' $k');
|
||||
} else if (v == false || v == null) {
|
||||
// Ignore
|
||||
} else if (v is Iterable) {
|
||||
var val = v.join(' ').replaceAll('"', '\\"');
|
||||
buf.write(' $k="$val"');
|
||||
} else if (v is Map) {
|
||||
var val = v.keys
|
||||
.fold<String>('', (out, k) => out += '$k: ${v[k]};')
|
||||
.replaceAll('"', '\\"');
|
||||
buf.write(' $k="$val"');
|
||||
} else {
|
||||
var val = v.toString().replaceAll('"', '\\"');
|
||||
buf.write(' $k="$val"');
|
||||
}
|
||||
});
|
||||
|
||||
if (node is SelfClosingNode) {
|
||||
buf.write((html5 != false) ? '>' : '/>');
|
||||
} else {
|
||||
buf.write('>');
|
||||
node.children.forEach((child) => _renderInto(child, buf));
|
||||
buf.write('</${node.tagName}>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String render(Node rootNode) {
|
||||
var buf = StringBuffer();
|
||||
if (doctype?.isNotEmpty == true) buf.write('<!DOCTYPE $doctype>');
|
||||
_renderInto(rootNode, buf);
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
class _PrettyStringRendererImpl implements StringRenderer {
|
||||
final bool? html5;
|
||||
final String? doctype, whitespace;
|
||||
|
||||
_PrettyStringRendererImpl({this.html5, this.whitespace, this.doctype});
|
||||
|
||||
void _applyTabs(int tabs, StringBuffer buf) {
|
||||
for (var i = 0; i < tabs; i++) {
|
||||
buf.write(whitespace ?? ' ');
|
||||
}
|
||||
}
|
||||
|
||||
void _renderInto(int tabs, Node node, StringBuffer buf) {
|
||||
if (tabs > 0) buf.writeln();
|
||||
_applyTabs(tabs, buf);
|
||||
|
||||
if (node is TextNode) {
|
||||
buf.write(node.text);
|
||||
} else {
|
||||
buf.write('<${node.tagName}');
|
||||
|
||||
node.attributes.forEach((k, v) {
|
||||
if (v == true) {
|
||||
buf.write(' $k');
|
||||
} else if (v == false || v == null) {
|
||||
// Ignore
|
||||
} else if (v is Iterable) {
|
||||
var val = v.join(' ').replaceAll('"', '\\"');
|
||||
buf.write(' $k="$val"');
|
||||
} else if (v is Map) {
|
||||
var val = v.keys
|
||||
.fold<String>('', (out, k) => out += '$k: ${v[k]};')
|
||||
.replaceAll('"', '\\"');
|
||||
buf.write(' $k="$val"');
|
||||
} else {
|
||||
var val = v.toString().replaceAll('"', '\\"');
|
||||
buf.write(' $k="$val"');
|
||||
}
|
||||
});
|
||||
|
||||
if (node is SelfClosingNode) {
|
||||
buf.write((html5 != false) ? '>' : '/>');
|
||||
} else {
|
||||
buf.write('>');
|
||||
node.children.forEach((child) => _renderInto(tabs + 1, child, buf));
|
||||
buf.writeln();
|
||||
_applyTabs(tabs, buf);
|
||||
buf.write('</${node.tagName}>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String render(Node rootNode) {
|
||||
var buf = StringBuffer();
|
||||
if (doctype?.isNotEmpty == true) buf.writeln('<!DOCTYPE $doctype>');
|
||||
_renderInto(0, rootNode, buf);
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
12
packages/html_builder/pubspec.yaml
Normal file
12
packages/html_builder/pubspec.yaml
Normal file
|
@ -0,0 +1,12 @@
|
|||
name: belatuk_html_builder
|
||||
description: Build HTML AST's and render them to HTML. This can be used as an internal DSL, i.e. for a templating engine.
|
||||
version: 3.0.0
|
||||
homepage: https://github.com/dart-backend/belatuk-common-utilities/packages/html_builder
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
collection: ^1.15.0
|
||||
dev_dependencies:
|
||||
html: ^0.15.0
|
||||
test: ^1.17.4
|
||||
lints: ^1.0.1
|
34
packages/html_builder/test/render_test.dart
Normal file
34
packages/html_builder/test/render_test.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
import 'package:html/parser.dart' as html5;
|
||||
import 'package:belatuk_html_builder/elements.dart';
|
||||
import 'package:belatuk_html_builder/belatuk_html_builder.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
test('pretty', () {
|
||||
var $dom = html(
|
||||
lang: 'en',
|
||||
c: [
|
||||
head(c: [
|
||||
title(c: [text('Hello, world!')])
|
||||
]),
|
||||
body(
|
||||
p: {'unresolved': true},
|
||||
c: [
|
||||
h1(c: [text('Hello, world!')]),
|
||||
br(),
|
||||
hr(),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
|
||||
var rendered = StringRenderer().render($dom);
|
||||
print(rendered);
|
||||
|
||||
var $parsed = html5.parse(rendered);
|
||||
var $title = $parsed.querySelector('title')!;
|
||||
expect($title.text.trim(), 'Hello, world!');
|
||||
var $h1 = $parsed.querySelector('h1')!;
|
||||
expect($h1.text.trim(), 'Hello, world!');
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue