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
packages
body_parser
html
jael
jinja
markdown
mustache
paginate
poll
redis
relations

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; return value;
}
} on FormatException { } on FormatException {
if (value.startsWith('[') && value.endsWith(']')) if (value.startsWith('[') && value.endsWith(']')) {
return json.decode(value); return json.decode(value);
else if (value.startsWith('{') && value.endsWith('}')) } else if (value.startsWith('{') && value.endsWith('}')) {
return json.decode(value); return json.decode(value);
else if (value.trim().toLowerCase() == 'null') } else if (value.trim().toLowerCase() == 'null') {
return null; return null;
else } else {
return value; 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
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

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,27 +429,26 @@ 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 TextEdit((b) {
return new TextEdit((b) { b
b ..range = toRange(u.span)
..range = toRange(u.span) ..newText = (symbol is JaelCustomElement &&
..newText = (symbol is JaelCustomElement && u.type == SymbolUsageType.definition)
u.type == SymbolUsageType.definition) ? '"$newName"'
? '"$newName"' : newName;
: newName; });
}); }).toList()
}).toList() };
};
}); });
} }
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
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

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

@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

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
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

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,10 +65,11 @@ 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() {
@ -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
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

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,43 +29,37 @@ 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),
); );
onCreated = StreamQueue(pollingService.onCreated);
onModified = StreamQueue(pollingService.onModified);
onRemoved = StreamQueue(pollingService.onRemoved);
created = await store.create({
'id': '0',
'text': 'Clean your room',
'completed': false,
});
}); });
tearDown(() => client.close()); tearDown(() {
onCreated.cancel();
onModified.cancel();
onRemoved.cancel();
client.close();
});
group('events', () { group('events', () {
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({
'id': '0',
'text': 'Clean your room',
'completed': false,
});
});
tearDown(() {
onCreated.cancel();
onModified.cancel();
onRemoved.cancel();
});
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
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

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) {
.toList(); return (result as List<RespType>)
.map<Map<String, dynamic>>((RespType s) =>
json.decode(s.payload as String) as Map<String, dynamic>)
.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
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

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);
}
}; };
} }

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