Merged from sdk-2.12.x_nnbd

This commit is contained in:
thomashii 2021-06-20 20:37:20 +08:00
parent 3adf9d62bc
commit 62f2235a6d
228 changed files with 3849 additions and 3305 deletions

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Dart Packages" level="project" />
</component>
</module>

View file

@ -1,421 +0,0 @@
<component name="libraryTable">
<library name="Dart Packages" type="DartPackagesLibraryType">
<properties>
<option name="packageNameToDirsMap">
<entry key="analyzer">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/analyzer-0.32.4/lib" />
</list>
</value>
</entry>
<entry key="args">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/args-1.5.0/lib" />
</list>
</value>
</entry>
<entry key="async">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.0.8/lib" />
</list>
</value>
</entry>
<entry key="boolean_selector">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="charcode">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.2/lib" />
</list>
</value>
</entry>
<entry key="collection">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.14.11/lib" />
</list>
</value>
</entry>
<entry key="convert">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/convert-2.0.2/lib" />
</list>
</value>
</entry>
<entry key="crypto">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/crypto-2.0.6/lib" />
</list>
</value>
</entry>
<entry key="csslib">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/csslib-0.14.4+1/lib" />
</list>
</value>
</entry>
<entry key="dart2_constant">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/dart2_constant-1.0.2+dart2/lib" />
</list>
</value>
</entry>
<entry key="front_end">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/front_end-0.1.4/lib" />
</list>
</value>
</entry>
<entry key="glob">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/glob-1.1.7/lib" />
</list>
</value>
</entry>
<entry key="html">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/html-0.13.3+2/lib" />
</list>
</value>
</entry>
<entry key="http">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http-0.11.3+17/lib" />
</list>
</value>
</entry>
<entry key="http_multi_server">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.0.5/lib" />
</list>
</value>
</entry>
<entry key="http_parser">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.3/lib" />
</list>
</value>
</entry>
<entry key="http_server">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_server-0.9.8/lib" />
</list>
</value>
</entry>
<entry key="io">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/io-0.3.3/lib" />
</list>
</value>
</entry>
<entry key="js">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/js-0.6.1+1/lib" />
</list>
</value>
</entry>
<entry key="json_rpc_2">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.0.9/lib" />
</list>
</value>
</entry>
<entry key="kernel">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/kernel-0.3.4/lib" />
</list>
</value>
</entry>
<entry key="logging">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/logging-0.11.3+2/lib" />
</list>
</value>
</entry>
<entry key="matcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.3+1/lib" />
</list>
</value>
</entry>
<entry key="meta">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.1.6/lib" />
</list>
</value>
</entry>
<entry key="mime">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/mime-0.9.6+2/lib" />
</list>
</value>
</entry>
<entry key="multi_server_socket">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/multi_server_socket-1.0.2/lib" />
</list>
</value>
</entry>
<entry key="node_preamble">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.4/lib" />
</list>
</value>
</entry>
<entry key="package_config">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_config-1.0.5/lib" />
</list>
</value>
</entry>
<entry key="package_resolver">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_resolver-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="path">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.6.2/lib" />
</list>
</value>
</entry>
<entry key="plugin">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin-0.2.0+3/lib" />
</list>
</value>
</entry>
<entry key="pool">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pool-1.3.6/lib" />
</list>
</value>
</entry>
<entry key="pub_semver">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.2/lib" />
</list>
</value>
</entry>
<entry key="shelf">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.3+3/lib" />
</list>
</value>
</entry>
<entry key="shelf_packages_handler">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-1.0.4/lib" />
</list>
</value>
</entry>
<entry key="shelf_static">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.8/lib" />
</list>
</value>
</entry>
<entry key="shelf_web_socket">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.2+4/lib" />
</list>
</value>
</entry>
<entry key="source_map_stack_trace">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-1.1.5/lib" />
</list>
</value>
</entry>
<entry key="source_maps">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.7/lib" />
</list>
</value>
</entry>
<entry key="source_span">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.4.1/lib" />
</list>
</value>
</entry>
<entry key="stack_trace">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.9.3/lib" />
</list>
</value>
</entry>
<entry key="stream_channel">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-1.6.8/lib" />
</list>
</value>
</entry>
<entry key="string_scanner">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.3/lib" />
</list>
</value>
</entry>
<entry key="term_glyph">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.0.1/lib" />
</list>
</value>
</entry>
<entry key="test">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test-1.3.0/lib" />
</list>
</value>
</entry>
<entry key="typed_data">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib" />
</list>
</value>
</entry>
<entry key="utf">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+5/lib" />
</list>
</value>
</entry>
<entry key="vm_service_client">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vm_service_client-0.2.6/lib" />
</list>
</value>
</entry>
<entry key="watcher">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+10/lib" />
</list>
</value>
</entry>
<entry key="web_socket_channel">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.0.9/lib" />
</list>
</value>
</entry>
<entry key="yaml">
<value>
<list>
<option value="$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib" />
</list>
</value>
</entry>
</option>
</properties>
<CLASSES>
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/analyzer-0.32.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/args-1.5.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/async-2.0.8/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/boolean_selector-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/charcode-1.1.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/collection-1.14.11/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/convert-2.0.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/crypto-2.0.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/csslib-0.14.4+1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/dart2_constant-1.0.2+dart2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/front_end-0.1.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/glob-1.1.7/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/html-0.13.3+2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http-0.11.3+17/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.0.5/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_parser-3.1.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/http_server-0.9.8/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/io-0.3.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/js-0.6.1+1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/json_rpc_2-2.0.9/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/kernel-0.3.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/logging-0.11.3+2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/matcher-0.12.3+1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/meta-1.1.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/mime-0.9.6+2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/multi_server_socket-1.0.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/node_preamble-1.4.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_config-1.0.5/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/package_resolver-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/path-1.6.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/plugin-0.2.0+3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pool-1.3.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.2/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.3+3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-1.0.4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_static-0.2.8/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/shelf_web_socket-0.2.2+4/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-1.1.5/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.7/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/source_span-1.4.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.9.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/stream_channel-1.6.8/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/string_scanner-1.0.3/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/term_glyph-1.0.1/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/test-1.3.0/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/typed_data-1.1.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/utf-0.9.0+5/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/vm_service_client-0.2.6/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/watcher-0.9.7+10/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/web_socket_channel-1.0.9/lib" />
<root url="file://$USER_HOME$/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib" />
</CLASSES>
<JAVADOC />
<LIBRARY_FILE />
<SOURCES />
</library>
</component>

View file

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/body_parser.iml" filepath="$PROJECT_DIR$/.idea/body_parser.iml" />
</modules>
</component>
</project>

View file

@ -1,7 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="main.dart" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true" nameIsGenerated="true">
<option name="filePath" value="$PROJECT_DIR$/example/main.dart" />
<option name="workingDirectory" value="$PROJECT_DIR$" />
<method />
</configuration>
</component>

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View 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.

View file

@ -1,3 +1,6 @@
# 2.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 1.1.1 # 1.1.1
* Dart 2 updates; should fix Angel in Travis. * Dart 2 updates; should fix Angel in Travis.

View file

@ -1,3 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer: analyzer:
strong-mode: strong-mode:
implicit-casts: false implicit-casts: false

View file

@ -5,16 +5,16 @@ import 'dart:isolate';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
import 'package:body_parser/body_parser.dart'; import 'package:body_parser/body_parser.dart';
main() async { void main() async {
var address = '127.0.0.1'; var address = '127.0.0.1';
var port = 3000; var port = 3000;
var futures = <Future>[]; var futures = <Future>[];
for (int i = 1; i < Platform.numberOfProcessors; i++) { for (var i = 1; i < Platform.numberOfProcessors; i++) {
futures.add(Isolate.spawn(start, [address, port, i])); futures.add(Isolate.spawn(start, [address, port, i]));
} }
Future.wait(futures).then((_) { await Future.wait(futures).then((_) {
print('All instances started.'); print('All instances started.');
print( print(
'Test with "wrk -t12 -c400 -d30s -s ./example/post.lua http://localhost:3000" or similar'); 'Test with "wrk -t12 -c400 -d30s -s ./example/post.lua http://localhost:3000" or similar');
@ -23,13 +23,13 @@ main() async {
} }
void start(List args) { void start(List args) {
var address = new InternetAddress(args[0] as String); var address = InternetAddress(args[0] as String);
int port = 8080; var port = 8080;
if (args[1] is int) { if (args[1] is int) {
args[1]; args[1];
} }
int id = 0; var id = 0;
if (args[2] is int) { if (args[2] is int) {
args[2]; args[2];
} }
@ -39,9 +39,9 @@ void start(List args) {
// ignore: deprecated_member_use // ignore: deprecated_member_use
var body = await defaultParseBody(request); var body = await defaultParseBody(request);
request.response request.response
..headers.contentType = new ContentType('application', 'json') ..headers.contentType = ContentType('application', 'json')
..write(json.encode(body.body)) ..write(json.encode(body.body));
..close(); await request.response.close();
}); });
print( print(
@ -50,11 +50,11 @@ void start(List args) {
} }
Future<BodyParseResult> defaultParseBody(HttpRequest request, Future<BodyParseResult> defaultParseBody(HttpRequest request,
{bool storeOriginalBuffer: false}) { {bool storeOriginalBuffer = false}) {
return parseBodyFromStream( return parseBodyFromStream(
request, request,
request.headers.contentType != null request.headers.contentType != null
? new MediaType.parse(request.headers.contentType.toString()) ? MediaType.parse(request.headers.contentType.toString())
: null, : null,
request.uri, request.uri,
storeOriginalBuffer: storeOriginalBuffer); storeOriginalBuffer: storeOriginalBuffer);

View file

@ -3,10 +3,10 @@ import 'file_upload_info.dart';
/// A representation of data from an incoming request. /// A representation of data from an incoming request.
abstract class BodyParseResult { abstract class BodyParseResult {
/// The parsed body. /// The parsed body.
Map<String, dynamic> get body; Map<String?, dynamic> get body;
/// The parsed query string. /// The parsed query string.
Map<String, dynamic> get query; Map<String?, dynamic> get query;
/// All files uploaded within this request. /// All files uploaded within this request.
List<FileUploadInfo> get files; List<FileUploadInfo> get files;
@ -14,7 +14,7 @@ abstract class BodyParseResult {
/// The original body bytes sent with this request. /// The original body bytes sent with this request.
/// ///
/// You must set [storeOriginalBuffer] to `true` to see this. /// You must set [storeOriginalBuffer] to `true` to see this.
List<int> get originalBuffer; List<int>? get originalBuffer;
/// If an error was encountered while parsing the body, it will appear here. /// If an error was encountered while parsing the body, it will appear here.
/// ///
@ -24,5 +24,5 @@ abstract class BodyParseResult {
/// If an error was encountered while parsing the body, the call stack will appear here. /// If an error was encountered while parsing the body, the call stack will appear here.
/// ///
/// Otherwise, this is `null`. /// Otherwise, this is `null`.
StackTrace get stack; StackTrace? get stack;
} }

View file

@ -2,6 +2,6 @@ import 'file_upload_info.dart';
List<FileUploadInfo> getFileDataFromChunk( List<FileUploadInfo> getFileDataFromChunk(
String chunk, String boundary, String fileUploadName, Map body) { String chunk, String boundary, String fileUploadName, Map body) {
List<FileUploadInfo> result = []; var result = <FileUploadInfo>[];
return result; return result;
} }

View file

@ -1,17 +1,17 @@
/// Represents a file uploaded to the server. /// Represents a file uploaded to the server.
class FileUploadInfo { class FileUploadInfo {
/// The MIME type of the uploaded file. /// The MIME type of the uploaded file.
String mimeType; String? mimeType;
/// The name of the file field from the request. /// The name of the file field from the request.
String name; String? name;
/// The filename of the file. /// The filename of the file.
String filename; String? filename;
/// The bytes that make up this file. /// The bytes that make up this file.
List<int> data; List<int> data;
FileUploadInfo( FileUploadInfo(
{this.mimeType, this.name, this.filename, this.data: const []}) {} {this.mimeType, this.name, this.filename, this.data = const []});
} }

View file

@ -1,20 +1,22 @@
import 'package:dart2_constant/convert.dart'; import 'dart:convert';
getValue(String value) { dynamic getValue(String value) {
try { try {
num numValue = num.parse(value); var numValue = num.parse(value);
if (!numValue.isNaN) if (!numValue.isNaN) {
return numValue; return numValue;
else } else {
return value;
} on FormatException {
if (value.startsWith('[') && value.endsWith(']'))
return json.decode(value);
else if (value.startsWith('{') && value.endsWith('}'))
return json.decode(value);
else if (value.trim().toLowerCase() == 'null')
return null;
else
return value; return value;
} }
} on FormatException {
if (value.startsWith('[') && value.endsWith(']')) {
return json.decode(value);
} else if (value.startsWith('{') && value.endsWith('}')) {
return json.decode(value);
} else if (value.trim().toLowerCase() == 'null') {
return null;
} else {
return value;
}
}
} }

View file

@ -3,19 +3,18 @@ import 'get_value.dart';
/// Parses a URI-encoded string into real data! **Wow!** /// Parses a URI-encoded string into real data! **Wow!**
/// ///
/// Whichever map you provide will be automatically populated from the urlencoded body string you provide. /// Whichever map you provide will be automatically populated from the urlencoded body string you provide.
buildMapFromUri(Map map, String body) { void buildMapFromUri(Map map, String body) {
RegExp parseArrayRgx = new RegExp(r'^(.+)\[\]$'); var parseArrayRgx = RegExp(r'^(.+)\[\]$');
for (String keyValuePair in body.split('&')) { for (var keyValuePair in body.split('&')) {
if (keyValuePair.contains('=')) { if (keyValuePair.contains('=')) {
var equals = keyValuePair.indexOf('='); var equals = keyValuePair.indexOf('=');
String key = Uri.decodeQueryComponent(keyValuePair.substring(0, equals)); var key = Uri.decodeQueryComponent(keyValuePair.substring(0, equals));
String value = var value = Uri.decodeQueryComponent(keyValuePair.substring(equals + 1));
Uri.decodeQueryComponent(keyValuePair.substring(equals + 1));
if (parseArrayRgx.hasMatch(key)) { if (parseArrayRgx.hasMatch(key)) {
Match queryMatch = parseArrayRgx.firstMatch(key); Match queryMatch = parseArrayRgx.firstMatch(key)!;
key = queryMatch.group(1); key = queryMatch.group(1)!;
if (!(map[key] is List)) { if (!(map[key] is List)) {
map[key] = []; map[key] = [];
} }
@ -23,21 +22,23 @@ buildMapFromUri(Map map, String body) {
map[key].add(getValue(value)); map[key].add(getValue(value));
} else if (key.contains('.')) { } else if (key.contains('.')) {
// i.e. map.foo.bar => [map, foo, bar] // i.e. map.foo.bar => [map, foo, bar]
List<String> keys = key.split('.'); var keys = key.split('.');
Map targetMap = map[keys[0]] != null ? map[keys[0]] as Map : {}; var targetMap = map[keys[0]] != null ? map[keys[0]] as Map? : {};
map[keys[0]] = targetMap; map[keys[0]] = targetMap;
for (int i = 1; i < keys.length; i++) { for (var i = 1; i < keys.length; i++) {
if (i < keys.length - 1) { if (i < keys.length - 1) {
targetMap[keys[i]] = targetMap[keys[i]] ?? {}; targetMap![keys[i]] = targetMap[keys[i]] ?? {};
targetMap = targetMap[keys[i]] as Map; targetMap = targetMap[keys[i]] as Map?;
} else { } else {
targetMap[keys[i]] = getValue(value); targetMap![keys[i]] = getValue(value);
} }
} }
} else } else {
map[key] = getValue(value); map[key] = getValue(value);
} else }
} else {
map[Uri.decodeQueryComponent(keyValuePair)] = true; map[Uri.decodeQueryComponent(keyValuePair)] = true;
} }
}
} }

View file

@ -1,8 +1,8 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:dart2_constant/convert.dart';
import 'package:http_parser/http_parser.dart'; import 'package:http_parser/http_parser.dart';
import 'package:http_server/http_server.dart'; import 'package:http_server/http_server.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
@ -14,11 +14,11 @@ import 'map_from_uri.dart';
/// Forwards to [parseBodyFromStream]. /// Forwards to [parseBodyFromStream].
@deprecated @deprecated
Future<BodyParseResult> parseBody(HttpRequest request, Future<BodyParseResult> parseBody(HttpRequest request,
{bool storeOriginalBuffer: false}) { {bool storeOriginalBuffer = false}) {
return parseBodyFromStream( return parseBodyFromStream(
request, request,
request.headers.contentType != null request.headers.contentType != null
? new MediaType.parse(request.headers.contentType.toString()) ? MediaType.parse(request.headers.contentType.toString())
: null, : null,
request.uri, request.uri,
storeOriginalBuffer: storeOriginalBuffer); storeOriginalBuffer: storeOriginalBuffer);
@ -33,13 +33,13 @@ Future<BodyParseResult> parseBody(HttpRequest request,
/// ///
/// Use [storeOriginalBuffer] to add the original request bytes to the result. /// Use [storeOriginalBuffer] to add the original request bytes to the result.
Future<BodyParseResult> parseBodyFromStream( Future<BodyParseResult> parseBodyFromStream(
Stream<Uint8List> data, MediaType contentType, Uri requestUri, Stream<Uint8List> data, MediaType? contentType, Uri requestUri,
{bool storeOriginalBuffer: false}) async { {bool storeOriginalBuffer = false}) async {
var result = new _BodyParseResultImpl(); var result = _BodyParseResultImpl();
Future<Uint8List> getBytes() { Future<Uint8List> getBytes() {
return data return data
.fold<BytesBuilder>(new BytesBuilder(copy: false), (a, b) => a..add(b)) .fold<BytesBuilder>(BytesBuilder(copy: false), (a, b) => a..add(b))
.then((b) => b.takeBytes()); .then((b) => b.takeBytes());
} }
@ -62,30 +62,29 @@ Future<BodyParseResult> parseBodyFromStream(
if (storeOriginalBuffer) { if (storeOriginalBuffer) {
var bytes = result.originalBuffer = await getBytes(); var bytes = result.originalBuffer = await getBytes();
var ctrl = new StreamController<Uint8List>() var ctrl = StreamController<Uint8List>()..add(bytes);
..add(bytes) await ctrl.close();
..close();
stream = ctrl.stream; stream = ctrl.stream;
} else { } else {
stream = data; stream = data;
} }
var parts = MimeMultipartTransformer( var parts = MimeMultipartTransformer(contentType.parameters['boundary']!)
contentType.parameters['boundary']).bind(stream) .bind(stream)
.map((part) => .map((part) =>
HttpMultipartFormData.parse(part, defaultEncoding: utf8)); HttpMultipartFormData.parse(part, defaultEncoding: utf8));
await for (HttpMultipartFormData part in parts) { await for (HttpMultipartFormData part in parts) {
if (part.isBinary || if (part.isBinary ||
part.contentDisposition.parameters.containsKey("filename")) { part.contentDisposition.parameters.containsKey('filename')) {
BytesBuilder builder = await part.fold( var builder = await part.fold(
new BytesBuilder(copy: false), BytesBuilder(copy: false),
(BytesBuilder b, d) => b (BytesBuilder b, d) => b
..add(d is! String ..add(d is! String
? (d as List<int>) ? (d as List<int>?)!
: (d as String).codeUnits)); : d.codeUnits));
var upload = new FileUploadInfo( var upload = FileUploadInfo(
mimeType: part.contentType.mimeType, mimeType: part.contentType!.mimeType,
name: part.contentDisposition.parameters['name'], name: part.contentDisposition.parameters['name'],
filename: filename:
part.contentDisposition.parameters['filename'] ?? 'file', part.contentDisposition.parameters['filename'] ?? 'file',
@ -99,9 +98,9 @@ Future<BodyParseResult> parseBodyFromStream(
} }
} else if (contentType.mimeType == 'application/json') { } else if (contentType.mimeType == 'application/json') {
result.body result.body
.addAll(_foldToStringDynamic(json.decode(await getBody()) as Map)); .addAll(_foldToStringDynamic(json.decode(await getBody()) as Map?)!);
} else if (contentType.mimeType == 'application/x-www-form-urlencoded') { } else if (contentType.mimeType == 'application/x-www-form-urlencoded') {
String body = await getBody(); var body = await getBody();
buildMapFromUri(result.body, body); buildMapFromUri(result.body, body);
} else if (storeOriginalBuffer == true) { } else if (storeOriginalBuffer == true) {
result.originalBuffer = await getBytes(); result.originalBuffer = await getBytes();
@ -125,25 +124,25 @@ Future<BodyParseResult> parseBodyFromStream(
class _BodyParseResultImpl implements BodyParseResult { class _BodyParseResultImpl implements BodyParseResult {
@override @override
Map<String, dynamic> body = {}; Map<String?, dynamic> body = {};
@override @override
List<FileUploadInfo> files = []; List<FileUploadInfo> files = [];
@override @override
List<int> originalBuffer; List<int>? originalBuffer;
@override @override
Map<String, dynamic> query = {}; Map<String?, dynamic> query = {};
@override @override
var error = null; var error;
@override @override
StackTrace stack = null; StackTrace? stack;
} }
Map<String, dynamic> _foldToStringDynamic(Map map) { Map<String, dynamic>? _foldToStringDynamic(Map? map) {
return map == null return map == null
? null ? null
: map.keys.fold<Map<String, dynamic>>( : map.keys.fold<Map<String, dynamic>>(

View file

@ -1,15 +1,14 @@
name: body_parser name: body_parser
author: Tobe O <thosakwe@gmail.com> version: 2.0.0
version: 1.1.1
description: Parse request bodies and query strings in Dart. Supports JSON, URL-encoded, and multi-part bodies. description: Parse request bodies and query strings in Dart. Supports JSON, URL-encoded, and multi-part bodies.
homepage: https://github.com/angel-dart/body_parser homepage: https://github.com/angel-dart/body_parser
environment: environment:
sdk: ">=2.10.0 <2.12.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
dart2_constant: ^1.0.0 http_parser: ^4.0.0
http_parser: ">=3.1.1 <4.0.0" http_server: ^1.0.0
http_server: ">=0.9.6 <1.0.0" mime: ^1.0.0
mime: ">=0.9.3 <1.0.0"
dev_dependencies: dev_dependencies:
http: ">=0.11.3 <0.12.0" http: ^0.13.0
test: ^1.15.7 test: ^1.17.0
pedantic: ^1.11.0

View file

@ -1,54 +1,54 @@
import 'dart:io'; import 'dart:io';
import 'dart:convert'; import 'dart:convert';
import 'package:body_parser/body_parser.dart'; import 'package:body_parser/body_parser.dart';
import 'package:dart2_constant/convert.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'server_test.dart'; import 'server_test.dart';
void main() { void main() {
HttpServer server; HttpServer? server;
String url; String? url;
http.Client client; http.Client? client;
setUp(() async { setUp(() async {
server = await HttpServer.bind('127.0.0.1', 0); server = await HttpServer.bind('127.0.0.1', 0);
server.listen((HttpRequest request) async { server!.listen((HttpRequest request) async {
//Server will simply return a JSON representation of the parsed body //Server will simply return a JSON representation of the parsed body
// ignore: deprecated_member_use // ignore: deprecated_member_use
request.response.write(jsonEncodeBody(await parseBody(request))); request.response.write(jsonEncodeBody(await parseBody(request)));
await request.response.close(); await request.response.close();
}); });
url = 'http://localhost:${server.port}'; url = 'http://localhost:${server!.port}';
print('Test server listening on $url'); print('Test server listening on $url');
client = http.Client(); client = http.Client();
}); });
tearDown(() async { tearDown(() async {
await server.close(force: true); await server!.close(force: true);
client.close(); client!.close();
server = null; server = null;
url = null; url = null;
client = null; client = null;
}); });
test('No upload', () async { test('No upload', () async {
String boundary = 'myBoundary'; var boundary = 'myBoundary';
Map<String, String> headers = { var headers = <String, String>{
'content-type': 'multipart/form-data; boundary=$boundary' 'content-type': 'multipart/form-data; boundary=$boundary'
}; };
String postData = ''' var postData = '''
--$boundary --$boundary
Content-Disposition: form-data; name="hello" Content-Disposition: form-data; name="hello"
world world
--$boundary-- --$boundary--
''' '''
.replaceAll("\n", "\r\n"); .replaceAll('\n', '\r\n');
print( print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}'); 'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response = await client.post(url, headers: headers, body: postData); var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}'); print('Response: ${response.body}');
var jsons = json.decode(response.body); var jsons = json.decode(response.body);
var files = jsons['files'].map((map) { var files = jsons['files'].map((map) {
@ -62,12 +62,12 @@ world
}); });
test('Single upload', () async { test('Single upload', () async {
String boundary = 'myBoundary'; var boundary = 'myBoundary';
Map<String, String> headers = { var headers = <String, String>{
'content-type': ContentType("multipart", "form-data", 'content-type': ContentType('multipart', 'form-data',
parameters: {"boundary": boundary}).toString() parameters: {'boundary': boundary}).toString()
}; };
String postData = ''' var postData = '''
--$boundary --$boundary
Content-Disposition: form-data; name="hello" Content-Disposition: form-data; name="hello"
@ -79,11 +79,12 @@ Content-Type: application/dart
Hello world Hello world
--$boundary-- --$boundary--
''' '''
.replaceAll("\n", "\r\n"); .replaceAll('\n', '\r\n');
print( print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}'); 'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response = await client.post(url, headers: headers, body: postData); var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}'); print('Response: ${response.body}');
var jsons = json.decode(response.body); var jsons = json.decode(response.body);
var files = jsons['files']; var files = jsons['files'];
@ -96,11 +97,11 @@ Hello world
}); });
test('Multiple upload', () async { test('Multiple upload', () async {
String boundary = 'myBoundary'; var boundary = 'myBoundary';
Map<String, String> headers = { var headers = <String, String>{
'content-type': 'multipart/form-data; boundary=$boundary' 'content-type': 'multipart/form-data; boundary=$boundary'
}; };
String postData = ''' var postData = '''
--$boundary --$boundary
Content-Disposition: form-data; name="json" Content-Disposition: form-data; name="json"
@ -123,11 +124,12 @@ function main() {
} }
--$boundary-- --$boundary--
''' '''
.replaceAll("\n", "\r\n"); .replaceAll('\n', '\r\n');
print( print(
'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}'); 'Form Data: \n${postData.replaceAll("\r", "\\r").replaceAll("\n", "\\n")}');
var response = await client.post(url, headers: headers, body: postData); var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}'); print('Response: ${response.body}');
var jsons = json.decode(response.body); var jsons = json.decode(response.body);
var files = jsons['files']; var files = jsons['files'];

View file

@ -1,7 +1,7 @@
import 'dart:convert';
import 'dart:io' show HttpRequest, HttpServer; import 'dart:io' show HttpRequest, HttpServer;
import 'package:body_parser/body_parser.dart'; import 'package:body_parser/body_parser.dart';
import 'package:dart2_constant/convert.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'package:test/test.dart'; import 'package:test/test.dart';
@ -27,26 +27,26 @@ String jsonEncodeBody(BodyParseResult result) {
} }
void main() { void main() {
HttpServer server; HttpServer? server;
String url; String? url;
http.Client client; http.Client? client;
setUp(() async { setUp(() async {
server = await HttpServer.bind('127.0.0.1', 0); server = await HttpServer.bind('127.0.0.1', 0);
server.listen((HttpRequest request) async { server!.listen((HttpRequest request) async {
//Server will simply return a JSON representation of the parsed body //Server will simply return a JSON representation of the parsed body
request.response.write( request.response.write(
// ignore: deprecated_member_use // ignore: deprecated_member_use
jsonEncodeBody(await parseBody(request, storeOriginalBuffer: true))); jsonEncodeBody(await parseBody(request, storeOriginalBuffer: true)));
await request.response.close(); await request.response.close();
}); });
url = 'http://localhost:${server.port}'; url = 'http://localhost:${server!.port}';
print('Test server listening on $url'); print('Test server listening on $url');
client = http.Client(); client = http.Client();
}); });
tearDown(() async { tearDown(() async {
await server.close(force: true); await server!.close(force: true);
client.close(); client!.close();
server = null; server = null;
url = null; url = null;
client = null; client = null;
@ -55,7 +55,7 @@ void main() {
group('query string', () { group('query string', () {
test('GET Simple', () async { test('GET Simple', () async {
print('GET $url/?hello=world'); print('GET $url/?hello=world');
var response = await client.get('$url/?hello=world'); var response = await client!.get(Uri.parse('$url/?hello=world'));
print('Response: ${response.body}'); print('Response: ${response.body}');
var result = json.decode(response.body); var result = json.decode(response.body);
expect(result['body'], equals({})); expect(result['body'], equals({}));
@ -68,7 +68,7 @@ void main() {
var postData = var postData =
'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz'; 'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz';
print('Body: $postData'); print('Body: $postData');
var response = await client.get('$url/?$postData'); var response = await client!.get(Uri.parse('$url/?$postData'));
print('Response: ${response.body}'); print('Response: ${response.body}');
var query = json.decode(response.body)['query']; var query = json.decode(response.body)['query'];
expect(query['hello'], equals('world')); expect(query['hello'], equals('world'));
@ -80,7 +80,7 @@ void main() {
test('JWT', () async { test('JWT', () async {
var postData = 'token=$TOKEN'; var postData = 'token=$TOKEN';
print('Body: $postData'); print('Body: $postData');
var response = await client.get('$url/?$postData'); var response = await client!.get(Uri.parse('$url/?$postData'));
print('Response: ${response.body}'); print('Response: ${response.body}');
var query = json.decode(response.body)['query']; var query = json.decode(response.body)['query'];
expect(query['token'], equals(TOKEN)); expect(query['token'], equals(TOKEN));
@ -88,13 +88,13 @@ void main() {
}); });
group('urlencoded', () { group('urlencoded', () {
Map<String, String> headers = { var headers = <String, String>{
'content-type': 'application/x-www-form-urlencoded' 'content-type': 'application/x-www-form-urlencoded'
}; };
test('POST Simple', () async { test('POST Simple', () async {
print('Body: hello=world'); print('Body: hello=world');
var response = var response = await client!.post(Uri.parse(url!),
await client.post(url, headers: headers, body: 'hello=world'); headers: headers, body: 'hello=world');
print('Response: ${response.body}'); print('Response: ${response.body}');
var result = json.decode(response.body); var result = json.decode(response.body);
expect(result['query'], equals({})); expect(result['query'], equals({}));
@ -107,7 +107,8 @@ void main() {
test('Post Complex', () async { test('Post Complex', () async {
var postData = var postData =
'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz'; 'hello=world&nums%5B%5D=1&nums%5B%5D=2.0&nums%5B%5D=${3 - 1}&map.foo.bar=baz';
var response = await client.post(url, headers: headers, body: postData); var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}'); print('Response: ${response.body}');
var body = json.decode(response.body)['body']; var body = json.decode(response.body)['body'];
expect(body['hello'], equals('world')); expect(body['hello'], equals('world'));
@ -118,18 +119,20 @@ void main() {
test('JWT', () async { test('JWT', () async {
var postData = 'token=$TOKEN'; var postData = 'token=$TOKEN';
var response = await client.post(url, headers: headers, body: postData); var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
var body = json.decode(response.body)['body']; var body = json.decode(response.body)['body'];
expect(body['token'], equals(TOKEN)); expect(body['token'], equals(TOKEN));
}); });
}); });
group('json', () { group('json', () {
Map<String, String> headers = {'content-type': 'application/json'}; var headers = <String, String>{'content-type': 'application/json'};
test('Post Simple', () async { test('Post Simple', () async {
var postData = json.encode({'hello': 'world'}); var postData = json.encode({'hello': 'world'});
print('Body: $postData'); print('Body: $postData');
var response = await client.post(url, headers: headers, body: postData); var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}'); print('Response: ${response.body}');
var result = json.decode(response.body); var result = json.decode(response.body);
expect(result['body'], equals({'hello': 'world'})); expect(result['body'], equals({'hello': 'world'}));
@ -147,7 +150,8 @@ void main() {
} }
}); });
print('Body: $postData'); print('Body: $postData');
var response = await client.post(url, headers: headers, body: postData); var response =
await client!.post(Uri.parse(url!), headers: headers, body: postData);
print('Response: ${response.body}'); print('Response: ${response.body}');
var body = json.decode(response.body)['body']; var body = json.decode(response.body)['body'];
expect(body['hello'], equals('world')); expect(body['hello'], equals('world'));

12
packages/html/AUTHORS.md Normal file
View 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.

View file

@ -1,2 +1,5 @@
# 3.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 2.0.0 # 2.0.0
* Angel 2 + Dart 2 updates. * Angel 2 + Dart 2 updates.

View file

@ -1,6 +1,6 @@
MIT License (MIT) MIT License
Copyright (c) 2021 dukefirehawk.com Copyright (c) 2017 Tobe O
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -4,8 +4,9 @@ import 'package:angel_html/angel_html.dart';
import 'package:html_builder/elements.dart'; import 'package:html_builder/elements.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
main() async { void main() async {
var app = Angel(), http = AngelHttp(app); var app = Angel();
var http = AngelHttp(app);
app.logger = Logger('angel_html') app.logger = Logger('angel_html')
..onRecord.listen((rec) { ..onRecord.listen((rec) {
print(rec); print(rec);
@ -29,7 +30,7 @@ main() async {
renderHtml( renderHtml(
enforceAcceptHeader: true, enforceAcceptHeader: true,
renderer: StringRenderer( renderer: StringRenderer(
doctype: null, //doctype: null,
pretty: false, pretty: false,
), ),
), ),

View file

@ -7,20 +7,21 @@ import 'package:html_builder/html_builder.dart';
/// You can provide a custom [renderer]. The default renders minified HTML5 pages. /// 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. /// Set [enforceAcceptHeader] to `true` to throw a `406 Not Acceptable` if the client doesn't accept HTML responses.
RequestHandler renderHtml({StringRenderer renderer, bool enforceAcceptHeader}) { RequestHandler renderHtml({StringRenderer? renderer, bool? enforceAcceptHeader}) {
renderer ??= new StringRenderer(pretty: false, html5: true); renderer ??= StringRenderer(pretty: false, html5: true);
return (RequestContext req, ResponseContext res) { return (RequestContext req, ResponseContext res) {
var oldSerializer = res.serializer; var oldSerializer = res.serializer;
res.serializer = (data) { res.serializer = (data) {
if (data is! Node) if (data is! Node) {
return oldSerializer(data); return oldSerializer!(data);
else { } else {
if (enforceAcceptHeader == true && !req.accepts('text/html')) if (enforceAcceptHeader == true && !req.accepts('text/html')) {
throw new AngelHttpException.notAcceptable(); throw AngelHttpException.notAcceptable();
}
var content = renderer.render(data as Node); var content = renderer!.render(data);
res res
..headers['content-type'] = 'text/html' ..headers['content-type'] = 'text/html'
..write(content); ..write(content);
@ -29,6 +30,6 @@ RequestHandler renderHtml({StringRenderer renderer, bool enforceAcceptHeader}) {
} }
}; };
return new Future<bool>.value(true); return Future<bool>.value(true);
}; };
} }

View file

@ -1,18 +1,28 @@
name: angel_html name: angel_html
version: 2.0.0 version: 3.0.0
description: Support for rendering html_builder AST's as responses in Angel. 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 homepage: https://github.com/angel-dart/html_builder
publish_to: none
environment: environment:
sdk: ">=2.10.0 <2.12.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel_framework: #^2.0.0-alpha angel_framework:
path: ../framework git:
html_builder: ^1.0.0 url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/framework
html_builder:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/html_builder
dev_dependencies: dev_dependencies:
angel_test: #^2.0.0-alpha angel_test:
path: ../test git:
html: ^0.13.2 url: https://github.com/dukefirehawk/angel.git
logging: ^0.11.0 ref: sdk-2.12.x_nnbd
test: ^1.15.7 path: packages/test
pedantic: ^1.0.0 html: ^0.15.0
logging: ^1.0.1
test: ^1.17.0
pedantic: ^1.11.0

View file

@ -5,12 +5,12 @@ import 'package:html_builder/elements.dart';
import 'package:html_builder/html_builder.dart'; import 'package:html_builder/html_builder.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
main() { void main() {
Angel app; Angel app;
TestClient client; late TestClient client;
setUp(() async { setUp(() async {
app = new Angel(); app = Angel();
app.fallback(renderHtml()); app.fallback(renderHtml());
@ -27,8 +27,8 @@ main() {
chain([ chain([
renderHtml( renderHtml(
enforceAcceptHeader: true, enforceAcceptHeader: true,
renderer: new StringRenderer( renderer: StringRenderer(
doctype: null, //doctype: null,
pretty: false, pretty: false,
), ),
), ),
@ -43,7 +43,7 @@ main() {
tearDown(() => client.close()); tearDown(() => client.close());
test('sets content type and body', () async { test('sets content type and body', () async {
var response = await client.get('/html'); var response = await client.get(Uri.parse('/html'));
print('Response: ${response.body}'); print('Response: ${response.body}');
expect( expect(
@ -56,12 +56,13 @@ main() {
group('enforce accept header', () { group('enforce accept header', () {
test('sends if correct accept or wildcard', () async { test('sends if correct accept or wildcard', () async {
var response = await client.get('/strict', headers: {'accept': '*/*'}); var response =
await client.get(Uri.parse('/strict'), headers: {'accept': '*/*'});
print('Response: ${response.body}'); print('Response: ${response.body}');
expect(response, expect(response,
allOf(hasContentType('text/html'), hasBody('<div>strict</div>'))); allOf(hasContentType('text/html'), hasBody('<div>strict</div>')));
response = await client.get('/strict', response = await client.get(Uri.parse('/strict'),
headers: {'accept': 'text/html,application/json,text/xml'}); headers: {'accept': 'text/html,application/json,text/xml'});
print('Response: ${response.body}'); print('Response: ${response.body}');
expect(response, expect(response,
@ -69,12 +70,12 @@ main() {
}); });
test('throws if incorrect or no accept', () async { test('throws if incorrect or no accept', () async {
var response = await client.get('/strict'); var response = await client.get(Uri.parse('/strict'));
print('Response: ${response.body}'); print('Response: ${response.statusCode} ${response.body}');
expect(response, hasStatus(406)); expect(response, hasStatus(406));
response = await client response = await client.get(Uri.parse('/strict'),
.get('/strict', headers: {'accept': 'application/json,text/xml'}); headers: {'accept': 'application/json,text/xml'});
print('Response: ${response.body}'); print('Response: ${response.body}');
expect(response, expect(response,
isAngelHttpException(statusCode: 406, message: '406 Not Acceptable')); isAngelHttpException(statusCode: 406, message: '406 Not Acceptable'));

View file

@ -1,3 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer: analyzer:
strong-mode: strong-mode:
implicit-casts: false implicit-casts: false

View file

@ -7,8 +7,8 @@ import 'package:io/io.dart';
import 'package:jael_language_server/jael_language_server.dart'; import 'package:jael_language_server/jael_language_server.dart';
import 'package:jael_language_server/src/protocol/language_server/server.dart'; import 'package:jael_language_server/src/protocol/language_server/server.dart';
main(List<String> args) async { void main(List<String> args) async {
var argParser = new ArgParser() var argParser = ArgParser()
..addFlag('help', ..addFlag('help',
abbr: 'h', negatable: false, help: 'Print this help information.') abbr: 'h', negatable: false, help: 'Print this help information.')
..addOption('log-file', help: 'A path to which to write a log file.'); ..addOption('log-file', help: 'A path to which to write a log file.');
@ -25,14 +25,14 @@ main(List<String> args) async {
printUsage(); printUsage();
return; return;
} else { } else {
var jaelServer = new JaelLanguageServer(); var jaelServer = JaelLanguageServer();
if (argResults.wasParsed('log-file')) { if (argResults.wasParsed('log-file')) {
var f = new File(argResults['log-file'] as String); var f = File(argResults['log-file'] as String);
await f.create(recursive: true); await f.create(recursive: true);
jaelServer.logger.onRecord.listen((rec) async { jaelServer.logger.onRecord.listen((rec) async {
var sink = await f.openWrite(mode: FileMode.append); var sink = f.openWrite(mode: FileMode.append);
sink.writeln(rec); sink.writeln(rec);
if (rec.error != null) sink.writeln(rec.error); if (rec.error != null) sink.writeln(rec.error);
if (rec.stackTrace != null) sink.writeln(rec.stackTrace); if (rec.stackTrace != null) sink.writeln(rec.stackTrace);
@ -47,7 +47,7 @@ main(List<String> args) async {
}); });
} }
var spec = new ZoneSpecification( var spec = ZoneSpecification(
handleUncaughtError: (self, parent, zone, error, stackTrace) { handleUncaughtError: (self, parent, zone, error, stackTrace) {
jaelServer.logger.severe('Uncaught', error, stackTrace); jaelServer.logger.severe('Uncaught', error, stackTrace);
}, },
@ -57,7 +57,7 @@ main(List<String> args) async {
); );
var zone = Zone.current.fork(specification: spec); var zone = Zone.current.fork(specification: spec);
await zone.run(() async { await zone.run(() async {
var stdio = new StdIOLanguageServer.start(jaelServer); var stdio = StdIOLanguageServer.start(jaelServer);
await stdio.onDone; await stdio.onDone;
}); });
} }

View file

@ -7,18 +7,19 @@ class Analyzer extends Parser {
final Logger logger; final Logger logger;
Analyzer(Scanner scanner, this.logger) : super(scanner); Analyzer(Scanner scanner, this.logger) : super(scanner);
@override
final errors = <JaelError>[]; final errors = <JaelError>[];
var _scope = new SymbolTable<JaelObject>(); SymbolTable<JaelObject>? _scope = SymbolTable<JaelObject>();
var allDefinitions = <Variable<JaelObject>>[]; var allDefinitions = <Variable<JaelObject>>[];
SymbolTable<JaelObject> get parentScope => SymbolTable<JaelObject>? get parentScope =>
_scope.isRoot ? _scope : _scope.parent; _scope!.isRoot ? _scope : _scope!.parent;
SymbolTable<JaelObject> get scope => _scope; SymbolTable<JaelObject>? get scope => _scope;
bool ensureAttributeIsPresent(Element element, String name) { bool ensureAttributeIsPresent(Element element, String name) {
if (element.getAttribute(name)?.value == null) { if (element.getAttribute(name)?.value == null) {
addError(new JaelError(JaelErrorSeverity.error, addError(JaelError(JaelErrorSeverity.error,
'Missing required attribute `$name`.', element.span)); 'Missing required attribute `$name`.', element.span));
return false; return false;
} }
@ -33,9 +34,9 @@ class Analyzer extends Parser {
bool ensureAttributeIsConstantString(Element element, String name) { bool ensureAttributeIsConstantString(Element element, String name) {
var a = element.getAttribute(name); var a = element.getAttribute(name);
if (a?.value is! StringLiteral || a?.value == null) { if (a?.value is! StringLiteral || a?.value == null) {
var e = new JaelError( var e = JaelError(
JaelErrorSeverity.warning, JaelErrorSeverity.warning,
"`$name` attribute should be a constant string literal.", '`$name` attribute should be a constant string literal.',
a?.span ?? element.tagName.span); a?.span ?? element.tagName.span);
addError(e); addError(e);
return false; return false;
@ -45,18 +46,18 @@ class Analyzer extends Parser {
} }
@override @override
Element parseElement() { Element? parseElement() {
try { try {
_scope = _scope.createChild(); _scope = _scope!.createChild();
var element = super.parseElement(); var element = super.parseElement();
if (element == null) return null; if (element == null) return null;
// Check if any custom element exists. // Check if any custom element exists.
_scope _scope!
.resolve(element.tagName.name) .resolve(element.tagName.name)
?.value ?.value
?.usages ?.usages
?.add(new SymbolUsage(SymbolUsageType.read, element.span)); .add(SymbolUsage(SymbolUsageType.read, element.span));
// Validate attrs // Validate attrs
var forEach = element.getAttribute('for-each'); var forEach = element.getAttribute('for-each');
@ -64,14 +65,14 @@ class Analyzer extends Parser {
var asAttr = element.getAttribute('as'); var asAttr = element.getAttribute('as');
if (asAttr != null) { if (asAttr != null) {
if (ensureAttributeIsConstantString(element, 'as')) { if (ensureAttributeIsConstantString(element, 'as')) {
var asName = asAttr.string.value; var asName = asAttr.string!.value;
_scope.create(asName, _scope!.create(asName,
value: new JaelVariable(asName, asAttr.span), constant: true); value: JaelVariable(asName, asAttr.span), constant: true);
} }
} }
if (forEach.value != null) { if (forEach.value != null) {
addError(new JaelError(JaelErrorSeverity.error, addError(JaelError(JaelErrorSeverity.error,
'Missing value for `for-each` directive.', forEach.span)); 'Missing value for `for-each` directive.', forEach.span));
} }
} }
@ -79,7 +80,7 @@ class Analyzer extends Parser {
var iff = element.getAttribute('if'); var iff = element.getAttribute('if');
if (iff != null) { if (iff != null) {
if (iff.value != null) { if (iff.value != null) {
addError(new JaelError(JaelErrorSeverity.error, addError(JaelError(JaelErrorSeverity.error,
'Missing value for `iff` directive.', iff.span)); 'Missing value for `iff` directive.', iff.span));
} }
} }
@ -94,29 +95,29 @@ class Analyzer extends Parser {
//logger.info('Found <case> at ${element.span.start.toolString}'); //logger.info('Found <case> at ${element.span.start.toolString}');
} else if (element.tagName.name == 'declare') { } else if (element.tagName.name == 'declare') {
if (element.attributes.isEmpty) { if (element.attributes.isEmpty) {
addError(new JaelError( addError(JaelError(
JaelErrorSeverity.warning, JaelErrorSeverity.warning,
'`declare` directive does not define any new symbols.', '`declare` directive does not define any new symbols.',
element.tagName.span)); element.tagName.span));
} else { } else {
for (var attr in element.attributes) { for (var attr in element.attributes) {
_scope.create(attr.name, _scope!
value: new JaelVariable(attr.name, attr.span)); .create(attr.name, value: JaelVariable(attr.name, attr.span));
} }
} }
} else if (element.tagName.name == 'element') { } else if (element.tagName.name == 'element') {
if (ensureAttributeIsConstantString(element, 'name')) { if (ensureAttributeIsConstantString(element, 'name')) {
var nameCtx = element.getAttribute('name').value as StringLiteral; var nameCtx = element.getAttribute('name')!.value as StringLiteral;
var name = nameCtx.value; var name = nameCtx.value;
//logger.info( //logger.info(
// 'Found custom element $name at ${element.span.start.toolString}'); // 'Found custom element $name at ${element.span.start.toolString}');
try { try {
var symbol = parentScope.create(name, var symbol = parentScope!.create(name,
value: new JaelCustomElement(name, element.tagName.span), value: JaelCustomElement(name, element.tagName.span),
constant: true); constant: true);
allDefinitions.add(symbol); allDefinitions.add(symbol);
} on StateError catch (e) { } on StateError catch (e) {
addError(new JaelError( addError(JaelError(
JaelErrorSeverity.error, e.message, element.tagName.span)); JaelErrorSeverity.error, e.message, element.tagName.span));
} }
} }
@ -133,19 +134,19 @@ class Analyzer extends Parser {
return element; return element;
} finally { } finally {
_scope = _scope.parent; _scope = _scope!.parent;
return null; return null;
} }
} }
@override @override
Expression parseExpression(int precedence) { Expression? parseExpression(int precedence) {
var expr = super.parseExpression(precedence); var expr = super.parseExpression(precedence);
if (expr == null) return null; if (expr == null) return null;
if (expr is Identifier) { if (expr is Identifier) {
var ref = _scope.resolve(expr.name); var ref = _scope!.resolve(expr.name);
ref?.value?.usages?.add(new SymbolUsage(SymbolUsageType.read, expr.span)); ref?.value?.usages.add(SymbolUsage(SymbolUsageType.read, expr.span));
} }
return expr; return expr;

View file

@ -11,13 +11,15 @@ abstract class JaelObject {
} }
class JaelCustomElement extends JaelObject { class JaelCustomElement extends JaelObject {
@override
final String name; final String name;
final attributes = new SplayTreeSet<String>(); final attributes = SplayTreeSet<String>();
JaelCustomElement(this.name, FileSpan span) : super(span); JaelCustomElement(this.name, FileSpan span) : super(span);
} }
class JaelVariable extends JaelObject { class JaelVariable extends JaelObject {
@override
final String name; final String name;
JaelVariable(this.name, FileSpan span) : super(span); JaelVariable(this.name, FileSpan span) : super(span);
} }

View file

@ -13,8 +13,8 @@ abstract class LanguageServer {
_onDone.complete(); _onDone.complete();
} }
Future<ServerCapabilities> initialize(int clientPid, String rootUri, Future<ServerCapabilities> initialize(int? clientPid, String? rootUri,
ClientCapabilities clientCapabilities, String trace) async => ClientCapabilities clientCapabilities, String? trace) async =>
ServerCapabilities((b) => b); ServerCapabilities((b) => b);
void initialized() {} void initialized() {}
void textDocumentDidOpen(TextDocumentItem document) {} void textDocumentDidOpen(TextDocumentItem document) {}
@ -24,7 +24,7 @@ abstract class LanguageServer {
Future<CompletionList> textDocumentCompletion( Future<CompletionList> textDocumentCompletion(
TextDocumentIdentifier documentId, Position position) async => TextDocumentIdentifier documentId, Position position) async =>
CompletionList((b) => b); CompletionList((b) => b);
Future<Location> textDocumentDefinition( Future<Location?> textDocumentDefinition(
TextDocumentIdentifier documentId, Position position) async => TextDocumentIdentifier documentId, Position position) async =>
null; null;
Future<List<Location>> textDocumentReferences( Future<List<Location>> textDocumentReferences(
@ -41,7 +41,7 @@ abstract class LanguageServer {
Future<List<SymbolInformation>> textDocumentSymbols( Future<List<SymbolInformation>> textDocumentSymbols(
TextDocumentIdentifier documentId) async => TextDocumentIdentifier documentId) async =>
[]; [];
Future<List<SymbolInformation>> workspaceSymbol(String query) async => []; Future<List<SymbolInformation>> workspaceSymbol(String? query) async => [];
Future<dynamic> textDocumentHover( Future<dynamic> textDocumentHover(
TextDocumentIdentifier documentId, Position position) async => TextDocumentIdentifier documentId, Position position) async =>
null; null;
@ -51,13 +51,13 @@ abstract class LanguageServer {
CodeActionContext context) async => CodeActionContext context) async =>
[]; [];
Future<void> workspaceExecuteCommand( Future<void> workspaceExecuteCommand(
String command, List<dynamic> arguments) async {} String? command, List<dynamic>? arguments) async {}
Future<WorkspaceEdit> textDocumentRename(TextDocumentIdentifier documentId, Future<WorkspaceEdit?> textDocumentRename(TextDocumentIdentifier documentId,
Position position, String newName) async => Position position, String? newName) async =>
null; null;
Stream<Diagnostics> get diagnostics => Stream.empty(); Stream<Diagnostics> get diagnostics => Stream.empty();
Stream<ApplyWorkspaceEditParams> get workspaceEdits => Stream.empty(); Stream<ApplyWorkspaceEditParams> get workspaceEdits => Stream.empty();
Stream<ShowMessageParams> get showMessages => Stream.empty(); Stream<ShowMessageParams>? get showMessages => Stream.empty();
Stream<ShowMessageParams> get logMessages => Stream.empty(); Stream<ShowMessageParams> get logMessages => Stream.empty();
void setupExtraMethods(Peer peer) {} void setupExtraMethods(Peer peer) {}

View file

@ -10,7 +10,7 @@ import 'wireformat.dart';
/// A Language Server communicating over stdin and stdout. /// A Language Server communicating over stdin and stdout.
class StdIOLanguageServer { class StdIOLanguageServer {
final LanguageServer _server; final LanguageServer _server;
Future<void> onDone; Future<void>? onDone;
/// Wrap [_server] and register RPC methods using the LSP wire protocol. /// Wrap [_server] and register RPC methods using the LSP wire protocol.
/// ///
@ -38,10 +38,10 @@ class StdIOLanguageServer {
peer peer
..registerMethod('initialize', (params) async { ..registerMethod('initialize', (params) async {
final serverCapabilities = await _server.initialize( final serverCapabilities = await _server.initialize(
params['processId'].valueOr(0) as int, params['processId'].valueOr(0) as int?,
params['rootUri'].valueOr('') as String, params['rootUri'].valueOr('') as String?,
ClientCapabilities.fromJson(params['capabilities'].value as Map), ClientCapabilities.fromJson(params['capabilities'].value as Map),
params['trace'].valueOr('off') as String); params['trace'].valueOr('off') as String?);
_isInitialized = true; _isInitialized = true;
return {'capabilities': serverCapabilities.toJson()}; return {'capabilities': serverCapabilities.toJson()};
}) })
@ -90,7 +90,7 @@ class StdIOLanguageServer {
}) })
..logMessages.map((e) => e.toJson()).forEach( ..logMessages.map((e) => e.toJson()).forEach(
(message) => peer.sendNotification('window/logMessage', message)) (message) => peer.sendNotification('window/logMessage', message))
..showMessages.map((e) => e.toJson()).forEach( ..showMessages!.map((e) => e.toJson()).forEach(
(message) => peer.sendNotification('window/showMessage', message)); (message) => peer.sendNotification('window/showMessage', message));
} }
@ -122,31 +122,31 @@ class StdIOLanguageServer {
(params) => _server (params) => _server
.textDocumentReferences( .textDocumentReferences(
_document(params), _position(params), _referenceContext(params)) _document(params), _position(params), _referenceContext(params))
.then((r) => r?.map((e) => e.toJson())?.toList())); .then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest( _registerRequest(
peer, peer,
'textDocument/implementation', 'textDocument/implementation',
(params) => _server (params) => _server
.textDocumentImplementation(_document(params), _position(params)) .textDocumentImplementation(_document(params), _position(params))
.then((r) => r?.map((e) => e.toJson())?.toList())); .then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest( _registerRequest(
peer, peer,
'textDocument/documentHighlight', 'textDocument/documentHighlight',
(params) => _server (params) => _server
.textDocumentHighlight(_document(params), _position(params)) .textDocumentHighlight(_document(params), _position(params))
.then((r) => r?.map((e) => e.toJson())?.toList())); .then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest( _registerRequest(
peer, peer,
'textDocument/documentSymbol', 'textDocument/documentSymbol',
(params) => _server (params) => _server
.textDocumentSymbols(_document(params)) .textDocumentSymbols(_document(params))
.then((r) => r?.map((e) => e.toJson())?.toList())); .then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest( _registerRequest(
peer, peer,
'workspace/symbol', 'workspace/symbol',
(params) => _server (params) => _server
.workspaceSymbol(_query(params)) .workspaceSymbol(_query(params))
.then((r) => r?.map((e) => e.toJson())?.toList())); .then((r) => r.map((e) => e.toJson()).toList()));
} }
void _codeActionMethods(Peer peer) { void _codeActionMethods(Peer peer) {
@ -156,18 +156,18 @@ class StdIOLanguageServer {
(params) => _server (params) => _server
.textDocumentCodeAction( .textDocumentCodeAction(
_document(params), _range(params), _codeActionContext(params)) _document(params), _range(params), _codeActionContext(params))
.then((r) => r?.map((e) => e.toJson())?.toList())); .then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest( _registerRequest(
peer, peer,
'workspace/executeCommand', 'workspace/executeCommand',
(params) => _server.workspaceExecuteCommand( (params) => _server.workspaceExecuteCommand(
params['command'].value as String, params['command'].value as String?,
params['arguments']?.value as List)); params['arguments']?.value as List?));
_registerRequest( _registerRequest(
peer, peer,
'textDocument/rename', 'textDocument/rename',
(params) async => (await _server.textDocumentRename(_document(params), (params) async => (await _server.textDocumentRename(_document(params),
_position(params), params['newName'].value as String)) _position(params), params['newName'].value as String?))!
.toJson()); .toJson());
} }
} }
@ -198,4 +198,4 @@ List<TextDocumentContentChangeEvent> _contentChanges(params) =>
.map((change) => TextDocumentContentChangeEvent.fromJson(change as Map)) .map((change) => TextDocumentContentChangeEvent.fromJson(change as Map))
.toList(); .toList();
String _query(params) => params['query'].value as String; String? _query(params) => params['query'].value as String?;

View file

@ -33,7 +33,7 @@ class _Parser {
bool _headerMode = true; bool _headerMode = true;
int _contentLength = -1; int _contentLength = -1;
StreamSubscription _subscription; late StreamSubscription _subscription;
_Parser(Stream<List<int>> stream) { _Parser(Stream<List<int>> stream) {
_subscription = _subscription =

View file

@ -17,14 +17,14 @@ import 'protocol/language_server/interface.dart';
import 'protocol/language_server/messages.dart'; import 'protocol/language_server/messages.dart';
class JaelLanguageServer extends LanguageServer { class JaelLanguageServer extends LanguageServer {
var _diagnostics = new StreamController<Diagnostics>(); final _diagnostics = StreamController<Diagnostics>();
var _done = new Completer(); final _done = Completer();
var _memFs = new MemoryFileSystem(); final _memFs = MemoryFileSystem();
var _localFs = const LocalFileSystem(); final _localFs = const LocalFileSystem();
Directory _localRootDir, _memRootDir; Directory? _localRootDir, _memRootDir;
var logger = new Logger('jael'); var logger = Logger('jael');
Uri _rootUri; late Uri _rootUri;
var _workspaceEdits = new StreamController<ApplyWorkspaceEditParams>(); final _workspaceEdits = StreamController<ApplyWorkspaceEditParams>();
@override @override
Stream<Diagnostics> get diagnostics => _diagnostics.stream; Stream<Diagnostics> get diagnostics => _diagnostics.stream;
@ -48,24 +48,24 @@ class JaelLanguageServer extends LanguageServer {
peer.registerMethod('textDocument/formatting', peer.registerMethod('textDocument/formatting',
(json_rpc_2.Parameters params) async { (json_rpc_2.Parameters params) async {
var documentId = var documentId =
new TextDocumentIdentifier.fromJson(params['textDocument'].asMap); TextDocumentIdentifier.fromJson(params['textDocument'].asMap);
var formattingOptions = var formattingOptions =
new FormattingOptions.fromJson(params['options'].asMap); FormattingOptions.fromJson(params['options'].asMap);
return await textDocumentFormatting(documentId, formattingOptions); return await textDocumentFormatting(documentId, formattingOptions);
}); });
} }
@override @override
Future<ServerCapabilities> initialize(int clientPid, String rootUri, Future<ServerCapabilities> initialize(int? clientPid, String? rootUri,
ClientCapabilities clientCapabilities, String trace) async { ClientCapabilities clientCapabilities, String? trace) async {
// Find our real root dir. // Find our real root dir.
_localRootDir = _localFs.directory(_rootUri = Uri.parse(rootUri)); _localRootDir = _localFs.directory(_rootUri = Uri.parse(rootUri!));
_memRootDir = _memFs.directory('/'); _memRootDir = _memFs.directory('/');
await _memRootDir.create(recursive: true); await _memRootDir!.create(recursive: true);
_memFs.currentDirectory = _memRootDir; _memFs.currentDirectory = _memRootDir;
// Copy all real files that end in *.jael (and *.jl for legacy) into the in-memory filesystem. // Copy all real files that end in *.jael (and *.jl for legacy) into the in-memory filesystem.
await for (var entity in _localRootDir.list(recursive: true)) { await for (var entity in _localRootDir!.list(recursive: true)) {
if (entity is File && p.extension(entity.path) == '.jael') { if (entity is File && p.extension(entity.path) == '.jael') {
logger.info('HEY ${entity.path}'); logger.info('HEY ${entity.path}');
var file = _memFs.file(entity.absolute.path); var file = _memFs.file(entity.absolute.path);
@ -78,24 +78,24 @@ class JaelLanguageServer extends LanguageServer {
'Found Jael file ${file.path}; copied to ${file.absolute.path}'); 'Found Jael file ${file.path}; copied to ${file.absolute.path}');
// Analyze it // Analyze it
var documentId = new TextDocumentIdentifier((b) { var documentId = TextDocumentIdentifier((b) {
b..uri = _rootUri.replace(path: file.path).toString(); b.uri = _rootUri.replace(path: file.path).toString();
}); });
await analyzerForId(documentId); await analyzerForId(documentId);
} }
} }
return new ServerCapabilities((b) { return ServerCapabilities((b) {
b b
..codeActionProvider = false ..codeActionProvider = false
..completionProvider = new CompletionOptions((b) { ..completionProvider = CompletionOptions((b) {
b b
..resolveProvider = true ..resolveProvider = true
..triggerCharacters = ..triggerCharacters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdeghijklmnopqrstuvxwyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdeghijklmnopqrstuvxwyz'
.codeUnits .codeUnits
.map((c) => new String.fromCharCode(c)) .map((c) => String.fromCharCode(c))
.toList(); .toList();
}) })
..definitionProvider = true ..definitionProvider = true
@ -108,13 +108,13 @@ class JaelLanguageServer extends LanguageServer {
..implementationProvider = true ..implementationProvider = true
..referencesProvider = true ..referencesProvider = true
..renameProvider = true ..renameProvider = true
..signatureHelpProvider = new SignatureHelpOptions((b) {}) ..signatureHelpProvider = SignatureHelpOptions((b) {})
..textDocumentSync = new TextDocumentSyncOptions((b) { ..textDocumentSync = TextDocumentSyncOptions((b) {
b b
..openClose = true ..openClose = true
..change = TextDocumentSyncKind.full ..change = TextDocumentSyncKind.full
..save = new SaveOptions((b) { ..save = SaveOptions((b) {
b..includeText = false; b.includeText = false;
}) })
..willSave = false ..willSave = false
..willSaveWaitUntil = false; ..willSaveWaitUntil = false;
@ -124,7 +124,7 @@ class JaelLanguageServer extends LanguageServer {
} }
Future<File> fileForId(TextDocumentIdentifier documentId) async { Future<File> fileForId(TextDocumentIdentifier documentId) async {
var uri = Uri.parse(documentId.uri); var uri = Uri.parse(documentId.uri!);
var relativePath = uri.path; var relativePath = uri.path;
var file = _memFs.directory('/').childFile(relativePath); var file = _memFs.directory('/').childFile(relativePath);
@ -156,14 +156,14 @@ class JaelLanguageServer extends LanguageServer {
Future<Analyzer> analyzerForId(TextDocumentIdentifier documentId) async { Future<Analyzer> analyzerForId(TextDocumentIdentifier documentId) async {
var scanner = await scannerForId(documentId); var scanner = await scannerForId(documentId);
var analyzer = new Analyzer(scanner, logger)..errors.addAll(scanner.errors); var analyzer = Analyzer(scanner, logger)..errors.addAll(scanner.errors);
analyzer.parseDocument(); analyzer.parseDocument();
emitDiagnostics(documentId.uri, analyzer.errors.map(toDiagnostic).toList()); emitDiagnostics(documentId.uri, analyzer.errors.map(toDiagnostic).toList());
return analyzer; return analyzer;
} }
Diagnostic toDiagnostic(JaelError e) { Diagnostic toDiagnostic(JaelError e) {
return new Diagnostic((b) { return Diagnostic((b) {
b b
..message = e.message ..message = e.message
..range = toRange(e.span) ..range = toRange(e.span)
@ -182,7 +182,7 @@ class JaelLanguageServer extends LanguageServer {
} }
Range toRange(FileSpan span) { Range toRange(FileSpan span) {
return new Range((b) { return Range((b) {
b b
..start = toPosition(span.start) ..start = toPosition(span.start)
..end = toPosition(span.end); ..end = toPosition(span.end);
@ -190,8 +190,8 @@ class JaelLanguageServer extends LanguageServer {
} }
Range emptyRange() { Range emptyRange() {
return new Range((b) => b return Range((b) => b
..start = b.end = new Position((b) { ..start = b.end = Position((b) {
b b
..character = 1 ..character = 1
..line = 0; ..line = 0;
@ -199,15 +199,15 @@ class JaelLanguageServer extends LanguageServer {
} }
Position toPosition(SourceLocation location) { Position toPosition(SourceLocation location) {
return new Position((b) { return Position((b) {
b b
..line = location.line ..line = location.line
..character = location.column; ..character = location.column;
}); });
} }
Location toLocation(String uri, FileSpan span) { Location toLocation(String? uri, FileSpan span) {
return new Location((b) { return Location((b) {
b b
..range = toRange(span) ..range = toRange(span)
..uri = uri; ..uri = uri;
@ -215,27 +215,27 @@ class JaelLanguageServer extends LanguageServer {
} }
bool isReachable(JaelObject obj, Position position) { bool isReachable(JaelObject obj, Position position) {
return obj.span.start.line <= position.line && return obj.span.start.line <= position.line! &&
obj.span.start.column <= position.character; obj.span.start.column <= position.character!;
} }
CompletionItem toCompletion(Variable<JaelObject> symbol) { CompletionItem? toCompletion(Variable<JaelObject> symbol) {
var value = symbol.value; var value = symbol.value;
if (value is JaelCustomElement) { if (value is JaelCustomElement) {
var name = value.name; var name = value.name;
return new CompletionItem((b) { return CompletionItem((b) {
b b
..kind = CompletionItemKind.classKind ..kind = CompletionItemKind.classKind
..label = symbol.name ..label = symbol.name
..textEdit = new TextEdit((b) { ..textEdit = TextEdit((b) {
b b
..range = emptyRange() ..range = emptyRange()
..newText = '<$name\$1>\n \$2\n</name>'; ..newText = '<$name\$1>\n \$2\n</name>';
}); });
}); });
} else if (value is JaelVariable) { } else if (value is JaelVariable) {
return new CompletionItem((b) { return CompletionItem((b) {
b b
..kind = CompletionItemKind.variable ..kind = CompletionItemKind.variable
..label = symbol.name; ..label = symbol.name;
@ -245,8 +245,8 @@ class JaelLanguageServer extends LanguageServer {
return null; return null;
} }
void emitDiagnostics(String uri, Iterable<Diagnostic> diagnostics) { void emitDiagnostics(String? uri, Iterable<Diagnostic> diagnostics) {
_diagnostics.add(new Diagnostics((b) { _diagnostics.add(Diagnostics((b) {
logger.info('$uri => ${diagnostics.map((d) => d.message).toList()}'); logger.info('$uri => ${diagnostics.map((d) => d.message).toList()}');
b b
..diagnostics = diagnostics.toList() ..diagnostics = diagnostics.toList()
@ -256,43 +256,42 @@ class JaelLanguageServer extends LanguageServer {
@override @override
Future textDocumentDidOpen(TextDocumentItem document) async { Future textDocumentDidOpen(TextDocumentItem document) async {
await analyzerForId( await analyzerForId(TextDocumentIdentifier((b) => b..uri = document.uri));
new TextDocumentIdentifier((b) => b..uri = document.uri));
} }
@override @override
Future textDocumentDidChange(VersionedTextDocumentIdentifier documentId, Future textDocumentDidChange(VersionedTextDocumentIdentifier documentId,
List<TextDocumentContentChangeEvent> changes) async { List<TextDocumentContentChangeEvent> changes) async {
var id = new TextDocumentIdentifier((b) => b..uri = documentId.uri); var id = TextDocumentIdentifier((b) => b..uri = documentId.uri);
var file = await fileForId(id); var file = await fileForId(id);
for (var change in changes) { for (var change in changes) {
if (change.text != null) { if (change.text != null) {
await file.writeAsString(change.text); await file.writeAsString(change.text!);
} else if (change.range != null) { } else if (change.range != null) {
var contents = await file.readAsString(); String? contents = await file.readAsString();
int findIndex(Position position) { int findIndex(Position position) {
var lines = contents.split('\n'); var lines = contents!.split('\n');
// Sum the length of the previous lines. // Sum the length of the previous lines.
int lineLength = lines var lineLength = lines
.take(position.line - 1) .take(position.line! - 1)
.map((s) => s.length) .map((s) => s.length)
.reduce((a, b) => a + b); .reduce((a, b) => a + b);
return lineLength + position.character - 1; return lineLength + position.character! - 1;
} }
if (change.range == null) { if (change.range == null) {
contents = change.text; contents = change.text;
} else { } else {
var start = findIndex(change.range.start), var start = findIndex(change.range!.start!),
end = findIndex(change.range.end); end = findIndex(change.range!.end!);
contents = contents.replaceRange(start, end, change.text); contents = contents.replaceRange(start, end, change.text!);
} }
logger.info('${file.path} => $contents'); logger.info('${file.path} => $contents');
await file.writeAsString(contents); await file.writeAsString(contents!);
} }
} }
@ -310,9 +309,9 @@ class JaelLanguageServer extends LanguageServer {
Future<CompletionList> textDocumentCompletion( Future<CompletionList> textDocumentCompletion(
TextDocumentIdentifier documentId, Position position) async { TextDocumentIdentifier documentId, Position position) async {
var analyzer = await analyzerForId(documentId); var analyzer = await analyzerForId(documentId);
var symbols = analyzer.scope.allVariables; var symbols = analyzer.scope!.allVariables;
var reachable = symbols.where((s) => isReachable(s.value, position)); var reachable = symbols.where((s) => isReachable(s.value!, position));
return new CompletionList((b) { return CompletionList((b) {
b b
..isIncomplete = false ..isIncomplete = false
..items = reachable.map(toCompletion).toList(); ..items = reachable.map(toCompletion).toList();
@ -320,16 +319,16 @@ class JaelLanguageServer extends LanguageServer {
} }
final RegExp _id = final RegExp _id =
new RegExp(r'(([A-Za-z][A-Za-z0-9_]*-)*([A-Za-z][A-Za-z0-9_]*))'); RegExp(r'(([A-Za-z][A-Za-z0-9_]*-)*([A-Za-z][A-Za-z0-9_]*))');
Future<String> currentName( Future<String?> currentName(
TextDocumentIdentifier documentId, Position position) async { TextDocumentIdentifier documentId, Position position) async {
// First, read the file. // First, read the file.
var file = await fileForId(documentId); var file = await fileForId(documentId);
var contents = await file.readAsString(); var contents = await file.readAsString();
// Next, find the current index. // Next, find the current index.
var scanner = new SpanScanner(contents); var scanner = SpanScanner(contents);
while (!scanner.isDone && while (!scanner.isDone &&
(scanner.state.line != position.line || (scanner.state.line != position.line ||
@ -339,10 +338,10 @@ class JaelLanguageServer extends LanguageServer {
// Next, just read the name. // Next, just read the name.
if (scanner.matches(_id)) { if (scanner.matches(_id)) {
var longest = scanner.lastSpan.text; var longest = scanner.lastSpan!.text;
while (scanner.matches(_id) && scanner.position > 0 && !scanner.isDone) { while (scanner.matches(_id) && scanner.position > 0 && !scanner.isDone) {
longest = scanner.lastSpan.text; longest = scanner.lastSpan!.text;
scanner.position--; scanner.position--;
} }
@ -352,19 +351,19 @@ class JaelLanguageServer extends LanguageServer {
} }
} }
Future<JaelObject> currentSymbol( Future<JaelObject?> currentSymbol(
TextDocumentIdentifier documentId, Position position) async { TextDocumentIdentifier documentId, Position position) async {
var name = await currentName(documentId, position); var name = await currentName(documentId, position);
if (name == null) return null; if (name == null) return null;
var analyzer = await analyzerForId(documentId); var analyzer = await analyzerForId(documentId);
var symbols = analyzer.allDefinitions ?? analyzer.scope.allVariables; var symbols = analyzer.allDefinitions; // ?? analyzer.scope!.allVariables;
logger logger
.info('Current symbols, seeking $name: ${symbols.map((v) => v.name)}'); .info('Current symbols, seeking $name: ${symbols.map((v) => v.name)}');
return analyzer.scope.resolve(name)?.value; return analyzer.scope!.resolve(name)?.value;
} }
@override @override
Future<Location> textDocumentDefinition( Future<Location?> textDocumentDefinition(
TextDocumentIdentifier documentId, Position position) async { TextDocumentIdentifier documentId, Position position) async {
var symbol = await currentSymbol(documentId, position); var symbol = await currentSymbol(documentId, position);
if (symbol != null) { if (symbol != null) {
@ -379,7 +378,7 @@ class JaelLanguageServer extends LanguageServer {
var symbol = await currentSymbol(documentId, position); var symbol = await currentSymbol(documentId, position);
if (symbol != null) { if (symbol != null) {
return symbol.usages.map((u) { return symbol.usages.map((u) {
return new DocumentHighlight((b) { return DocumentHighlight((b) {
b b
..range = toRange(u.span) ..range = toRange(u.span)
..kind = u.type == SymbolUsageType.definition ..kind = u.type == SymbolUsageType.definition
@ -392,11 +391,11 @@ class JaelLanguageServer extends LanguageServer {
} }
@override @override
Future<Hover> textDocumentHover( Future<Hover?> textDocumentHover(
TextDocumentIdentifier documentId, Position position) async { TextDocumentIdentifier documentId, Position position) async {
var symbol = await currentSymbol(documentId, position); var symbol = await currentSymbol(documentId, position);
if (symbol != null) { if (symbol != null) {
return new Hover((b) { return Hover((b) {
b b
..contents = symbol.span.text ..contents = symbol.span.text
..range = toRange(symbol.span); ..range = toRange(symbol.span);
@ -430,14 +429,13 @@ class JaelLanguageServer extends LanguageServer {
@override @override
Future<WorkspaceEdit> textDocumentRename(TextDocumentIdentifier documentId, Future<WorkspaceEdit> textDocumentRename(TextDocumentIdentifier documentId,
Position position, String newName) async { Position position, String? newName) async {
var symbol = await currentSymbol(documentId, position); var symbol = await currentSymbol(documentId, position);
if (symbol != null) { if (symbol != null) {
return new WorkspaceEdit((b) { return WorkspaceEdit((b) {
b b.changes = {
..changes = {
symbol.name: symbol.usages.map((u) { symbol.name: symbol.usages.map((u) {
return new TextEdit((b) { return TextEdit((b) {
b b
..range = toRange(u.span) ..range = toRange(u.span)
..newText = (symbol is JaelCustomElement && ..newText = (symbol is JaelCustomElement &&
@ -449,8 +447,8 @@ class JaelLanguageServer extends LanguageServer {
}; };
}); });
} }
return new WorkspaceEdit((b) { return WorkspaceEdit((b) {
b..changes = {}; b.changes = {};
}); });
} }
@ -459,28 +457,28 @@ class JaelLanguageServer extends LanguageServer {
TextDocumentIdentifier documentId) async { TextDocumentIdentifier documentId) async {
var analyzer = await analyzerForId(documentId); var analyzer = await analyzerForId(documentId);
return analyzer.allDefinitions.map((symbol) { return analyzer.allDefinitions.map((symbol) {
return new SymbolInformation((b) { return SymbolInformation((b) {
b b
..kind = SymbolKind.classSymbol ..kind = SymbolKind.classSymbol
..name = symbol.name ..name = symbol.name
..location = toLocation(documentId.uri, symbol.value.span); ..location = toLocation(documentId.uri, symbol.value!.span);
}); });
}).toList(); }).toList();
} }
@override @override
Future<void> workspaceExecuteCommand(String command, List arguments) async { Future<void> workspaceExecuteCommand(String? command, List? arguments) async {
// TODO: implement workspaceExecuteCommand // TODO: implement workspaceExecuteCommand
} }
@override @override
Future<List<SymbolInformation>> workspaceSymbol(String query) async { Future<List<SymbolInformation>> workspaceSymbol(String? query) async {
var values = <JaelObject>[]; var values = <JaelObject?>[];
await for (var file in _memRootDir.list(recursive: true)) { await for (var file in _memRootDir!.list(recursive: true)) {
if (file is File) { if (file is File) {
var id = new TextDocumentIdentifier((b) { var id = TextDocumentIdentifier((b) {
b..uri = file.uri.toString(); b.uri = file.uri.toString();
}); });
var analyzer = await analyzerForId(id); var analyzer = await analyzerForId(id);
values.addAll(analyzer.allDefinitions.map((v) => v.value)); values.addAll(analyzer.allDefinitions.map((v) => v.value));
@ -488,11 +486,11 @@ class JaelLanguageServer extends LanguageServer {
} }
return values.map((o) { return values.map((o) {
return new SymbolInformation((b) { return SymbolInformation((b) {
b b
..name = o.name ..name = o!.name
..location = toLocation(o.span.sourceUrl.toString(), o.span) ..location = toLocation(o.span.sourceUrl.toString(), o.span)
..containerName = p.basename(o.span.sourceUrl.path) ..containerName = p.basename(o.span.sourceUrl!.path)
..kind = o is JaelCustomElement ..kind = o is JaelCustomElement
? SymbolKind.classSymbol ? SymbolKind.classSymbol
: SymbolKind.variable; : SymbolKind.variable;
@ -500,7 +498,7 @@ class JaelLanguageServer extends LanguageServer {
}).toList(); }).toList();
} }
Future<List<TextEdit>> textDocumentFormatting( Future<List<TextEdit>?> textDocumentFormatting(
TextDocumentIdentifier documentId, TextDocumentIdentifier documentId,
FormattingOptions formattingOptions) async { FormattingOptions formattingOptions) async {
try { try {
@ -510,16 +508,17 @@ class JaelLanguageServer extends LanguageServer {
var document = var document =
parseDocument(contents, sourceUrl: file.uri, onError: errors.add); parseDocument(contents, sourceUrl: file.uri, onError: errors.add);
if (errors.isNotEmpty) return null; if (errors.isNotEmpty) return null;
var formatter = new JaelFormatter( var formatter = JaelFormatter(
formattingOptions.tabSize, formattingOptions.insertSpaces, 80); formattingOptions.tabSize!, formattingOptions.insertSpaces, 80);
var formatted = formatter.apply(document); var formatted = formatter.apply(document!);
logger.info('Original:${contents}\nFormatted:\n$formatted'); logger.info('Original:$contents\nFormatted:\n$formatted');
if (formatted.isNotEmpty) await file.writeAsString(formatted); if (formatted.isNotEmpty) await file.writeAsString(formatted);
return [ return [
new TextEdit((b) { TextEdit((b) {
b b
..newText = formatted ..newText = formatted
..range = document == null ? emptyRange() : toRange(document.span); ..range = toRange(document
.span); //document == null ? emptyRange() : toRange(document.span);
}) })
]; ];
} catch (e, st) { } catch (e, st) {
@ -535,7 +534,7 @@ class JaelLanguageServer extends LanguageServer {
@override @override
// TODO: implement showMessages // TODO: implement showMessages
Stream<ShowMessageParams> get showMessages => null; Stream<ShowMessageParams>? get showMessages => null;
} }
abstract class DiagnosticSeverity { abstract class DiagnosticSeverity {
@ -543,14 +542,14 @@ abstract class DiagnosticSeverity {
} }
class FormattingOptions { class FormattingOptions {
final num tabSize; final num? tabSize;
final bool insertSpaces; final bool? insertSpaces;
FormattingOptions(this.tabSize, this.insertSpaces); FormattingOptions(this.tabSize, this.insertSpaces);
factory FormattingOptions.fromJson(Map json) { factory FormattingOptions.fromJson(Map json) {
return new FormattingOptions( return FormattingOptions(
json['tabSize'] as num, json['insertSpaces'] as bool); json['tabSize'] as num?, json['insertSpaces'] as bool?);
} }
} }

View file

@ -1,25 +1,36 @@
name: jael_language_server name: jael_language_server
version: 0.0.0 version: 0.0.0
description: Language Server Protocol implementation for the Jael templating engine. description: Language Server Protocol implementation for the Jael templating engine.
author: Tobe Osakwe <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/vscode homepage: https://github.com/angel-dart/vscode
publish_to: none
environment: environment:
sdk: '>=2.10.0 <2.12.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
args: ^1.0.0 args: ^2.1.1
# dart_language_server: ^0.1.16 # dart_language_server: ^0.1.16
file: ^5.0.0 file: ^6.1.2
io: ^0.3.2 io: ^1.0.0
jael: #^2.0.0 jael:
path: ../jael git:
jael_preprocessor: #^2.0.0 url: https://github.com/dukefirehawk/angel.git
path: ../jael_preprocessor ref: sdk-2.12.x_nnbd
json_rpc_2: ^2.0.0 path: packages/jael/jael
logging: ^0.11.3 jael_preprocessor:
path: ^1.0.0 git:
source_span: ^1.0.0 url: https://github.com/dukefirehawk/angel.git
string_scanner: ^1.0.0 ref: sdk-2.12.x_nnbd
symbol_table: ^2.0.0 path: packages/jael/jael_preprocessor
json_rpc_2: ^3.0.1
logging: ^1.0.1
path: ^1.8.0
source_span: ^1.8.1
string_scanner: ^1.1.0
symbol_table:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/symbol_table
pedantic: ^1.11.0
executables: executables:
jael_language_server: jael_language_server jael_language_server: jael_language_server

View file

@ -1,6 +1,6 @@
MIT License (MIT) MIT License
Copyright (c) 2021 dukefirehawk.com Copyright (c) 2017 The Angel Framework
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -5,18 +5,18 @@ part 'stateful.g.dart';
void main() {} void main() {}
class _AppState { class _AppState {
final int ticks; final int? ticks;
_AppState({this.ticks}); _AppState({this.ticks});
_AppState copyWith({int ticks}) { _AppState copyWith({int? ticks}) {
return _AppState(ticks: ticks ?? this.ticks); return _AppState(ticks: ticks ?? this.ticks);
} }
} }
@Jael(template: '<div>Tick count: {{state.ticks}}</div>') @Jael(template: '<div>Tick count: {{state.ticks}}</div>')
class StatefulApp extends Component<_AppState> with _StatefulAppJaelTemplate { class StatefulApp extends Component<_AppState> with _StatefulAppJaelTemplate {
Timer _timer; Timer? _timer;
StatefulApp() { StatefulApp() {
state = _AppState(ticks: 0); state = _AppState(ticks: 0);
@ -27,6 +27,6 @@ class StatefulApp extends Component<_AppState> with _StatefulAppJaelTemplate {
@override @override
void beforeDestroy() { void beforeDestroy() {
_timer.cancel(); _timer!.cancel();
} }
} }

View file

@ -7,7 +7,7 @@ part of 'stateful.dart';
// ************************************************************************** // **************************************************************************
abstract class _StatefulAppJaelTemplate implements Component<_AppState> { abstract class _StatefulAppJaelTemplate implements Component<_AppState> {
Timer get _timer; Timer? get _timer;
void beforeDestroy(); void beforeDestroy();
@override @override
DomNode render() { DomNode render() {

View file

@ -19,7 +19,7 @@ class MyApp extends Component with _MyAppJaelTemplate {}
</div> </div>
''') ''')
class LabeledInput extends Component with _LabeledInputJaelTemplate { class LabeledInput extends Component with _LabeledInputJaelTemplate {
final String name; final String? name;
LabeledInput({this.name}); LabeledInput({this.name});
} }

View file

@ -17,7 +17,7 @@ abstract class _MyAppJaelTemplate implements Component<dynamic> {
} }
abstract class _LabeledInputJaelTemplate implements Component<dynamic> { abstract class _LabeledInputJaelTemplate implements Component<dynamic> {
String get name; String? get name;
@override @override
DomNode render() { DomNode render() {
return h('div', {}, [ return h('div', {}, [
@ -27,7 +27,7 @@ abstract class _LabeledInputJaelTemplate implements Component<dynamic> {
h('br', {}, []), h('br', {}, []),
h('input', { h('input', {
'name': name, 'name': name,
'placeholder': "Enter " + name + "...", 'placeholder': "Enter " + name! + "...",
'type': "text" 'type': "text"
}, []) }, [])
]); ]);

View file

@ -21,7 +21,7 @@ class JaelComponentGenerator extends GeneratorForAnnotation<Jael> {
Element element, ConstantReader annotation, BuildStep buildStep) async { Element element, ConstantReader annotation, BuildStep buildStep) async {
if (element is ClassElement) { if (element is ClassElement) {
// Load the template // Load the template
String templateString; String? templateString;
var inputId = buildStep.inputId; var inputId = buildStep.inputId;
var ann = Jael( var ann = Jael(
template: annotation.peek('template')?.stringValue, template: annotation.peek('template')?.stringValue,
@ -47,10 +47,10 @@ class JaelComponentGenerator extends GeneratorForAnnotation<Jael> {
var fs = BuildFileSystem(buildStep, inputId.package); var fs = BuildFileSystem(buildStep, inputId.package);
var errors = <jael.JaelError>[]; var errors = <jael.JaelError>[];
var doc = await jael.parseDocument(templateString, var doc = await jael.parseDocument(templateString!,
sourceUrl: inputId.uri, asDSX: ann.asDsx, onError: errors.add); sourceUrl: inputId.uri, asDSX: ann.asDsx!, onError: errors.add);
if (errors.isEmpty) { if (errors.isEmpty) {
doc = await jael.resolve(doc, fs.file(inputId.uri).parent, doc = await jael.resolve(doc!, fs.file(inputId.uri).parent,
onError: errors.add); onError: errors.add);
} }
@ -98,7 +98,7 @@ class JaelComponentGenerator extends GeneratorForAnnotation<Jael> {
..returns = refer('DomNode') ..returns = refer('DomNode')
..annotations.add(refer('override')) ..annotations.add(refer('override'))
..body = Block((b) { ..body = Block((b) {
var result = compileElementChild(doc.root); var result = compileElementChild(doc!.root);
b.addExpression(result.returned); b.addExpression(result.returned);
}); });
})); }));
@ -123,7 +123,7 @@ class JaelComponentGenerator extends GeneratorForAnnotation<Jael> {
for (var attr in child.attributes) { for (var attr in child.attributes) {
attrs[attr.name] = attr.value == null attrs[attr.name] = attr.value == null
? literalTrue ? literalTrue
: CodeExpression(Code(attr.value.span.text)); : CodeExpression(Code(attr.value!.span.text));
} }
var tagName = child.tagName.name; var tagName = child.tagName.name;

View file

@ -10,9 +10,9 @@ import 'package:file/file.dart';
import 'package:path/src/context.dart'; import 'package:path/src/context.dart';
/// Converts a [DartType] to a [TypeReference]. /// Converts a [DartType] to a [TypeReference].
TypeReference convertTypeReference(DartType t) { TypeReference convertTypeReference(DartType? t) {
return new TypeReference((b) { return new TypeReference((b) {
b..symbol = t.name; b..symbol = t!.name;
if (t is InterfaceType) { if (t is InterfaceType) {
b.types.addAll(t.typeArguments.map(convertTypeReference)); b.types.addAll(t.typeArguments.map(convertTypeReference));
@ -35,7 +35,7 @@ Parameter convertParameter(ParameterElement e) {
..type = convertTypeReference(e.type) ..type = convertTypeReference(e.type)
..named = e.isNamed ..named = e.isNamed
..defaultTo = ..defaultTo =
e.defaultValueCode == null ? null : Code(e.defaultValueCode); e.defaultValueCode == null ? null : Code(e.defaultValueCode!);
}); });
} }
@ -68,11 +68,11 @@ class BuildFileSystem extends FileSystem {
@override @override
Directory directory(path) { Directory directory(path) {
String p; late String p;
if (path is String) if (path is String)
p = path; p = path;
else if (path is Uri) else if (path is Uri)
p = p.toString(); p = path.toString(); //p.toString();
else if (path is FileSystemEntity) else if (path is FileSystemEntity)
p = path.path; p = path.path;
else else
@ -82,11 +82,11 @@ class BuildFileSystem extends FileSystem {
@override @override
File file(path) { File file(path) {
String p; late String p;
if (path is String) if (path is String)
p = path; p = path;
else if (path is Uri) else if (path is Uri)
p = p.toString(); p = path.toString(); // p.toString();
else if (path is FileSystemEntity) else if (path is FileSystemEntity)
p = path.path; p = path.path;
else else
@ -192,7 +192,7 @@ class BuildSystemFile extends File {
throw _unsupported(); throw _unsupported();
@override @override
Stream<List<int>> openRead([int start, int end]) => throw _unsupported(); Stream<List<int>> openRead([int? start, int? end]) => throw _unsupported();
@override @override
RandomAccessFile openSync({FileMode mode = FileMode.read}) => RandomAccessFile openSync({FileMode mode = FileMode.read}) =>
@ -347,10 +347,10 @@ class BuildSystemDirectory extends Directory {
void createSync({bool recursive = false}) => throw _unsupported(); void createSync({bool recursive = false}) => throw _unsupported();
@override @override
Future<Directory> createTemp([String prefix]) => throw _unsupported(); Future<Directory> createTemp([String? prefix]) => throw _unsupported();
@override @override
Directory createTempSync([String prefix]) => throw _unsupported(); Directory createTempSync([String? prefix]) => throw _unsupported();
@override @override
Future<FileSystemEntity> delete({bool recursive = false}) => Future<FileSystemEntity> delete({bool recursive = false}) =>

View file

@ -2,7 +2,7 @@ import 'dom_builder.dart';
import 'dom_node.dart'; import 'dom_node.dart';
abstract class BuilderNode extends DomNode { abstract class BuilderNode extends DomNode {
DomBuilderElement<T> build<T>(DomBuilder<T> dom); DomBuilderElement<T>? build<T>(DomBuilder<T> dom);
void destroy<T>(DomBuilderElement<T> el); void destroy<T>(DomBuilderElement<T> el);
} }
@ -21,7 +21,7 @@ class _Text extends BuilderNode {
_Text(this.text); _Text(this.text);
@override @override
DomBuilderElement<T> build<T>(DomBuilder<T> dom) { DomBuilderElement<T>? build<T>(DomBuilder<T> dom) {
dom.text(text); dom.text(text);
// TODO: implement build // TODO: implement build
return null; return null;
@ -41,7 +41,7 @@ class _H extends BuilderNode {
_H(this.tagName, this.props, this.children); _H(this.tagName, this.props, this.children);
@override @override
DomBuilderElement<T> build<T>(DomBuilder<T> dom) { DomBuilderElement<T>? build<T>(DomBuilder<T> dom) {
// TODO: implement build // TODO: implement build
return null; return null;
} }

View file

@ -1,7 +1,7 @@
import 'dom_node.dart'; import 'dom_node.dart';
abstract class Component<State> extends DomNode { abstract class Component<State> extends DomNode {
State state; late State state;
DomNode render(); DomNode render();

View file

@ -6,7 +6,7 @@ abstract class DomBuilder<T> {
} }
abstract class DomBuilderElement<T> extends DomBuilder<T> { abstract class DomBuilderElement<T> extends DomBuilder<T> {
void attr(String name, [String value]); void attr(String name, [String? value]);
void attrs(Map<String, String> map); void attrs(Map<String, String> map);

File diff suppressed because it is too large Load diff

View file

@ -1,19 +1,19 @@
/// A annotation for components that source-gen their `render()` methods. /// A annotation for components that source-gen their `render()` methods.
class Jael { class Jael {
/// The raw template. /// The raw template.
final String template; final String? template;
/// The path to a [template]. /// The path to a [template].
final String templateUrl; final String? templateUrl;
/// Whether to parse the [template] as `DSX`. /// Whether to parse the [template] as `DSX`.
final bool asDsx; final bool? asDsx;
const Jael({this.template, this.templateUrl, this.asDsx}); const Jael({this.template, this.templateUrl, this.asDsx});
} }
/// Shorthand for enabling `DSX` syntax when using a [Jael] annotation. /// Shorthand for enabling `DSX` syntax when using a [Jael] annotation.
class Dsx extends Jael { class Dsx extends Jael {
const Dsx({String template, String templateUrl}) const Dsx({String? template, String? templateUrl})
: super(template: template, templateUrl: templateUrl, asDsx: true); : super(template: template, templateUrl: templateUrl, asDsx: true);
} }

View file

@ -1,18 +1,25 @@
name: jael_web name: jael_web
version: 0.0.0 version: 0.0.0
description: Experimental virtual DOM/SPA engine built on Jael. Supports SSR. description: Experimental virtual DOM/SPA engine built on Jael. Supports SSR.
publish_to: none
environment: environment:
sdk: '>=2.10.0 <2.12.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
build: ^1.0.0 build: ^2.0.2
build_config: ^0.3.0 build_config: ^1.0.0
code_builder: ^3.0.0 code_builder: ^4.0.0
jael: #^2.0.0 jael:
path: ../jael git:
jael_preprocessor: #^2.0.0 url: https://github.com/dukefirehawk/angel.git
path: ../jael_preprocessor ref: sdk-2.12.x_nnbd
source_gen: ^0.9.0 path: packages/jael/jael
jael_preprocessor:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/jael/jael_preprocessor
source_gen: ^1.0.2
dev_dependencies: dev_dependencies:
build_runner: ^1.0.0 build_runner: ^2.0.4
build_web_compilers: ^1.0.0 build_web_compilers: ^3.0.0
pedantic: ^1.0.0 pedantic: ^1.11.1

12
packages/jinja/AUTHORS.md Normal file
View 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.

View file

@ -1,3 +1,6 @@
## 1.0.0 # 2.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 1.0.0
- Initial version, created by Stagehand - Initial version, created by Stagehand

View file

@ -11,7 +11,7 @@ import 'package:angel_framework/http.dart';
import 'package:angel_jinja/angel_jinja.dart'; import 'package:angel_jinja/angel_jinja.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
main() async { void main() async {
var app = Angel(); var app = Angel();
var http = AngelHttp(app); var http = AngelHttp(app);
var viewsDir = p.join( var viewsDir = p.join(

View file

@ -4,7 +4,7 @@ import 'package:angel_framework/http.dart';
import 'package:angel_jinja/angel_jinja.dart'; import 'package:angel_jinja/angel_jinja.dart';
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
main() async { void main() async {
var app = Angel(); var app = Angel();
var http = AngelHttp(app); var http = AngelHttp(app);
var viewsDir = p.join( var viewsDir = p.join(

View file

@ -9,45 +9,45 @@ import 'package:jinja/jinja.dart';
/// All options other than [createLoader] are passed to either [FileSystemLoader] /// All options other than [createLoader] are passed to either [FileSystemLoader]
/// or [Environment]. /// or [Environment].
AngelConfigurer jinja({ AngelConfigurer jinja({
Iterable<String> ext = const ['html'], Set<String> ext = const {'html'},
String path = 'lib/src/templates', String path = 'lib/src/templates',
bool followLinks = true, bool followLinks = true,
String stmtOpen = '{%', String blockStart = '{%',
String stmtClose = '%}', String blockEnd = '%}',
String varOpen = '{{', String varOpen = '{{',
String varClose = '}}', String varClose = '}}',
String commentOpen = '{#', String commentStart = '{#',
String commentClose = '#}', String commentEnd = '#}',
defaultValue, defaultValue,
bool autoReload = true, //bool autoReload = true,
Map<String, Function> filters = const <String, Function>{}, Map<String, Function> filters = const <String, Function>{},
Map<String, Function> tests = const <String, Function>{}, Map<String, Function> tests = const <String, Function>{},
Loader Function() createLoader, Loader Function()? createLoader,
}) { }) {
return (app) { return (app) {
createLoader ??= () { createLoader ??= () {
return FileSystemLoader( return FileSystemLoader(
ext: ext.toList(), extensions: ext,
path: path, path: path,
followLinks: followLinks, followLinks: followLinks,
); );
}; };
var env = Environment( var env = Environment(
loader: createLoader(), loader: createLoader!(),
stmtOpen: stmtOpen, blockStart: blockStart,
stmtClose: stmtClose, blockEnd: blockEnd,
varOpen: varOpen, variableStart: varOpen,
varClose: varClose, variableEnd: varClose,
commentOpen: commentOpen, commentStart: commentStart,
commentClose: commentClose, commentEnd: commentEnd,
//defaultValue: defaultValue, //defaultValue: defaultValue,
autoReload: autoReload, //autoReload: autoReload,
filters: filters, filters: filters,
tests: tests, tests: tests,
); );
app.viewGenerator = (path, [values]) { app.viewGenerator = (path, [values]) {
return env.getTemplate(path).render(values); return env.getTemplate(path).render(values) as String;
}; };
}; };
} }

View file

@ -1,17 +1,23 @@
name: angel_jinja name: angel_jinja
version: 2.0.0
description: Angel support for the Jinja2 templating engine, ported from Python to Dart. description: Angel support for the Jinja2 templating engine, ported from Python to Dart.
version: 1.0.0-rc.0
homepage: https://github.com/angel-dart/jinja homepage: https://github.com/angel-dart/jinja
author: Tobe O <thosakwe@gmail.com> publish_to: none
environment: environment:
sdk: '>=2.10.0 <2.12.0' sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel_framework: #^2.0.0-alpha angel_framework:
path: ../framework git:
jinja: ^0.0.4 url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/framework
jinja: ^0.3.1
dev_dependencies: dev_dependencies:
angel_test: #^2.0.0 angel_test:
path: ../test git:
path: ^1.0.0 url: https://github.com/dukefirehawk/angel.git
pedantic: ^1.0.0 ref: sdk-2.12.x_nnbd
test: ^1.15.7 path: packages/test
path: ^1.8.0
pedantic: ^1.11.0
test: ^1.17.0

View 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.

View file

@ -1,3 +1,6 @@
# 3.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 2.0.0 # 2.0.0
* Angel 2 + Dart 2 updates. * Angel 2 + Dart 2 updates.
* Use `package:file`. * Use `package:file`.

View file

@ -5,7 +5,7 @@ import 'package:angel_framework/http.dart';
import 'package:angel_markdown/angel_markdown.dart'; import 'package:angel_markdown/angel_markdown.dart';
import 'package:file/local.dart'; import 'package:file/local.dart';
main() async { void main() async {
var app = await createServer(); var app = await createServer();
var http = AngelHttp(app); var http = AngelHttp(app);
var server = await http.startServer(InternetAddress.loopbackIPv4, 3000); var server = await http.startServer(InternetAddress.loopbackIPv4, 3000);
@ -14,7 +14,7 @@ main() async {
Future<Angel> createServer() async { Future<Angel> createServer() async {
// Create a new server, and install the Markdown renderer. // Create a new server, and install the Markdown renderer.
var app = new Angel(); var app = Angel();
var fs = LocalFileSystem(); var fs = LocalFileSystem();
await app await app
.configure(markdown(fs.directory('views'), template: (content, locals) { .configure(markdown(fs.directory('views'), template: (content, locals) {

View file

@ -4,7 +4,7 @@ import 'package:angel_framework/angel_framework.dart';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:markdown/markdown.dart'; import 'package:markdown/markdown.dart';
final RegExp _braces = new RegExp(r'@?{{(((\\})|([^}]))+)}}'); final RegExp _braces = RegExp(r'@?{{(((\\})|([^}]))+)}}');
/// Configures an [Angel] instance to render Markdown templates from the specified [viewsDirectory]. /// Configures an [Angel] instance to render Markdown templates from the specified [viewsDirectory].
/// ///
@ -18,7 +18,8 @@ AngelConfigurer markdown(
Directory viewsDirectory, { Directory viewsDirectory, {
String extension, String extension,
ExtensionSet extensionSet, ExtensionSet extensionSet,
FutureOr<String> template(String content, Map<String, dynamic> locals), FutureOr<String> Function(String content, Map<String, dynamic> locals)
template,
}) { }) {
extension ??= '.md'; extension ??= '.md';
extensionSet ??= ExtensionSet.gitHubWeb; extensionSet ??= ExtensionSet.gitHubWeb;
@ -40,9 +41,10 @@ AngelConfigurer markdown(
var split = expr.split('.'); var split = expr.split('.');
var root = split[0]; var root = split[0];
if (locals?.containsKey(root) != true) if (locals?.containsKey(root) != true) {
throw new UnimplementedError( throw UnimplementedError(
'Expected a local named "$root", but none was provided. Expression text: "$text"'); 'Expected a local named "$root", but none was provided. Expression text: "$text"');
}
return _resolveDotNotation(split, locals[root]).toString(); return _resolveDotNotation(split, locals[root]).toString();
} }
@ -55,13 +57,13 @@ AngelConfigurer markdown(
}; };
} }
_resolveDotNotation(List<String> split, target) { dynamic _resolveDotNotation(List<String> split, target) {
if (split.length == 1) return target; if (split.length == 1) return target;
InstanceMirror mirror = reflect(target); var mirror = reflect(target);
for (int i = 1; i < split.length; i++) { for (var i = 1; i < split.length; i++) {
mirror = mirror.getField(new Symbol(split[i])); mirror = mirror.getField(Symbol(split[i]));
} }
return mirror.reflectee; return mirror.reflectee;

View file

@ -1,17 +1,23 @@
name: angel_markdown name: angel_markdown
version: 2.0.0 version: 3.0.0
description: Angel Markdown view generator. Write static sites, with no build step. description: Angel Markdown view generator. Write static sites, with no build step.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/markdown homepage: https://github.com/angel-dart/markdown
publish_to: none
environment: environment:
sdk: ">=2.10.0 <2.12.0" sdk: ">=2.10.0 <3.0.0"
dependencies: dependencies:
angel_framework: #^2.0.0-alpha angel_framework:
path: ../framework git:
file: ^5.0.0 url: https://github.com/dukefirehawk/angel.git
markdown: ^2.0.0 ref: sdk-2.12.x_nnbd
path: packages/framework
file: ^6.1.2
markdown: ^4.0.0
dev_dependencies: dev_dependencies:
angel_test: #^2.0.0 angel_test:
path: ../test git:
pedantic: ^1.0.0 url: https://github.com/dukefirehawk/angel.git
test: ^1.15.7 ref: sdk-2.12.x_nnbd
path: packages/test
pedantic: ^1.11.0
test: ^1.17.0

View 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.

View file

@ -1,2 +1,6 @@
# 3.0.0
* Migrated to support Dart SDK 2.12.x NNBD
* Replaced `mustache4dart` with `mustache_template`
# 2.0.0 # 2.0.0
* Angel 2 and Dart 2 support. * Angel 2 and Dart 2 support.

View file

@ -1,2 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer: analyzer:
strong-mode: true strong-mode:
implicit-casts: false

View file

@ -3,9 +3,9 @@ import 'package:angel_mustache/angel_mustache.dart';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:file/local.dart'; import 'package:file/local.dart';
const FileSystem fs = const LocalFileSystem(); const FileSystem fs = LocalFileSystem();
configureServer(Angel app) async { void configureServer(Angel app) async {
// Run the plug-in // Run the plug-in
await app.configure(mustache(fs.directory('views'))); await app.configure(mustache(fs.directory('views')));

View file

@ -1,32 +1,38 @@
library angel_mustache; library angel_mustache;
import 'dart:async';
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:mustache4dart/mustache4dart.dart' show render; import 'package:mustache_template/mustache_template.dart' as viewer;
import 'package:path/path.dart' as p; import 'package:path/path.dart' as p;
import 'src/cache.dart'; import 'src/cache.dart';
import 'src/mustache_context.dart'; import 'src/mustache_context.dart';
mustache(Directory viewsDirectory, Future Function(Angel app) mustache(Directory viewsDirectory,
{String fileExtension: '.mustache', String partialsPath: './partials'}) { {String fileExtension = '.mustache', String partialsPath = './partials'}) {
Directory partialsDirectory = viewsDirectory.fileSystem var partialsDirectory = viewsDirectory.fileSystem
.directory(p.join(p.fromUri(viewsDirectory.uri), partialsPath)); .directory(p.join(p.fromUri(viewsDirectory.uri), partialsPath));
MustacheContext context = var context =
new MustacheContext(viewsDirectory, partialsDirectory, fileExtension); MustacheContext(viewsDirectory, partialsDirectory, fileExtension);
MustacheViewCache cache = new MustacheViewCache(context); var cache = MustacheViewCache(context);
return (Angel app) async { return (Angel app) async {
app.viewGenerator = (String name, [Map data]) async { app.viewGenerator = (String name, [Map? data]) async {
var partialsProvider; //var partialsProvider;
partialsProvider = (String name) { var partialsProvider = (String name) {
String template = cache.getPartialSync(name, app); var template = cache.getPartialSync(name, app)!;
return render(template, data ?? {}, partial: partialsProvider); //return render(template, data ?? {}, partial: partialsProvider);
return viewer.Template(template, name: name);
}; };
String viewTemplate = await cache.getView(name, app); var viewTemplate = await (cache.getView(name, app));
return await render(viewTemplate, data ?? {}, partial: partialsProvider); //return await render(viewTemplate, data ?? {}, partial: partialsProvider);
var t = viewer.Template(viewTemplate ?? '',
partialResolver: partialsProvider);
return t.renderString(data ?? {});
}; };
}; };
} }

View file

@ -6,54 +6,57 @@ import 'package:angel_framework/angel_framework.dart';
import 'package:angel_mustache/src/mustache_context.dart'; import 'package:angel_mustache/src/mustache_context.dart';
class MustacheViewCache { class MustacheViewCache {
/** /// The context for which views and partials are
* The context for which views and partials are /// served from.
* served from. MustacheContext? context;
*/
MustacheContext context;
HashMap<String, String> viewCache = new HashMap(); HashMap<String, String> viewCache = HashMap();
HashMap<String, String> partialCache = new HashMap(); HashMap<String, String> partialCache = HashMap();
MustacheViewCache([this.context]); MustacheViewCache([this.context]);
Future<String> getView(String viewName, Angel app) async { Future<String?> getView(String viewName, Angel app) async {
if (app.isProduction) { if (app.environment.isProduction) {
if (viewCache.containsKey(viewName)) { if (viewCache.containsKey(viewName)) {
return viewCache[viewName]; return viewCache[viewName];
} }
} }
File viewFile = context.resolveView(viewName); var viewFile = context?.resolveView(viewName);
if (viewFile == null) {
throw FileSystemException('View "$viewName" was not found.', 'null');
}
if (viewFile.existsSync()) { if (viewFile.existsSync()) {
String viewTemplate = await viewFile.readAsString(); var viewTemplate = await viewFile.readAsString();
if (app.isProduction) { if (app.environment.isProduction) {
this.viewCache[viewName] = viewTemplate; viewCache[viewName] = viewTemplate;
} }
return viewTemplate; return viewTemplate;
} else } else {
throw new FileSystemException( throw FileSystemException(
'View "$viewName" was not found.', viewFile.path); 'View "$viewName" was not found.', viewFile.path);
} }
}
String getPartialSync(String partialName, Angel app) { String? getPartialSync(String partialName, Angel app) {
if (app.isProduction) { if (app.environment.isProduction) {
if (partialCache.containsKey(partialName)) { if (partialCache.containsKey(partialName)) {
return partialCache[partialName]; return partialCache[partialName];
} }
} }
File partialFile = context.resolvePartial(partialName); var partialFile = context!.resolvePartial(partialName);
if (partialFile.existsSync()) { if (partialFile.existsSync()) {
String partialTemplate = partialFile.readAsStringSync(); var partialTemplate = partialFile.readAsStringSync();
if (app.isProduction) { if (app.environment.isProduction) {
this.partialCache[partialName] = partialTemplate; partialCache[partialName] = partialTemplate;
} }
return partialTemplate; return partialTemplate;
} else } else {
throw new FileSystemException( throw FileSystemException(
'View "$partialName" was not found.', partialFile.path); 'View "$partialName" was not found.', partialFile.path);
} }
}
} }

View file

@ -1,20 +1,19 @@
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:path/path.dart' as path;
class MustacheContext { class MustacheContext {
Directory viewDirectory; Directory? viewDirectory;
Directory partialDirectory; Directory? partialDirectory;
String extension; String? extension;
MustacheContext([this.viewDirectory, this.partialDirectory, this.extension]); MustacheContext([this.viewDirectory, this.partialDirectory, this.extension]);
File resolveView(String viewName) { File resolveView(String viewName) {
return viewDirectory.childFile('${viewName}${extension}'); return viewDirectory!.childFile('$viewName$extension');
} }
File resolvePartial(String partialName) { File resolvePartial(String partialName) {
return partialDirectory.childFile('${partialName}${extension}'); return partialDirectory!.childFile('$partialName$extension');
} }
} }

View file

@ -1,16 +1,20 @@
name: angel_mustache name: angel_mustache
description: Mustache view generator for Angel.
author: thosakwe <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/angel_mustache
version: 2.0.0 version: 2.0.0
description: Mustache view generator for Angel.
homepage: https://github.com/angel-dart/angel_mustache
publish_to: none
environment: environment:
sdk: ">=2.10.0 <2.12.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel_framework: #^2.0.0-alpha angel_framework: #^2.0.0-alpha
path: ../framework git:
file: ^5.0.0 url: https://github.com/dukefirehawk/angel.git
mustache4dart: ^3.0.0-dev ref: sdk-2.12.x_nnbd
path: ^1.0.0 path: packages/framework
file: ^6.1.2
mustache_template: ^2.0.0
path: ^1.8.0
dev_dependencies: dev_dependencies:
http: ^0.12.2 http: ^0.13.3
test: ^1.15.7 test: ^1.17.8
pedantic: ^1.11.1

View file

@ -1,30 +1,31 @@
import 'dart:async'; import 'dart:async';
import 'package:angel_framework/angel_framework.dart'; import 'package:angel_framework/angel_framework.dart';
import 'package:angel_mustache/angel_mustache.dart'; import 'package:angel_mustache/angel_mustache.dart';
import 'package:file/file.dart';
import 'package:file/local.dart'; import 'package:file/local.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
main() async { void main() async {
Angel angel = new Angel(); var angel = Angel();
await angel.configure(mustache(const LocalFileSystem().directory('./test'))); await angel.configure(mustache(const LocalFileSystem().directory('./test')));
test('can render templates', () async { test('can render templates', () async {
var hello = await angel.viewGenerator('hello', {'name': 'world'}); var hello = await angel.viewGenerator!('hello', {'name': 'world'});
var bar = await angel.viewGenerator('foo/bar', {'framework': 'angel'}); var bar = await angel.viewGenerator!('foo/bar', {'framework': 'angel'});
expect(hello, equals("Hello, world!")); expect(hello, equals('Hello, world!'));
expect(bar, equals("angel_framework")); expect(bar, equals('angel_framework'));
}); });
test('throws if view is not found', () { test('throws if view is not found', () {
expect(new Future(() async { expect(Future(() async {
var fails = await angel.viewGenerator('fail', {'this_should': 'fail'}); var fails = await angel.viewGenerator!('fail', {'this_should': 'fail'});
print(fails); print(fails);
}), throws); }), throwsA(isA<FileSystemException>()));
}); });
test("partials", () async { test('partials', () async {
var withPartial = await angel.viewGenerator('with-partial'); var withPartial = await angel.viewGenerator!('with-partial');
expect(withPartial, equals("Hello, world!")); expect(withPartial, equals('Hello, world!'));
}); });
} }

View 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.

View file

@ -1,2 +1,5 @@
# 3.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 2.0.0 # 2.0.0
* Dart2 + Angel2 update. * Dart2 + Angel2 update.

View file

@ -1,4 +1,4 @@
MIT License (MIT) MIT License
Copyright (c) 2021 dukefirehawk.com Copyright (c) 2021 dukefirehawk.com

View file

@ -0,0 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,11 +1,11 @@
import 'package:angel_paginate/angel_paginate.dart'; import 'package:angel_paginate/angel_paginate.dart';
main() { void main() {
var iterable = [1, 2, 3, 4]; var iterable = [1, 2, 3, 4];
var p = new Paginator(iterable); var p = Paginator(iterable);
// Get the current page (default: page 1) // Get the current page (default: page 1)
var page = p.current; var page = p.current!;
print(page.total); print(page.total);
print(page.startIndex); print(page.startIndex);
print(page.data); // The actual items on this page. print(page.data); // The actual items on this page.

View file

@ -1,7 +1,7 @@
/// Efficiently paginates collections of items in an object-oriented manner. /// Efficiently paginates collections of items in an object-oriented manner.
class Paginator<T> { class Paginator<T> {
final Map<int, PaginationResult<T>> _cache = {}; final Map<int, PaginationResult<T>> _cache = {};
PaginationResult<T> _current; PaginationResult<T>? _current;
int _page = 0; int _page = 0;
/// The collection of items to be paginated. /// The collection of items to be paginated.
@ -15,7 +15,7 @@ class Paginator<T> {
/// For example, you would only have to paginate page 1 once. Future calls would return a cached version. /// For example, you would only have to paginate page 1 once. Future calls would return a cached version.
final bool useCache; final bool useCache;
Paginator(this._items, {this.itemsPerPage: 5, this.useCache: true}); Paginator(this._items, {this.itemsPerPage = 5, this.useCache = true});
/// Returns `true` if there are more items at lesser page indices than the current one. /// Returns `true` if there are more items at lesser page indices than the current one.
bool get canGoBack => _page > 0; bool get canGoBack => _page > 0;
@ -37,21 +37,23 @@ class Paginator<T> {
/// Fetches the current page. This will be cached until [back] or [next] is called. /// Fetches the current page. This will be cached until [back] or [next] is called.
/// ///
/// If [useCache] is `true` (default), then computations will be cached even after the page changes. /// If [useCache] is `true` (default), then computations will be cached even after the page changes.
PaginationResult<T> get current { PaginationResult<T>? get current {
if (_current != null) if (_current != null) {
return _current; return _current;
else } else {
return _current = _getPage(); return _current = _getPage();
} }
}
PaginationResult<T> _computePage() { PaginationResult<T> _computePage() {
var len = _items.length; var len = _items.length;
var it = _items.skip(_page * (itemsPerPage ?? 5)); //var it = _items.skip(_page * (itemsPerPage ?? 5));
var it = _items.skip(_page * (itemsPerPage));
var offset = len - it.length; var offset = len - it.length;
it = it.take(itemsPerPage); it = it.take(itemsPerPage);
var last = _lastPage(); var last = _lastPage();
// print('cur: $_page, last: $last'); // print('cur: $_page, last: $last');
return new _PaginationResultImpl(it, return _PaginationResultImpl(it,
currentPage: _page + 1, currentPage: _page + 1,
previousPage: _page <= 0 ? -1 : _page, previousPage: _page <= 0 ? -1 : _page,
nextPage: _page >= last - 1 ? -1 : _page + 2, nextPage: _page >= last - 1 ? -1 : _page + 2,
@ -63,11 +65,12 @@ class Paginator<T> {
} }
PaginationResult<T> _getPage() { PaginationResult<T> _getPage() {
if (useCache != false) if (useCache != false) {
return _cache.putIfAbsent(_page, () => _computePage()); return _cache.putIfAbsent(_page, () => _computePage());
else } else {
return _computePage(); return _computePage();
} }
}
int _lastPage() { int _lastPage() {
var n = (_items.length / itemsPerPage).round(); var n = (_items.length / itemsPerPage).round();
@ -110,40 +113,40 @@ class Paginator<T> {
/// Stores the result of a pagination. /// Stores the result of a pagination.
abstract class PaginationResult<T> { abstract class PaginationResult<T> {
factory PaginationResult.fromMap(Map<String, dynamic> map) => factory PaginationResult.fromMap(Map<String, dynamic> map) =>
new _PaginationResultImpl((map['data'] as Iterable).cast<T>(), _PaginationResultImpl((map['data'] as List).cast<T>(),
currentPage: map['current_page'], currentPage: map['current_page'] as int?,
endIndex: map['end_index'], endIndex: map['end_index'] as int?,
itemsPerPage: map['items_per_page'], itemsPerPage: map['items_per_page'] as int?,
nextPage: map['next_page'], nextPage: map['next_page'] as int?,
previousPage: map['previous_page'], previousPage: map['previous_page'] as int?,
startIndex: map['start_index'], startIndex: map['start_index'] as int?,
total: map['total']); total: map['total'] as int?);
List<T> get data; Iterable<T> get data;
int get currentPage; int? get currentPage;
int get previousPage; int? get previousPage;
int get nextPage; int? get nextPage;
int get itemsPerPage; int? get itemsPerPage;
int get total; int? get total;
int get startIndex; int? get startIndex;
int get endIndex; int? get endIndex;
Map<String, dynamic> toJson(); Map<String, dynamic> toJson();
} }
class _PaginationResultImpl<T> implements PaginationResult<T> { class _PaginationResultImpl<T> implements PaginationResult<T> {
final Iterable<T> _data; final Iterable<T> _data;
Iterable<T> _cachedData; Iterable<T>? _cachedData;
@override @override
final int currentPage; final int? currentPage;
_PaginationResultImpl(this._data, _PaginationResultImpl(this._data,
{this.currentPage, {this.currentPage,
@ -155,25 +158,25 @@ class _PaginationResultImpl<T> implements PaginationResult<T> {
this.total}); this.total});
@override @override
List<T> get data => _cachedData ?? (_cachedData = new List<T>.from(_data)); Iterable<T> get data => _cachedData ?? (_cachedData = List<T>.from(_data));
@override @override
final int endIndex; final int? endIndex;
@override @override
final int itemsPerPage; final int? itemsPerPage;
@override @override
final int nextPage; final int? nextPage;
@override @override
final int previousPage; final int? previousPage;
@override @override
final int startIndex; final int? startIndex;
@override @override
final int total; final int? total;
@override @override
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {

View file

@ -1,15 +1,22 @@
name: angel_paginate name: angel_paginate
version: 2.0.0 version: 3.0.0
description: Platform-agnostic pagination library, with custom support for the Angel framework. description: Platform-agnostic pagination library, with custom support for the Angel framework.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/paginate homepage: https://github.com/angel-dart/paginate
publish_to: none
environment: environment:
sdk: ">=2.10.0 <2.12.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel_framework: #^2.0.0-alpha angel_framework:
path: ../framework git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/framework
dev_dependencies: dev_dependencies:
angel_test: #^2.0.0 angel_test:
path: ../test git:
logging: ^0.11.0 url: https://github.com/dukefirehawk/angel.git
test: ^1.15.7 ref: sdk-2.12.x_nnbd
path: packages/test
logging: ^1.0.1
test: ^1.17.8
pedantic: ^1.11.1

View file

@ -2,7 +2,7 @@ import 'package:test/test.dart';
import 'bounds_test.dart' as bounds; import 'bounds_test.dart' as bounds;
import 'paginate_test.dart' as paginate; import 'paginate_test.dart' as paginate;
main() { void main() {
group('bounds', bounds.main); group('bounds', bounds.main);
group('paginate', paginate.main); group('paginate', paginate.main);
} }

View file

@ -11,15 +11,15 @@ final List<Map<String, String>> mjAlbums = [
{'michael': 'jackson'} {'michael': 'jackson'}
]; ];
main() { void main() {
TestClient client; late TestClient client;
setUp(() async { setUp(() async {
var app = Angel(); var app = Angel();
app.get('/api/songs', (req, res) { app.get('/api/songs', (req, res) {
var p = Paginator(mjAlbums, itemsPerPage: mjAlbums.length); var p = Paginator(mjAlbums, itemsPerPage: mjAlbums.length);
p.goToPage(int.parse(req.queryParameters['page'] ?? '1')); p.goToPage(int.parse(req.queryParameters['page'] as String? ?? '1'));
return p.current; return p.current;
}); });
@ -41,7 +41,7 @@ main() {
queryParameters: {r'$limit': (mjAlbums.length + 1).toString()})); queryParameters: {r'$limit': (mjAlbums.length + 1).toString()}));
var page = PaginationResult<Map<String, dynamic>>.fromMap( var page = PaginationResult<Map<String, dynamic>>.fromMap(
json.decode(response.body)); json.decode(response.body) as Map<String, dynamic>);
print('page: ${page.toJson()}'); print('page: ${page.toJson()}');

View file

@ -53,7 +53,7 @@ void main() {
test('first page', () { test('first page', () {
var paginator = Paginator<int>(DATA); var paginator = Paginator<int>(DATA);
expect(paginator.pageNumber, 1); expect(paginator.pageNumber, 1);
var r = paginator.current; var r = paginator.current!;
print(r.toJson()); print(r.toJson());
expect(r.total, DATA.length); expect(r.total, DATA.length);
expect(r.itemsPerPage, 5); expect(r.itemsPerPage, 5);
@ -62,14 +62,14 @@ void main() {
expect(r.nextPage, 2); expect(r.nextPage, 2);
expect(r.startIndex, 0); expect(r.startIndex, 0);
expect(r.endIndex, 4); expect(r.endIndex, 4);
expect(r.data, DATA.skip(r.startIndex).take(r.itemsPerPage).toList()); expect(r.data, DATA.skip(r.startIndex!).take(r.itemsPerPage!).toList());
}); });
}); });
test('third page', () { test('third page', () {
var paginator = Paginator<int>(DATA)..goToPage(3); var paginator = Paginator<int>(DATA)..goToPage(3);
expect(paginator.pageNumber, 3); expect(paginator.pageNumber, 3);
var r = paginator.current; var r = paginator.current!;
print(r.toJson()); print(r.toJson());
expect(r.total, DATA.length); expect(r.total, DATA.length);
expect(r.itemsPerPage, 5); expect(r.itemsPerPage, 5);
@ -78,7 +78,7 @@ void main() {
expect(r.nextPage, 4); expect(r.nextPage, 4);
expect(r.startIndex, 10); expect(r.startIndex, 10);
expect(r.endIndex, 14); expect(r.endIndex, 14);
expect(r.data, DATA.skip(r.startIndex).take(r.itemsPerPage).toList()); expect(r.data, DATA.skip(r.startIndex!).take(r.itemsPerPage!).toList());
paginator.back(); paginator.back();
expect(paginator.pageNumber, 2); expect(paginator.pageNumber, 2);
@ -87,7 +87,7 @@ void main() {
test('last page', () { test('last page', () {
var paginator = Paginator<int>(DATA); var paginator = Paginator<int>(DATA);
paginator.goToPage(paginator.lastPageNumber); paginator.goToPage(paginator.lastPageNumber);
var r = paginator.current; var r = paginator.current!;
expect(r.total, DATA.length); expect(r.total, DATA.length);
expect(r.itemsPerPage, 5); expect(r.itemsPerPage, 5);
expect(r.previousPage, paginator.lastPageNumber - 1); expect(r.previousPage, paginator.lastPageNumber - 1);
@ -96,7 +96,7 @@ void main() {
expect(r.startIndex, (paginator.lastPageNumber - 1) * 5); expect(r.startIndex, (paginator.lastPageNumber - 1) * 5);
expect(r.endIndex, r.startIndex); expect(r.endIndex, r.startIndex);
expect(r.data, [DATA.last]); expect(r.data, [DATA.last]);
expect(r.data, DATA.skip(r.startIndex).take(r.itemsPerPage).toList()); expect(r.data, DATA.skip(r.startIndex!).take(r.itemsPerPage!).toList());
}); });
test('dump pages', () { test('dump pages', () {
@ -104,14 +104,14 @@ void main() {
print('${paginator.lastPageNumber} page(s) of data:'); print('${paginator.lastPageNumber} page(s) of data:');
do { do {
print(' * Page #${paginator.pageNumber}: ${paginator.current.data}'); print(' * Page #${paginator.pageNumber}: ${paginator.current!.data}');
paginator.next(); paginator.next();
} while (paginator.canGoForward); } while (paginator.canGoForward);
}); });
test('empty collection', () { test('empty collection', () {
var paginator = Paginator([]); var paginator = Paginator([]);
var page = paginator.current; var page = paginator.current!;
print(page.toJson()); print(page.toJson());
expect(page.total, 0); expect(page.total, 0);

12
packages/poll/AUTHORS.md Normal file
View 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.

View file

@ -1,2 +1,5 @@
# 2.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 1.0.0 # 1.0.0
* Created package + tests * Created package + tests

View file

@ -1,4 +1,4 @@
MIT License (MIT) MIT License
Copyright (c) 2021 dukefirehawk.com Copyright (c) 2021 dukefirehawk.com

View file

@ -1,2 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer: analyzer:
strong-mode: true strong-mode:
implicit-casts: false

View file

@ -1,11 +1,11 @@
import 'package:angel_client/io.dart'; import 'package:angel_client/io.dart';
import 'package:angel_poll/angel_poll.dart'; import 'package:angel_poll/angel_poll.dart';
main() { void main() {
var app = new Rest('http://localhost:3000'); var app = Rest('http://localhost:3000');
var todos = new ServiceList( var todos = ServiceList(
new PollingService( PollingService(
// Typically, you'll pass a REST-based service instance here. // Typically, you'll pass a REST-based service instance here.
app.service('api/todos'), app.service('api/todos'),

View file

@ -41,21 +41,20 @@ class PollingService extends Service {
final List _items = []; final List _items = [];
final List<StreamSubscription> _subs = []; final List<StreamSubscription> _subs = [];
final StreamController _onIndexed = new StreamController(), final StreamController<List<dynamic>> _onIndexed = StreamController(),
_onRead = new StreamController(), _onRead = StreamController(),
_onCreated = new StreamController(), _onCreated = StreamController(),
_onModified = new StreamController(), _onModified = StreamController(),
_onUpdated = new StreamController(), _onUpdated = StreamController(),
_onRemoved = new StreamController(); _onRemoved = StreamController();
Timer _timer; late Timer _timer;
@override @override
Angel get app => inner.app; Angel get app => inner.app;
// TODO: To revisit this logic
@override @override
Stream<List> get onIndexed => _onIndexed.stream; Stream<List<dynamic>> get onIndexed => _onIndexed.stream;
@override @override
Stream get onRead => _onRead.stream; Stream get onRead => _onRead.stream;
@ -73,16 +72,18 @@ class PollingService extends Service {
Stream get onRemoved => _onRemoved.stream; Stream get onRemoved => _onRemoved.stream;
PollingService(this.inner, Duration interval, PollingService(this.inner, Duration interval,
{this.checkForCreated: true, {this.checkForCreated = true,
this.checkForModified: true, this.checkForModified = true,
this.checkForRemoved: true, this.checkForRemoved = true,
this.idField: 'id', this.idField = 'id',
this.asPaginated: false, this.asPaginated = false,
EqualityBy compareId, EqualityBy? compareId,
this.compareItems: const MapEquality()}) this.compareItems = const MapEquality()})
: compareId = compareId ?? new EqualityBy((map) => map[idField ?? 'id']) { : compareId = compareId ?? EqualityBy((map) => map[idField]) {
_timer = new Timer.periodic(interval, (_) { _timer = Timer.periodic(interval, (_) {
index().catchError(_onIndexed.addError); index().catchError((error) {
_onIndexed.addError(error as Object);
});
}); });
var streams = <Stream, StreamController>{ var streams = <Stream, StreamController>{
@ -109,20 +110,21 @@ class PollingService extends Service {
Future close() async { Future close() async {
_timer.cancel(); _timer.cancel();
_subs.forEach((s) => s.cancel()); _subs.forEach((s) => s.cancel());
_onIndexed.close(); await _onIndexed.close();
_onRead.close(); await _onRead.close();
_onCreated.close(); await _onCreated.close();
_onModified.close(); await _onModified.close();
_onUpdated.close(); await _onUpdated.close();
_onRemoved.close(); await _onRemoved.close();
} }
// TODO: To revisit this logic // TODO: To revisit this logic
@override @override
Future<List> index([Map params]) { Future<List<dynamic>> index([Map? params]) {
return inner.index().then((data) { return inner.index().then((data) {
//return asPaginated == true ? data['data'] : data; //return asPaginated == true ? data['data'] : data;
return asPaginated == true ? data[0] : data; //return asPaginated == true ? data[0] : data;
return data!;
}); });
} }
@ -132,17 +134,17 @@ class PollingService extends Service {
} }
*/ */
@override @override
Future remove(id, [Map params]) { Future remove(id, [Map<String, dynamic>? params]) {
return inner.remove(id, params).then((result) { return inner.remove(id, params).then((result) {
_items.remove(result); _items.remove(result);
return result; return result;
}).catchError(_onRemoved.addError); }).catchError(_onRemoved.addError);
} }
_handleUpdate(result) { dynamic _handleUpdate(result) {
int index = -1; var index = -1;
for (int i = 0; i < _items.length; i++) { for (var i = 0; i < _items.length; i++) {
if (compareId.equals(_items[i], result)) { if (compareId.equals(_items[i], result)) {
index = i; index = i;
break; break;
@ -157,7 +159,7 @@ class PollingService extends Service {
} }
@override @override
Future update(id, data, [Map params]) { Future update(id, data, [Map<String, dynamic>? params]) {
return inner return inner
.update(id, data, params) .update(id, data, params)
.then(_handleUpdate) .then(_handleUpdate)
@ -165,7 +167,7 @@ class PollingService extends Service {
} }
@override @override
Future modify(id, data, [Map params]) { Future modify(id, data, [Map<String, dynamic>? params]) {
return inner return inner
.modify(id, data, params) .modify(id, data, params)
.then(_handleUpdate) .then(_handleUpdate)
@ -173,7 +175,7 @@ class PollingService extends Service {
} }
@override @override
Future create(data, [Map params]) { Future create(data, [Map<String, dynamic>? params]) {
return inner.create(data, params).then((result) { return inner.create(data, params).then((result) {
_items.add(result); _items.add(result);
return result; return result;
@ -181,18 +183,19 @@ class PollingService extends Service {
} }
@override @override
Future read(id, [Map params]) { Future read(id, [Map<String, dynamic>? params]) {
return inner.read(id, params); return inner.read(id, params);
} }
void _handleIndexed(data) { void _handleIndexed(List<dynamic> data) {
var items = asPaginated == true ? data['data'] : data; //var items = asPaginated == true ? data['data'] : data;
bool changesComputed = false; var items = data;
var changesComputed = false;
if (checkForCreated != false) { if (checkForCreated != false) {
var newItems = <int, dynamic>{}; var newItems = <int, dynamic>{};
for (int i = 0; i < items.length; i++) { for (var i = 0; i < items.length; i++) {
var item = items[i]; var item = items[i];
if (!_items.any((i) => compareId.equals(i, item))) { if (!_items.any((i) => compareId.equals(i, item))) {
@ -202,7 +205,7 @@ class PollingService extends Service {
newItems.forEach((index, item) { newItems.forEach((index, item) {
_items.insert(index, item); _items.insert(index, item);
_onCreated.add(item); _onCreated.add([item]);
}); });
changesComputed = newItems.isNotEmpty; changesComputed = newItems.isNotEmpty;
@ -211,7 +214,7 @@ class PollingService extends Service {
if (checkForRemoved != false) { if (checkForRemoved != false) {
var removedItems = <int, dynamic>{}; var removedItems = <int, dynamic>{};
for (int i = 0; i < _items.length; i++) { for (var i = 0; i < _items.length; i++) {
var item = _items[i]; var item = _items[i];
if (!items.any((i) => compareId.equals(i, item))) { if (!items.any((i) => compareId.equals(i, item))) {
@ -221,7 +224,7 @@ class PollingService extends Service {
removedItems.forEach((index, item) { removedItems.forEach((index, item) {
_items.removeAt(index); _items.removeAt(index);
_onRemoved.add(item); _onRemoved.add([item]);
}); });
changesComputed = changesComputed || removedItems.isNotEmpty; changesComputed = changesComputed || removedItems.isNotEmpty;
@ -231,7 +234,7 @@ class PollingService extends Service {
var modifiedItems = <int, dynamic>{}; var modifiedItems = <int, dynamic>{};
for (var item in items) { for (var item in items) {
for (int i = 0; i < _items.length; i++) { for (var i = 0; i < _items.length; i++) {
var localItem = _items[i]; var localItem = _items[i];
if (compareId.equals(item, localItem)) { if (compareId.equals(item, localItem)) {
@ -244,7 +247,7 @@ class PollingService extends Service {
} }
modifiedItems.forEach((index, item) { modifiedItems.forEach((index, item) {
_onModified.add(_items[index] = item); _onModified.add([_items[index] = item]);
}); });
changesComputed = changesComputed || modifiedItems.isNotEmpty; changesComputed = changesComputed || modifiedItems.isNotEmpty;
@ -253,8 +256,8 @@ class PollingService extends Service {
if (!changesComputed) { if (!changesComputed) {
_items _items
..clear() ..clear()
..addAll(items); ..add(items);
_onIndexed.add(items); _onIndexed.add([items]);
} }
} }
} }

View file

@ -1,16 +1,23 @@
name: angel_poll name: angel_poll
version: 1.0.0 version: 2.0.0
description: package:angel_client support for "realtime" interactions with Angel via long polling. description: package:angel_client support for "realtime" interactions with Angel via long polling.
author: Tobe O <thosakwe@gmail.com> publish_to: none
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: '>=2.12.0 <3.0.0'
homepage: https://github.com/angel-dart/poll homepage: https://github.com/angel-dart/poll
dependencies: dependencies:
angel_client: # ^1.0.0 angel_client: # ^1.0.0
path: ../client git:
async: ">=1.10.0 <3.0.0" url: https://github.com/dukefirehawk/angel.git
collection: ^1.14.12 ref: sdk-2.12.x_nnbd
path: packages/client
async: ^2.7.0
collection: ^1.15.0
dev_dependencies: dev_dependencies:
angel_test: # ^1.1.0 angel_test:
path: ../test git:
test: ^1.15.7 url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/test
test: ^1.17.8
pedantic: ^1.11.1

View file

@ -1,4 +1,5 @@
import 'package:angel_framework/angel_framework.dart' as srv; import 'package:angel_framework/angel_framework.dart' as srv;
import 'package:angel_container/mirrors.dart';
import 'package:angel_poll/angel_poll.dart'; import 'package:angel_poll/angel_poll.dart';
import 'package:angel_test/angel_test.dart'; import 'package:angel_test/angel_test.dart';
import 'package:async/async.dart'; import 'package:async/async.dart';
@ -6,13 +7,18 @@ import 'package:logging/logging.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
srv.Service store; late srv.Service store;
TestClient client; late TestClient client;
PollingService pollingService; late PollingService pollingService;
var created;
late StreamQueue onCreated;
late StreamQueue onModified;
late StreamQueue onRemoved;
setUp(() async { setUp(() async {
var app = new srv.Angel(); var app = srv.Angel(reflector: MirrorsReflector());
app.logger = new Logger.detached('angel_poll') app.logger = Logger.detached('angel_poll')
..onRecord.listen((rec) { ..onRecord.listen((rec) {
print(rec); print(rec);
if (rec.error != null) { if (rec.error != null) {
@ -23,29 +29,21 @@ void main() {
store = app.use( store = app.use(
'/api/todos', '/api/todos',
new srv.MapService( srv.MapService(
autoIdAndDateFields: false, autoIdAndDateFields: false,
), ),
); );
client = await connectTo(app); client = await connectTo(app);
pollingService = new PollingService( pollingService = PollingService(
client.service('api/todos'), client.service('api/todos'),
const Duration(milliseconds: 100), const Duration(milliseconds: 100),
); );
});
tearDown(() => client.close()); onCreated = StreamQueue(pollingService.onCreated);
onModified = StreamQueue(pollingService.onModified);
group('events', () { onRemoved = StreamQueue(pollingService.onRemoved);
var created;
StreamQueue onCreated, onModified, onRemoved;
setUp(() async {
onCreated = new StreamQueue(pollingService.onCreated);
onModified = new StreamQueue(pollingService.onModified);
onRemoved = new StreamQueue(pollingService.onRemoved);
created = await store.create({ created = await store.create({
'id': '0', 'id': '0',
@ -58,8 +56,10 @@ void main() {
onCreated.cancel(); onCreated.cancel();
onModified.cancel(); onModified.cancel();
onRemoved.cancel(); onRemoved.cancel();
client.close();
}); });
group('events', () {
test('fires indexed', () async { test('fires indexed', () async {
var indexed = await pollingService.index(); var indexed = await pollingService.index();
print(indexed); print(indexed);
@ -80,7 +80,7 @@ void main() {
var result = await onModified.next; var result = await onModified.next;
print(result); print(result);
expect(result, new Map.from(created)..['text'] = 'go to school'); expect(result, Map.from({'': created})..['text'] = 'go to school');
}); });
test('manual modify', () async { test('manual modify', () async {
@ -91,7 +91,7 @@ void main() {
var result = await onModified.next; var result = await onModified.next;
print(result); print(result);
expect(result, new Map.from(created)..['text'] = 'eat'); expect(result, Map.from({'': created})..['text'] = 'eat');
}); });
test('fires removed', () async { test('fires removed', () async {

12
packages/redis/AUTHORS.md Normal file
View 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.

View file

@ -1,2 +1,5 @@
# 2.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 1.0.0 # 1.0.0
* First version. * First version.

View file

@ -1,6 +1,6 @@
MIT License (MIT) MIT License
Copyright (c) 2021 dukefirehawk.com Copyright (c) 2018 The Angel Framework
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View file

@ -0,0 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,17 +1,18 @@
import 'package:angel_redis/angel_redis.dart'; import 'package:angel_redis/angel_redis.dart';
import 'package:resp_client/resp_client.dart'; import 'package:resp_client/resp_client.dart';
import 'package:resp_client/resp_commands.dart'; import 'package:resp_client/resp_commands.dart';
import 'package:resp_client/resp_server.dart';
main() async { void main() async {
var connection = await connectSocket('localhost'); var connection = await connectSocket('localhost');
var client = new RespClient(connection); var client = RespClient(connection);
var service = new RedisService(new RespCommands(client), prefix: 'example'); var service = RedisService(RespCommandsTier2(client), prefix: 'example');
// Create an object // Create an object
await service.create({'id': 'a', 'hello': 'world'}); await service.create({'id': 'a', 'hello': 'world'});
// Read it... // Read it...
var read = await service.read('a'); var read = await (service.read('a'));
print(read['hello']); print(read['hello']);
// Delete it. // Delete it.

View file

@ -6,92 +6,99 @@ import 'package:resp_client/resp_commands.dart';
/// An Angel service that reads and writes JSON within a Redis store. /// An Angel service that reads and writes JSON within a Redis store.
class RedisService extends Service<String, Map<String, dynamic>> { class RedisService extends Service<String, Map<String, dynamic>> {
final RespCommands respCommands; final RespCommandsTier2 respCommands;
/// An optional string prefixed to keys before they are inserted into Redis. /// An optional string prefixed to keys before they are inserted into Redis.
/// ///
/// Consider using this if you are using several different Redis collections /// Consider using this if you are using several different Redis collections
/// within a single application. /// within a single application.
final String prefix; final String? prefix;
RedisService(this.respCommands, {this.prefix}); RedisService(this.respCommands, {this.prefix});
String _applyPrefix(String id) => prefix == null ? id : '$prefix:$id'; String? _applyPrefix(String? id) => prefix == null ? id : '$prefix:$id';
@override @override
Future<List<Map<String, dynamic>>> index( Future<List<Map<String, dynamic>>> index(
[Map<String, dynamic> params]) async { [Map<String, dynamic>? params]) async {
var result = var result =
await respCommands.client.writeArrayOfBulk(['KEYS', _applyPrefix('*')]); //await respCommands.client.writeArrayOfBulk(['KEYS', _applyPrefix('*')]);
await respCommands.tier1.tier0.execute(['KEYS', _applyPrefix('*')]);
var keys = result.payload.map((RespType s) => s.payload) as Iterable; var keys = result.payload.map((RespType s) => s.payload) as Iterable;
if (keys.isEmpty) return []; if (keys.isEmpty) return [];
result = await respCommands.client.writeArrayOfBulk(['MGET']..addAll(keys)); //result = await respCommands.client.writeArrayOfBulk(['MGET']..addAll(keys));
return result.payload result = await respCommands.tier1.tier0.execute(['MGET', ...keys]);
.map<Map<String, dynamic>>(
(RespType s) => json.decode(s.payload) as Map<String, dynamic>) if (result.isArray) {
return (result as List<RespType>)
.map<Map<String, dynamic>>((RespType s) =>
json.decode(s.payload as String) as Map<String, dynamic>)
.toList(); .toList();
} else {
// TODO: To be reviewed for handling none array objects
return [json.decode(result.payload as String) as Map<String, dynamic>];
}
} }
@override @override
Future<Map<String, dynamic>> read(String id, Future<Map<String, dynamic>> read(String id,
[Map<String, dynamic> params]) async { [Map<String, dynamic>? params]) async {
var value = await respCommands.get(_applyPrefix(id)); var value = await respCommands.get(_applyPrefix(id)!);
if (value == null) { if (value == null) {
throw new AngelHttpException.notFound( throw AngelHttpException.notFound(message: 'No record found for ID $id');
message: 'No record found for ID $id');
} else { } else {
return json.decode(value); return json.decode(value) as Map<String, dynamic>;
} }
} }
@override @override
Future<Map<String, dynamic>> create(Map<String, dynamic> data, Future<Map<String, dynamic>> create(Map<String, dynamic>? data,
[Map<String, dynamic> params]) async { [Map<String, dynamic>? params]) async {
String id; String? id;
if (data['id'] != null) if (data!['id'] != null) {
id = data['id'] as String; id = data['id'] as String?;
else { } else {
var keyVar = await respCommands.client var keyVar = await respCommands.tier1.tier0
.writeArrayOfBulk(['INCR', _applyPrefix('angel_redis:id')]); .execute(['INCR', _applyPrefix('angel_redis:id')]);
id = keyVar.payload.toString(); id = keyVar.payload.toString();
data = new Map<String, dynamic>.from(data)..['id'] = id; data = Map<String, dynamic>.from(data)..['id'] = id;
} }
await respCommands.set(_applyPrefix(id), json.encode(data)); await respCommands.set(_applyPrefix(id)!, json.encode(data));
return data; return data;
} }
@override @override
Future<Map<String, dynamic>> modify(String id, Map<String, dynamic> data, Future<Map<String, dynamic>> modify(String id, Map<String, dynamic>? data,
[Map<String, dynamic> params]) async { [Map<String, dynamic>? params]) async {
var input = await read(id); var input = await (read(id));
input.addAll(data); input.addAll(data!);
return await update(id, input, params); return await update(id, input, params);
} }
@override @override
Future<Map<String, dynamic>> update(String id, Map<String, dynamic> data, Future<Map<String, dynamic>> update(String id, Map<String, dynamic>? data,
[Map<String, dynamic> params]) async { [Map<String, dynamic>? params]) async {
data = new Map<String, dynamic>.from(data)..['id'] = id; data = Map<String, dynamic>.from(data!)..['id'] = id;
await respCommands.set(_applyPrefix(id), json.encode(data)); await respCommands.set(_applyPrefix(id)!, json.encode(data));
return data; return data;
} }
@override @override
Future<Map<String, dynamic>> remove(String id, Future<Map<String, dynamic>> remove(String id,
[Map<String, dynamic> params]) async { [Map<String, dynamic>? params]) async {
var client = respCommands.client; var client = respCommands.tier1.tier0;
await client.writeArrayOfBulk(['MULTI']); await client.execute(['MULTI']);
await client.writeArrayOfBulk(['GET', _applyPrefix(id)]); await client.execute(['GET', _applyPrefix(id)]);
await client.writeArrayOfBulk(['DEL', _applyPrefix(id)]); await client.execute(['DEL', _applyPrefix(id)]);
var result = await client.writeArrayOfBulk(['EXEC']); var result = await client.execute(['EXEC']);
var str = result.payload[0] as RespBulkString; var str = result.payload[0] as RespBulkString;
if (str.payload == null) if (str.payload == null) {
throw new AngelHttpException.notFound( throw AngelHttpException.notFound(message: 'No record found for ID $id');
message: 'No record found for ID $id'); } else {
else return json.decode(str.payload!) as Map<String, dynamic>;
return json.decode(str.payload); }
} }
} }

View file

@ -1,15 +1,22 @@
name: angel_redis name: angel_redis
version: 1.0.0 version: 2.0.0
description: An Angel service provider for Redis. Works well for caching volatile data. description: An Angel service provider for Redis. Works well for caching volatile data.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/redis homepage: https://github.com/angel-dart/redis
publish_to: none
environment: environment:
sdk: ">=2.10.0 <2.12.0" sdk: '>=2.12.0 <3.0.0'
dependencies: dependencies:
angel_framework: #^2.0.0-alpha angel_framework:
path: ../framework git:
angel_http_exception: #^1.0.0 url: https://github.com/dukefirehawk/angel.git
path: ../http_exception ref: sdk-2.12.x_nnbd
resp_client: ^0.1.6 path: packages/framework
angel_http_exception:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/http_exception
resp_client: ^1.2.0
dev_dependencies: dev_dependencies:
test: ^1.15.7 test: ^1.17.8
pedantic: ^1.11.1

View file

@ -2,15 +2,16 @@ import 'package:angel_http_exception/angel_http_exception.dart';
import 'package:angel_redis/angel_redis.dart'; import 'package:angel_redis/angel_redis.dart';
import 'package:resp_client/resp_client.dart'; import 'package:resp_client/resp_client.dart';
import 'package:resp_client/resp_commands.dart'; import 'package:resp_client/resp_commands.dart';
import 'package:resp_client/resp_server.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
main() async { void main() async {
RespServerConnection connection; late RespServerConnection connection;
RedisService service; late RedisService service;
setUp(() async { setUp(() async {
connection = await connectSocket('localhost'); connection = await connectSocket('localhost');
service = RedisService(RespCommands(RespClient(connection)), service = RedisService(RespCommandsTier2(RespClient(connection)),
prefix: 'angel_redis_test'); prefix: 'angel_redis_test');
}); });
@ -36,7 +37,7 @@ main() async {
test('create without id', () async { test('create without id', () async {
var input = {'bar': 'baz'}; var input = {'bar': 'baz'};
var output = await service.create(input); var output = await (service.create(input));
print(output); print(output);
expect(output.keys, contains('id')); expect(output.keys, contains('id'));
expect(output, containsPair('bar', 'baz')); expect(output, containsPair('bar', 'baz'));

View 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.

View file

@ -0,0 +1,5 @@
# 2.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 1.0.0
* Initial checkin

View file

@ -1,4 +1,4 @@
MIT License (MIT) MIT License
Copyright (c) 2021 dukefirehawk.com Copyright (c) 2021 dukefirehawk.com

View file

@ -0,0 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -12,51 +12,44 @@ import 'no_service.dart';
/// * [localKey]: `userId` /// * [localKey]: `userId`
/// * [foreignKey]: `id` /// * [foreignKey]: `id`
HookedServiceEventListener belongsTo(Pattern servicePath, HookedServiceEventListener belongsTo(Pattern servicePath,
{String as, {String? as,
String foreignKey, String? foreignKey,
String localKey, String? localKey,
getForeignKey(obj), Function(dynamic obj)? getForeignKey,
assignForeignObject(foreign, obj)}) { Function(dynamic foreign, dynamic obj)? assignForeignObject}) {
String localId = localKey; var localId = localKey;
var foreignName = var foreignName =
as?.isNotEmpty == true ? as : pluralize.singular(servicePath.toString()); as?.isNotEmpty == true ? as! : pluralize.singular(servicePath.toString());
if (localId == null) { localId ??= foreignName + 'Id';
localId = foreignName + 'Id';
// print('No local key provided for belongsTo, defaulting to \'$localId\'.');
}
return (HookedServiceEvent e) async { return (HookedServiceEvent e) async {
var ref = e.getService(servicePath); var ref = e.getService(servicePath);
if (ref == null) throw noService(servicePath); if (ref == null) throw noService(servicePath);
_getForeignKey(obj) { dynamic _getForeignKey(obj) {
if (getForeignKey != null) if (getForeignKey != null) {
return getForeignKey(obj); return getForeignKey(obj);
else if (obj is Map) } else if (obj is Map) {
return obj[localId]; return obj[localId];
//TODO: Undefined class } else if (localId == null || localId == 'userId') {
//else if (obj is Extensible)
// return obj.properties[localId];
else if (localId == null || localId == 'userId')
return obj.userId; return obj.userId;
else } else {
return reflect(obj).getField(new Symbol(localId)).reflectee; return reflect(obj).getField(Symbol(localId)).reflectee;
}
} }
_assignForeignObject(foreign, obj) { dynamic _assignForeignObject(foreign, obj) {
if (assignForeignObject != null) if (assignForeignObject != null) {
return assignForeignObject(foreign, obj); return assignForeignObject(foreign, obj);
else if (obj is Map) } else if (obj is Map) {
obj[foreignName] = foreign; obj[foreignName] = foreign;
//TODO: Undefined class } else {
//else if (obj is Extensible) reflect(obj).setField(Symbol(foreignName), foreign);
// obj.properties[foreignName] = foreign; }
else
reflect(obj).setField(new Symbol(foreignName), foreign);
} }
_normalize(obj) async { Future _normalize(obj) async {
if (obj != null) { if (obj != null) {
var id = await _getForeignKey(obj); var id = await _getForeignKey(obj);
var indexed = await ref.index({ var indexed = await ref.index({
@ -73,8 +66,10 @@ HookedServiceEventListener belongsTo(Pattern servicePath,
} }
if (e.result is Iterable) { if (e.result is Iterable) {
await Future.wait(e.result.map(_normalize)); //await Future.wait(e.result.map(_normalize));
} else await e.result.map(_normalize);
} else {
await _normalize(e.result); await _normalize(e.result);
}
}; };
} }

View file

@ -12,51 +12,44 @@ import 'no_service.dart';
/// * [foreignKey]: `userId` /// * [foreignKey]: `userId`
/// * [localKey]: `id` /// * [localKey]: `id`
HookedServiceEventListener belongsToMany(Pattern servicePath, HookedServiceEventListener belongsToMany(Pattern servicePath,
{String as, {String? as,
String foreignKey, String? foreignKey,
String localKey, String? localKey,
getForeignKey(obj), Function(dynamic obj)? getForeignKey,
assignForeignObject(List foreign, obj)}) { Function(dynamic foreign, dynamic obj)? assignForeignObject}) {
String localId = localKey; var localId = localKey;
var foreignName = var foreignName =
as?.isNotEmpty == true ? as : pluralize.plural(servicePath.toString()); as?.isNotEmpty == true ? as! : pluralize.plural(servicePath.toString());
if (localId == null) { localId ??= foreignName + 'Id';
localId = foreignName + 'Id';
// print('No local key provided for belongsToMany, defaulting to \'$localId\'.');
}
return (HookedServiceEvent e) async { return (HookedServiceEvent e) async {
var ref = e.getService(servicePath); var ref = e.getService(servicePath);
if (ref == null) throw noService(servicePath); if (ref == null) throw noService(servicePath);
_getForeignKey(obj) { dynamic _getForeignKey(obj) {
if (getForeignKey != null) if (getForeignKey != null) {
return getForeignKey(obj); return getForeignKey(obj);
else if (obj is Map) } else if (obj is Map) {
return obj[localId]; return obj[localId];
//TODO: Undefined class } else if (localId == null || localId == 'userId') {
//else if (obj is Extensible)
// return obj.properties[localId];
else if (localId == null || localId == 'userId')
return obj.userId; return obj.userId;
else } else {
return reflect(obj).getField(new Symbol(localId)).reflectee; return reflect(obj).getField(Symbol(localId)).reflectee;
}
} }
_assignForeignObject(foreign, obj) { dynamic _assignForeignObject(foreign, obj) {
if (assignForeignObject != null) if (assignForeignObject != null) {
return assignForeignObject(foreign, obj); return assignForeignObject(foreign as List?, obj);
else if (obj is Map) } else if (obj is Map) {
obj[foreignName] = foreign; obj[foreignName] = foreign;
//TODO: Undefined class } else {
//else if (obj is Extensible) reflect(obj).setField(Symbol(foreignName), foreign);
// obj.properties[foreignName] = foreign; }
else
reflect(obj).setField(new Symbol(foreignName), foreign);
} }
_normalize(obj) async { Future<void> _normalize(obj) async {
if (obj != null) { if (obj != null) {
var id = await _getForeignKey(obj); var id = await _getForeignKey(obj);
var indexed = await ref.index({ var indexed = await ref.index({
@ -73,8 +66,10 @@ HookedServiceEventListener belongsToMany(Pattern servicePath,
} }
if (e.result is Iterable) { if (e.result is Iterable) {
await Future.wait(e.result.map(_normalize)); //await Future.wait(e.result.map(_normalize));
} else await e.result.map(_normalize);
} else {
await _normalize(e.result); await _normalize(e.result);
}
}; };
} }

Some files were not shown because too many files have changed in this diff Show more