Skip to content

Commit

Permalink
add standalone option
Browse files Browse the repository at this point in the history
  • Loading branch information
Kilian Schulte committed Dec 30, 2024
1 parent d50f290 commit 7c60e97
Show file tree
Hide file tree
Showing 10 changed files with 59 additions and 18 deletions.
1 change: 1 addition & 0 deletions packages/jaspr/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Added `table` and related html methods.
- Added `value` parameter to `select()` method.
- Add 'standalone' option to `renderComponent` method.

## 0.16.2

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ class ClientScriptAdapter extends HeadScopeAdapter {
final List<ClientTarget> clientTargets;

@override
void applyHead(MarkupRenderObject head) {
bool applyHead(MarkupRenderObject head) {
if (clientTargets.isEmpty) {
return;
return false;
}

String source;
Expand All @@ -27,5 +27,7 @@ class ClientScriptAdapter extends HeadScopeAdapter {
head.createChildRenderObject()
..updateElement('script', null, null, null, {'src': '$source.dart.js', 'defer': ''}, null),
);

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@ class GlobalStylesAdapter extends HeadScopeAdapter {
final ServerAppBinding binding;

@override
void applyHead(MarkupRenderObject head) {
bool applyHead(MarkupRenderObject head) {
var styles = binding.options.styles?.call() ?? [];
if (styles.isEmpty) {
return;
return false;
}

head.children.insertBefore(
head.createChildRenderObject()
..updateElement('style', null, null, null, null, null)
..children.insertBefore(head.createChildRenderObject()..updateText(styles.render(), true)),
);

return true;
}
}
11 changes: 8 additions & 3 deletions packages/jaspr/lib/src/server/adapters/head_scope_adapter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,17 @@ abstract class HeadScopeAdapter extends RenderAdapter {
var html = root.children.findWhere((c) => c.tag == 'html')?.node ?? root;
var head = html.children.findWhere((c) => c.tag == 'head')?.node;

bool needsInsert = false;
if (head == null) {
html.children.insertAfter(head = html.createChildRenderObject()..tag = 'head');
head = html.createChildRenderObject()..tag = 'head';
needsInsert = true;
}

applyHead(head);
var didApply = applyHead(head);
if (didApply && needsInsert) {
html.children.insertAfter(head);
}
}

void applyHead(MarkupRenderObject head);
bool applyHead(MarkupRenderObject head);
}
11 changes: 6 additions & 5 deletions packages/jaspr/lib/src/server/render_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ typedef FileLoader = Future<String?> Function(String);
/// Performs the rendering process and provides the created [AppBinding] to [setup].
///
/// If [Jaspr.useIsolates] is true, this spawns an isolate for each render.
Future<String> render(SetupFunction setup, Uri requestUri, FileLoader loadFile) async {
Future<String> render(SetupFunction setup, Uri requestUri, FileLoader loadFile, bool standalone) async {
if (!Jaspr.useIsolates) {
var binding = ServerAppBinding()
..setCurrentUri(requestUri)
..setFileHandler(loadFile);
setup(binding);
return binding.render();
return binding.render(standalone: standalone);
}

var resultCompleter = Completer<String>.sync();
Expand All @@ -38,7 +38,7 @@ Future<String> render(SetupFunction setup, Uri requestUri, FileLoader loadFile)
});

try {
var message = _RenderMessage(setup, requestUri, port.sendPort);
var message = _RenderMessage(setup, requestUri, standalone, port.sendPort);
await Isolate.spawn(_render, message, onError: errorPort.sendPort);

return await resultCompleter.future;
Expand All @@ -62,16 +62,17 @@ void _render(_RenderMessage message) async {
});
message.setup(binding);

var result = await binding.render();
var result = await binding.render(standalone: message.standalone);
message.sendPort.send(result);
}

class _RenderMessage {
final SetupFunction setup;
final Uri requestUri;
final bool standalone;
final SendPort sendPort;

_RenderMessage(this.setup, this.requestUri, this.sendPort);
_RenderMessage(this.setup, this.requestUri, this.standalone, this.sendPort);
}

class _LoadFileRequest {
Expand Down
8 changes: 5 additions & 3 deletions packages/jaspr/lib/src/server/run_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,16 @@ Handler serveApp(AppHandler handler) {
typedef RenderFunction = FutureOr<Response> Function(Component);
typedef AppHandler = FutureOr<Response> Function(Request, RenderFunction render);

/// Directly renders the provided component into a html string
Future<String> renderComponent(Component app) async {
/// Directly renders the provided component into a html string.
///
/// When [standalone] is false (default), the html output will have a full document structure (html, head, body).
Future<String> renderComponent(Component app, {bool standalone = false}) async {
_checkInitialized('renderComponent');
var fileHandler = staticFileHandler();
return render(_createSetup(app), Uri.parse('https://0.0.0.0/'), (name) async {
var response = await fileHandler(Request('get', Uri.parse('https://0.0.0.0/$name')));
return response.statusCode == 200 ? response.readAsString() : null;
});
}, standalone);
}

void _checkInitialized(String method) {
Expand Down
4 changes: 2 additions & 2 deletions packages/jaspr/lib/src/server/server_binding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ServerAppBinding extends AppBinding with ComponentsBinding {
super.attachRootComponent(ClientComponentRegistry(child: app));
}

Future<String> render() async {
Future<String> render({bool standalone = false}) async {
if (rootElement == null) return '';

if (rootElement!.owner.isFirstBuild) {
Expand All @@ -38,7 +38,7 @@ class ServerAppBinding extends AppBinding with ComponentsBinding {
var adapters = [
..._adapters.reversed,
GlobalStylesAdapter(this),
DocumentAdapter(),
if (!standalone) DocumentAdapter(),
];

for (var adapter in adapters.reversed) {
Expand Down
2 changes: 1 addition & 1 deletion packages/jaspr/lib/src/server/server_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Handler createHandler(SetupHandler handle, {http.Client? client, Handler? fileHa
}

return Response.ok(
await render(setup, requestUri, fileLoader),
await render(setup, requestUri, fileLoader, false),
headers: {'Content-Type': 'text/html'},
);
});
Expand Down
28 changes: 28 additions & 0 deletions packages/jaspr/test/server/render_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@TestOn('vm')

import 'package:jaspr/server.dart';
import 'package:jaspr_test/server_test.dart';

void main() {
group('render test', () {
setUpAll(() {
Jaspr.initializeApp(useIsolates: false);
});

test('renders component with document', () async {
var result = await renderComponent(div(id: 'test', []));

expect(
result,
equals('<!DOCTYPE html>\n'
'<html><head><base href="/"/></head><body><div id="test"></div></body></html>\n'
''));
});

test('renders standalone component', () async {
var result = await renderComponent(div(id: 'test', []), standalone: true);

expect(result, equals('<div id="test"></div>\n'));
});
});
}

0 comments on commit 7c60e97

Please sign in to comment.