Added inlineAssets, split out inlineAssetsFromVirtualDirectory
This commit is contained in:
parent
3faa5509ea
commit
d997734f0f
3 changed files with 124 additions and 49 deletions
35
README.md
35
README.md
|
@ -6,10 +6,12 @@ the infamous
|
||||||
and other SEO optimizations that can easily become tedious to perform by hand.
|
and other SEO optimizations that can easily become tedious to perform by hand.
|
||||||
|
|
||||||
## `inlineAssets`
|
## `inlineAssets`
|
||||||
This function is a simple one; it wraps a `VirtualDirectory` to patch the way it sends
|
A
|
||||||
`.html` files.
|
[response finalizer](https://angel-dart.gitbook.io/angel/the-basics/request-lifecycle)
|
||||||
|
that can be used in any application to patch HTML responses, including those sent with
|
||||||
|
a templating engine like Jael.
|
||||||
|
|
||||||
In any `.html` file sent down, `link` and `script` elements that point to internal resources
|
In any `text/html` response sent down, `link` and `script` elements that point to internal resources
|
||||||
will have the contents of said file read, and inlined into the HTML page itself.
|
will have the contents of said file read, and inlined into the HTML page itself.
|
||||||
|
|
||||||
In this case, "internal resources" refers to a URI *without* a scheme, i.e. `/site.css` or
|
In this case, "internal resources" refers to a URI *without* a scheme, i.e. `/site.css` or
|
||||||
|
@ -21,6 +23,32 @@ import 'package:angel_seo/angel_seo.dart';
|
||||||
import 'package:angel_static/angel_static.dart';
|
import 'package:angel_static/angel_static.dart';
|
||||||
import 'package:file/local.dart';
|
import 'package:file/local.dart';
|
||||||
|
|
||||||
|
main() async {
|
||||||
|
var app = new Angel()..lazyParseBodies = true;
|
||||||
|
var fs = const LocalFileSystem();
|
||||||
|
var http = new AngelHttp(app);
|
||||||
|
|
||||||
|
app.responseFinalizers.add(inlineAssets(fs.directory('web')));
|
||||||
|
|
||||||
|
app.use(() => throw new AngelHttpException.notFound());
|
||||||
|
|
||||||
|
var server = await http.startServer('127.0.0.1', 3000);
|
||||||
|
print('Listening at http://${server.address.address}:${server.port}');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `inlineAssetsFromVirtualDirectory`
|
||||||
|
This function is a simple one; it wraps a `VirtualDirectory` to patch the way it sends
|
||||||
|
`.html` files.
|
||||||
|
|
||||||
|
Produces the same functionality as `inlineAssets`.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
|
import 'package:angel_seo/angel_seo.dart';
|
||||||
|
import 'package:angel_static/angel_static.dart';
|
||||||
|
import 'package:file/local.dart';
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
var app = new Angel()..lazyParseBodies = true;
|
var app = new Angel()..lazyParseBodies = true;
|
||||||
var fs = const LocalFileSystem();
|
var fs = const LocalFileSystem();
|
||||||
|
@ -41,5 +69,4 @@ main() async {
|
||||||
var server = await http.startServer('127.0.0.1', 3000);
|
var server = await http.startServer('127.0.0.1', 3000);
|
||||||
print('Listening at http://${server.address.address}:${server.port}');
|
print('Listening at http://${server.address.address}:${server.port}');
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:angel_framework/angel_framework.dart';
|
import 'package:angel_framework/angel_framework.dart';
|
||||||
import 'package:angel_seo/angel_seo.dart';
|
import 'package:angel_seo/angel_seo.dart';
|
||||||
import 'package:angel_static/angel_static.dart';
|
import 'package:angel_static/angel_static.dart';
|
||||||
|
import 'package:dart2_constant/convert.dart';
|
||||||
import 'package:file/local.dart';
|
import 'package:file/local.dart';
|
||||||
|
|
||||||
main() async {
|
main() async {
|
||||||
|
@ -8,7 +9,8 @@ main() async {
|
||||||
var fs = const LocalFileSystem();
|
var fs = const LocalFileSystem();
|
||||||
var http = new AngelHttp(app);
|
var http = new AngelHttp(app);
|
||||||
|
|
||||||
var vDir = inlineAssets(
|
// You can wrap a [VirtualDirectory]
|
||||||
|
var vDir = inlineAssetsFromVirtualDirectory(
|
||||||
new VirtualDirectory(
|
new VirtualDirectory(
|
||||||
app,
|
app,
|
||||||
fs,
|
fs,
|
||||||
|
@ -18,6 +20,20 @@ main() async {
|
||||||
|
|
||||||
app.use(vDir.handleRequest);
|
app.use(vDir.handleRequest);
|
||||||
|
|
||||||
|
// OR, just add a finalizer. Note that [VirtualDirectory] *streams* its response,
|
||||||
|
// so a response finalizer does not touch its contents.
|
||||||
|
//
|
||||||
|
// You likely won't need to use both; it just depends on your use case.
|
||||||
|
app.responseFinalizers.add(inlineAssets(fs.directory('web')));
|
||||||
|
|
||||||
|
app.get('/using_response_buffer', (ResponseContext res) async {
|
||||||
|
var indexHtml = fs.directory('web').childFile('index.html');
|
||||||
|
var contents = await indexHtml.readAsString();
|
||||||
|
res
|
||||||
|
..headers['content-type'] = 'text/html; charset=utf-8'
|
||||||
|
..buffer.add(utf8.encode(contents));
|
||||||
|
});
|
||||||
|
|
||||||
app.use(() => throw new AngelHttpException.notFound());
|
app.use(() => throw new AngelHttpException.notFound());
|
||||||
|
|
||||||
var server = await http.startServer('127.0.0.1', 3000);
|
var server = await http.startServer('127.0.0.1', 3000);
|
||||||
|
|
|
@ -15,26 +15,38 @@ import 'package:path/path.dart' as p;
|
||||||
///
|
///
|
||||||
/// In this case, "internal resources" refers to a URI *without* a scheme, i.e. `/site.css` or
|
/// In this case, "internal resources" refers to a URI *without* a scheme, i.e. `/site.css` or
|
||||||
/// `foo/bar/baz.js`.
|
/// `foo/bar/baz.js`.
|
||||||
VirtualDirectory inlineAssets(VirtualDirectory vDir) => new _InlineAssets(vDir);
|
RequestMiddleware inlineAssets(Directory assetDirectory) {
|
||||||
|
return (req, res) {
|
||||||
|
if (res.willCloseItself ||
|
||||||
|
res.streaming ||
|
||||||
|
res.contentType.mimeType != 'text/html') {
|
||||||
|
return new Future<bool>.value(true);
|
||||||
|
} else {
|
||||||
|
var doc = html.parse(utf8.decode(res.buffer.takeBytes()));
|
||||||
|
return inlineAssetsIntoDocument(doc, assetDirectory).then((_) {
|
||||||
|
res.buffer.add(utf8.encode(doc.outerHtml));
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
class _InlineAssets extends VirtualDirectory {
|
/// Wraps a `VirtualDirectory` to patch the way it sends
|
||||||
final VirtualDirectory inner;
|
/// `.html` files.
|
||||||
|
///
|
||||||
_InlineAssets(this.inner)
|
/// In any `.html` file sent down, `link` and `script` elements that point to internal resources
|
||||||
: super(inner.app, inner.fileSystem,
|
/// will have the contents of said file read, and inlined into the HTML page itself.
|
||||||
source: inner.source,
|
///
|
||||||
indexFileNames: inner.indexFileNames,
|
/// In this case, "internal resources" refers to a URI *without* a scheme, i.e. `/site.css` or
|
||||||
publicPath: inner.publicPath,
|
/// `foo/bar/baz.js`.
|
||||||
callback: inner.callback,
|
VirtualDirectory inlineAssetsFromVirtualDirectory(VirtualDirectory vDir) =>
|
||||||
allowDirectoryListing: inner.allowDirectoryListing);
|
new _InlineAssets(vDir);
|
||||||
|
|
||||||
@override
|
|
||||||
Future<bool> serveFile(
|
|
||||||
File file, FileStat stat, RequestContext req, ResponseContext res) async {
|
|
||||||
if (p.extension(file.path) == '.html') {
|
|
||||||
var contents = await file.readAsString();
|
|
||||||
var doc = html.parse(contents, sourceUrl: file.uri.toString());
|
|
||||||
|
|
||||||
|
/// Replaces `link` and `script` tags within a [doc] with the static contents they would otherwise trigger an HTTP request to.
|
||||||
|
///
|
||||||
|
/// Powers both [inlineAssets] and [inlineAssetsFromVirtualDirectory].
|
||||||
|
Future inlineAssetsIntoDocument(
|
||||||
|
html.Document doc, Directory assetDirectory) async {
|
||||||
var linksWithRel = doc.head
|
var linksWithRel = doc.head
|
||||||
?.getElementsByTagName('link')
|
?.getElementsByTagName('link')
|
||||||
?.where((link) => link.attributes['rel'] == 'stylesheet') ??
|
?.where((link) => link.attributes['rel'] == 'stylesheet') ??
|
||||||
|
@ -47,7 +59,7 @@ class _InlineAssets extends VirtualDirectory {
|
||||||
var uri = Uri.parse(link.attributes['href']);
|
var uri = Uri.parse(link.attributes['href']);
|
||||||
|
|
||||||
if (uri.scheme.isEmpty) {
|
if (uri.scheme.isEmpty) {
|
||||||
var styleFile = inner.source.childFile(uri.path);
|
var styleFile = assetDirectory.childFile(uri.path);
|
||||||
|
|
||||||
if (await styleFile.exists()) {
|
if (await styleFile.exists()) {
|
||||||
var style = new html.Element.tag('style')
|
var style = new html.Element.tag('style')
|
||||||
|
@ -69,7 +81,7 @@ class _InlineAssets extends VirtualDirectory {
|
||||||
var uri = Uri.parse(script.attributes['src']);
|
var uri = Uri.parse(script.attributes['src']);
|
||||||
|
|
||||||
if (uri.scheme.isEmpty) {
|
if (uri.scheme.isEmpty) {
|
||||||
var scriptFile = inner.source.childFile(uri.path);
|
var scriptFile = assetDirectory.childFile(uri.path);
|
||||||
if (await scriptFile.exists()) {
|
if (await scriptFile.exists()) {
|
||||||
script.attributes.remove('src');
|
script.attributes.remove('src');
|
||||||
script.innerHtml = await scriptFile.readAsString();
|
script.innerHtml = await scriptFile.readAsString();
|
||||||
|
@ -77,6 +89,26 @@ class _InlineAssets extends VirtualDirectory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InlineAssets extends VirtualDirectory {
|
||||||
|
final VirtualDirectory inner;
|
||||||
|
|
||||||
|
_InlineAssets(this.inner)
|
||||||
|
: super(inner.app, inner.fileSystem,
|
||||||
|
source: inner.source,
|
||||||
|
indexFileNames: inner.indexFileNames,
|
||||||
|
publicPath: inner.publicPath,
|
||||||
|
callback: inner.callback,
|
||||||
|
allowDirectoryListing: inner.allowDirectoryListing);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> serveFile(
|
||||||
|
File file, FileStat stat, RequestContext req, ResponseContext res) async {
|
||||||
|
if (p.extension(file.path) == '.html') {
|
||||||
|
var contents = await file.readAsString();
|
||||||
|
var doc = html.parse(contents, sourceUrl: file.uri.toString());
|
||||||
|
await inlineAssetsIntoDocument(doc, inner.source);
|
||||||
|
|
||||||
res
|
res
|
||||||
..headers['content-type'] = 'text/html; charset=utf8'
|
..headers['content-type'] = 'text/html; charset=utf8'
|
||||||
|
|
Loading…
Reference in a new issue