Add 'packages/html/' from commit '62d29b45fde6a235a24b280c461d35bf1158585e'
git-subtree-dir: packages/html git-subtree-mainline:5834fbe416
git-subtree-split:62d29b45fd
This commit is contained in:
commit
a584b8ce02
11 changed files with 354 additions and 0 deletions
58
packages/html/.gitignore
vendored
Normal file
58
packages/html/.gitignore
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# See https://www.dartlang.org/tools/private-files.html
|
||||||
|
|
||||||
|
# Files and directories created by pub
|
||||||
|
.packages
|
||||||
|
.pub/
|
||||||
|
build/
|
||||||
|
# If you're building an application, you may want to check-in your pubspec.lock
|
||||||
|
pubspec.lock
|
||||||
|
|
||||||
|
# Directory created by dartdoc
|
||||||
|
# If you don't generate documentation locally you can remove this line.
|
||||||
|
doc/api/
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.xml
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
.dart_tool
|
6
packages/html/.idea/vcs.xml
Normal file
6
packages/html/.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
1
packages/html/.travis.yml
Normal file
1
packages/html/.travis.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
language: dart
|
2
packages/html/CHANGELOG.md
Normal file
2
packages/html/CHANGELOG.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# 2.0.0
|
||||||
|
* Angel 2 + Dart 2 updates.
|
21
packages/html/LICENSE
Normal file
21
packages/html/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 The Angel Framework
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
83
packages/html/README.md
Normal file
83
packages/html/README.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
# html
|
||||||
|
[![Pub](https://img.shields.io/pub/v/angel_html.svg)](https://pub.dartlang.org/packages/angel_html)
|
||||||
|
[![build status](https://travis-ci.org/angel-dart/html.svg)](https://travis-ci.org/angel-dart/html)
|
||||||
|
|
||||||
|
A plug-in that allows you to return html_builder AST's from request handlers, and have them sent as HTML automatically.
|
||||||
|
|
||||||
|
[`package:html_builder`](https://github.com/thosakwe/html_builder) is a simple virtual DOM library
|
||||||
|
(without diffing, you can find that
|
||||||
|
[here](https://github.com/thosakwe/html_builder_vdom)), with a handy Dart DSL that makes it easy to build HTML
|
||||||
|
AST's:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:html_builder/elements.dart';
|
||||||
|
|
||||||
|
Node myDom = html(lang: 'en', c: [
|
||||||
|
head(c: [
|
||||||
|
meta(name: 'viewport', content: 'width=device-width, initial-scale=1'),
|
||||||
|
title(c: [
|
||||||
|
text('html_builder example page')
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
body(c: [
|
||||||
|
h1(c: [
|
||||||
|
text('Hello world!'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
This plug-in means that you can now `return` these AST's, and Angel will automatically send them to
|
||||||
|
clients. Ultimately, the implication is that you can use `html_builder` as a substitute for a
|
||||||
|
templating system within Dart. With [hot reloading](https://github.com/angel-dart/hot), you won't
|
||||||
|
even need to reload your server (as it should be).
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
In your `pubspec.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
angel_html: ^1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
The `renderHtml` function does all the magic for you.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
configureServer(Angel app) async {
|
||||||
|
// Wire it up!
|
||||||
|
app.fallback(renderHtml());
|
||||||
|
|
||||||
|
// You can pass a custom StringRenderer if you need more control over the output.
|
||||||
|
app.fallback(renderHtml(renderer: new StringRenderer(html5: false)));
|
||||||
|
|
||||||
|
app.get('/greet/:name', (RequestContext req) {
|
||||||
|
return html(lang: 'en', c: [
|
||||||
|
head(c: [
|
||||||
|
meta(name: 'viewport', content: 'width=device-width, initial-scale=1'),
|
||||||
|
title(c: [
|
||||||
|
text('Greetings!')
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
body(c: [
|
||||||
|
h1(c: [
|
||||||
|
text('Hello, ${req.params['id']}!'),
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, `renderHtml` will ignore the client's `Accept` header. However, if you pass
|
||||||
|
`enforceAcceptHeader` as `true`, then a `406 Not Acceptable` error will be thrown if the
|
||||||
|
client doesn't accept `*/*` or `text/html`.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
configureServer(Angel app) async {
|
||||||
|
// Wire it up!
|
||||||
|
app.fallback(renderHtml(enforceAcceptHeader: true));
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
4
packages/html/analysis_options.yaml
Normal file
4
packages/html/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:pedantic/analysis_options.yaml
|
||||||
|
analyzer:
|
||||||
|
strong-mode:
|
||||||
|
implicit-casts: false
|
46
packages/html/example/main.dart
Normal file
46
packages/html/example/main.dart
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:angel_framework/http.dart';
|
||||||
|
import 'package:angel_html/angel_html.dart';
|
||||||
|
import 'package:html_builder/elements.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
|
main() async {
|
||||||
|
var app = Angel(), http = AngelHttp(app);
|
||||||
|
app.logger = Logger('angel_html')
|
||||||
|
..onRecord.listen((rec) {
|
||||||
|
print(rec);
|
||||||
|
if (rec.error != null) print(rec.error);
|
||||||
|
if (rec.stackTrace != null) print(rec.stackTrace);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.fallback(renderHtml());
|
||||||
|
|
||||||
|
app.get('/html', (req, res) {
|
||||||
|
return html(c: [
|
||||||
|
head(c: [
|
||||||
|
title(c: [text('ok')])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'/strict',
|
||||||
|
chain([
|
||||||
|
renderHtml(
|
||||||
|
enforceAcceptHeader: true,
|
||||||
|
renderer: StringRenderer(
|
||||||
|
doctype: null,
|
||||||
|
pretty: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(req, res) {
|
||||||
|
return div(c: [text('strict')]);
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
|
||||||
|
app.fallback((req, res) => throw AngelHttpException.notFound());
|
||||||
|
|
||||||
|
await http.startServer('127.0.0.1', 3000);
|
||||||
|
print('Listening at ${http.uri}');
|
||||||
|
}
|
34
packages/html/lib/angel_html.dart
Normal file
34
packages/html/lib/angel_html.dart
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import 'dart:async';
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:html_builder/html_builder.dart';
|
||||||
|
|
||||||
|
/// Returns a [RequestMiddleware] that allows you to return `html_builder` [Node]s as responses.
|
||||||
|
///
|
||||||
|
/// You can provide a custom [renderer]. The default renders minified HTML5 pages.
|
||||||
|
///
|
||||||
|
/// Set [enforceAcceptHeader] to `true` to throw a `406 Not Acceptable` if the client doesn't accept HTML responses.
|
||||||
|
RequestHandler renderHtml({StringRenderer renderer, bool enforceAcceptHeader}) {
|
||||||
|
renderer ??= new StringRenderer(pretty: false, html5: true);
|
||||||
|
|
||||||
|
return (RequestContext req, ResponseContext res) {
|
||||||
|
var oldSerializer = res.serializer;
|
||||||
|
|
||||||
|
res.serializer = (data) {
|
||||||
|
if (data is! Node)
|
||||||
|
return oldSerializer(data);
|
||||||
|
else {
|
||||||
|
if (enforceAcceptHeader == true && !req.accepts('text/html'))
|
||||||
|
throw new AngelHttpException.notAcceptable();
|
||||||
|
|
||||||
|
var content = renderer.render(data as Node);
|
||||||
|
res
|
||||||
|
..headers['content-type'] = 'text/html'
|
||||||
|
..write(content);
|
||||||
|
res.close();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new Future<bool>.value(true);
|
||||||
|
};
|
||||||
|
}
|
16
packages/html/pubspec.yaml
Normal file
16
packages/html/pubspec.yaml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
name: angel_html
|
||||||
|
version: 2.0.0
|
||||||
|
description: Support for rendering html_builder AST's as responses in Angel.
|
||||||
|
author: Tobe O <thosakwe@gmail.com>
|
||||||
|
homepage: https://github.com/angel-dart/html_builder
|
||||||
|
environment:
|
||||||
|
sdk: ">=2.0.0-dev <3.0.0"
|
||||||
|
dependencies:
|
||||||
|
angel_framework: ^2.0.0-alpha
|
||||||
|
html_builder: ^1.0.0
|
||||||
|
dev_dependencies:
|
||||||
|
angel_test: ^2.0.0-alpha
|
||||||
|
html: ^0.13.2
|
||||||
|
logging: ^0.11.0
|
||||||
|
test: ^1.0.0
|
||||||
|
pedantic: ^1.0.0
|
83
packages/html/test/all_test.dart
Normal file
83
packages/html/test/all_test.dart
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:angel_html/angel_html.dart';
|
||||||
|
import 'package:angel_test/angel_test.dart';
|
||||||
|
import 'package:html_builder/elements.dart';
|
||||||
|
import 'package:html_builder/html_builder.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
main() {
|
||||||
|
Angel app;
|
||||||
|
TestClient client;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
app = new Angel();
|
||||||
|
|
||||||
|
app.fallback(renderHtml());
|
||||||
|
|
||||||
|
app.get('/html', (req, res) {
|
||||||
|
return html(c: [
|
||||||
|
head(c: [
|
||||||
|
title(c: [text('ok')])
|
||||||
|
])
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get(
|
||||||
|
'/strict',
|
||||||
|
chain([
|
||||||
|
renderHtml(
|
||||||
|
enforceAcceptHeader: true,
|
||||||
|
renderer: new StringRenderer(
|
||||||
|
doctype: null,
|
||||||
|
pretty: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(req, res) {
|
||||||
|
return div(c: [text('strict')]);
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
client = await connectTo(app);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() => client.close());
|
||||||
|
|
||||||
|
test('sets content type and body', () async {
|
||||||
|
var response = await client.get('/html');
|
||||||
|
print('Response: ${response.body}');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
response,
|
||||||
|
allOf(
|
||||||
|
hasContentType('text/html'),
|
||||||
|
hasBody(
|
||||||
|
'<!DOCTYPE html><html><head><title>ok</title></head></html>')));
|
||||||
|
});
|
||||||
|
|
||||||
|
group('enforce accept header', () {
|
||||||
|
test('sends if correct accept or wildcard', () async {
|
||||||
|
var response = await client.get('/strict', headers: {'accept': '*/*'});
|
||||||
|
print('Response: ${response.body}');
|
||||||
|
expect(response,
|
||||||
|
allOf(hasContentType('text/html'), hasBody('<div>strict</div>')));
|
||||||
|
|
||||||
|
response = await client.get('/strict',
|
||||||
|
headers: {'accept': 'text/html,application/json,text/xml'});
|
||||||
|
print('Response: ${response.body}');
|
||||||
|
expect(response,
|
||||||
|
allOf(hasContentType('text/html'), hasBody('<div>strict</div>')));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws if incorrect or no accept', () async {
|
||||||
|
var response = await client.get('/strict');
|
||||||
|
print('Response: ${response.body}');
|
||||||
|
expect(response, hasStatus(406));
|
||||||
|
|
||||||
|
response = await client
|
||||||
|
.get('/strict', headers: {'accept': 'application/json,text/xml'});
|
||||||
|
print('Response: ${response.body}');
|
||||||
|
expect(response,
|
||||||
|
isAngelHttpException(statusCode: 406, message: '406 Not Acceptable'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue