Merged from sdk-2.12.x_nnbd

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,12 @@
Primary Authors
===============
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
Thomas is the current maintainer of the code base. He has refactored and migrated the
code base to support NNBD.
* __[Tobe O](thosakwe@gmail.com)__
Tobe has written much of the original code prior to NNBD migration. He has moved on and
is no longer involved with the project.

View file

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

View file

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

View file

@ -5,16 +5,16 @@ import 'dart:isolate';
import 'package:http_parser/http_parser.dart';
import 'package:body_parser/body_parser.dart';
main() async {
void main() async {
var address = '127.0.0.1';
var port = 3000;
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]));
}
Future.wait(futures).then((_) {
await Future.wait(futures).then((_) {
print('All instances started.');
print(
'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) {
var address = new InternetAddress(args[0] as String);
int port = 8080;
var address = InternetAddress(args[0] as String);
var port = 8080;
if (args[1] is int) {
args[1];
}
int id = 0;
var id = 0;
if (args[2] is int) {
args[2];
}
@ -39,9 +39,9 @@ void start(List args) {
// ignore: deprecated_member_use
var body = await defaultParseBody(request);
request.response
..headers.contentType = new ContentType('application', 'json')
..write(json.encode(body.body))
..close();
..headers.contentType = ContentType('application', 'json')
..write(json.encode(body.body));
await request.response.close();
});
print(
@ -50,11 +50,11 @@ void start(List args) {
}
Future<BodyParseResult> defaultParseBody(HttpRequest request,
{bool storeOriginalBuffer: false}) {
{bool storeOriginalBuffer = false}) {
return parseBodyFromStream(
request,
request.headers.contentType != null
? new MediaType.parse(request.headers.contentType.toString())
? MediaType.parse(request.headers.contentType.toString())
: null,
request.uri,
storeOriginalBuffer: storeOriginalBuffer);

View file

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

View file

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

View file

@ -1,17 +1,17 @@
/// Represents a file uploaded to the server.
class FileUploadInfo {
/// The MIME type of the uploaded file.
String mimeType;
String? mimeType;
/// The name of the file field from the request.
String name;
String? name;
/// The filename of the file.
String filename;
String? filename;
/// The bytes that make up this file.
List<int> data;
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 {
num numValue = num.parse(value);
if (!numValue.isNaN)
var numValue = num.parse(value);
if (!numValue.isNaN) {
return numValue;
else
} else {
return value;
}
} on FormatException {
if (value.startsWith('[') && value.endsWith(']'))
if (value.startsWith('[') && value.endsWith(']')) {
return json.decode(value);
else if (value.startsWith('{') && value.endsWith('}'))
} else if (value.startsWith('{') && value.endsWith('}')) {
return json.decode(value);
else if (value.trim().toLowerCase() == 'null')
} else if (value.trim().toLowerCase() == 'null') {
return null;
else
} else {
return value;
}
}
}

View file

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

View file

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

View file

@ -1,15 +1,14 @@
name: body_parser
author: Tobe O <thosakwe@gmail.com>
version: 1.1.1
version: 2.0.0
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
environment:
sdk: ">=2.10.0 <2.12.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
dart2_constant: ^1.0.0
http_parser: ">=3.1.1 <4.0.0"
http_server: ">=0.9.6 <1.0.0"
mime: ">=0.9.3 <1.0.0"
http_parser: ^4.0.0
http_server: ^1.0.0
mime: ^1.0.0
dev_dependencies:
http: ">=0.11.3 <0.12.0"
test: ^1.15.7
http: ^0.13.0
test: ^1.17.0
pedantic: ^1.11.0

View file

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

View file

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

View file

@ -4,8 +4,9 @@ import 'package:angel_html/angel_html.dart';
import 'package:html_builder/elements.dart';
import 'package:logging/logging.dart';
main() async {
var app = Angel(), http = AngelHttp(app);
void main() async {
var app = Angel();
var http = AngelHttp(app);
app.logger = Logger('angel_html')
..onRecord.listen((rec) {
print(rec);
@ -29,7 +30,7 @@ main() async {
renderHtml(
enforceAcceptHeader: true,
renderer: StringRenderer(
doctype: null,
//doctype: null,
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.
///
/// Set [enforceAcceptHeader] to `true` to throw a `406 Not Acceptable` if the client doesn't accept HTML responses.
RequestHandler renderHtml({StringRenderer renderer, bool enforceAcceptHeader}) {
renderer ??= new StringRenderer(pretty: false, html5: true);
RequestHandler renderHtml({StringRenderer? renderer, bool? enforceAcceptHeader}) {
renderer ??= StringRenderer(pretty: false, html5: true);
return (RequestContext req, ResponseContext res) {
var oldSerializer = res.serializer;
res.serializer = (data) {
if (data is! Node)
return oldSerializer(data);
else {
if (enforceAcceptHeader == true && !req.accepts('text/html'))
throw new AngelHttpException.notAcceptable();
if (data is! Node) {
return oldSerializer!(data);
} else {
if (enforceAcceptHeader == true && !req.accepts('text/html')) {
throw AngelHttpException.notAcceptable();
}
var content = renderer.render(data as Node);
var content = renderer!.render(data);
res
..headers['content-type'] = 'text/html'
..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
version: 2.0.0
version: 3.0.0
description: Support for rendering html_builder AST's as responses in Angel.
author: Tobe O <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/html_builder
publish_to: none
environment:
sdk: ">=2.10.0 <2.12.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel_framework: #^2.0.0-alpha
path: ../framework
html_builder: ^1.0.0
angel_framework:
git:
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:
angel_test: #^2.0.0-alpha
path: ../test
html: ^0.13.2
logging: ^0.11.0
test: ^1.15.7
pedantic: ^1.0.0
angel_test:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/test
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:test/test.dart';
main() {
void main() {
Angel app;
TestClient client;
late TestClient client;
setUp(() async {
app = new Angel();
app = Angel();
app.fallback(renderHtml());
@ -27,8 +27,8 @@ main() {
chain([
renderHtml(
enforceAcceptHeader: true,
renderer: new StringRenderer(
doctype: null,
renderer: StringRenderer(
//doctype: null,
pretty: false,
),
),
@ -43,7 +43,7 @@ main() {
tearDown(() => client.close());
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}');
expect(
@ -56,12 +56,13 @@ main() {
group('enforce accept header', () {
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}');
expect(response,
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'});
print('Response: ${response.body}');
expect(response,
@ -69,12 +70,12 @@ main() {
});
test('throws if incorrect or no accept', () async {
var response = await client.get('/strict');
print('Response: ${response.body}');
var response = await client.get(Uri.parse('/strict'));
print('Response: ${response.statusCode} ${response.body}');
expect(response, hasStatus(406));
response = await client
.get('/strict', headers: {'accept': 'application/json,text/xml'});
response = await client.get(Uri.parse('/strict'),
headers: {'accept': 'application/json,text/xml'});
print('Response: ${response.body}');
expect(response,
isAngelHttpException(statusCode: 406, message: '406 Not Acceptable'));

View file

@ -1,3 +1,4 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
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/src/protocol/language_server/server.dart';
main(List<String> args) async {
var argParser = new ArgParser()
void main(List<String> args) async {
var argParser = ArgParser()
..addFlag('help',
abbr: 'h', negatable: false, help: 'Print this help information.')
..addOption('log-file', help: 'A path to which to write a log file.');
@ -25,14 +25,14 @@ main(List<String> args) async {
printUsage();
return;
} else {
var jaelServer = new JaelLanguageServer();
var jaelServer = JaelLanguageServer();
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);
jaelServer.logger.onRecord.listen((rec) async {
var sink = await f.openWrite(mode: FileMode.append);
var sink = f.openWrite(mode: FileMode.append);
sink.writeln(rec);
if (rec.error != null) sink.writeln(rec.error);
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) {
jaelServer.logger.severe('Uncaught', error, stackTrace);
},
@ -57,7 +57,7 @@ main(List<String> args) async {
);
var zone = Zone.current.fork(specification: spec);
await zone.run(() async {
var stdio = new StdIOLanguageServer.start(jaelServer);
var stdio = StdIOLanguageServer.start(jaelServer);
await stdio.onDone;
});
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,25 +1,36 @@
name: jael_language_server
version: 0.0.0
description: Language Server Protocol implementation for the Jael templating engine.
author: Tobe Osakwe <thosakwe@gmail.com>
homepage: https://github.com/angel-dart/vscode
publish_to: none
environment:
sdk: '>=2.10.0 <2.12.0'
sdk: '>=2.12.0 <3.0.0'
dependencies:
args: ^1.0.0
args: ^2.1.1
# dart_language_server: ^0.1.16
file: ^5.0.0
io: ^0.3.2
jael: #^2.0.0
path: ../jael
jael_preprocessor: #^2.0.0
path: ../jael_preprocessor
json_rpc_2: ^2.0.0
logging: ^0.11.3
path: ^1.0.0
source_span: ^1.0.0
string_scanner: ^1.0.0
symbol_table: ^2.0.0
file: ^6.1.2
io: ^1.0.0
jael:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
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
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:
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
of this software and associated documentation files (the "Software"), to deal

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,7 @@ abstract class 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);

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.
class Jael {
/// The raw template.
final String template;
final String? template;
/// The path to a [template].
final String templateUrl;
final String? templateUrl;
/// Whether to parse the [template] as `DSX`.
final bool asDsx;
final bool? asDsx;
const Jael({this.template, this.templateUrl, this.asDsx});
}
/// Shorthand for enabling `DSX` syntax when using a [Jael] annotation.
class Dsx extends Jael {
const Dsx({String template, String templateUrl})
const Dsx({String? template, String? templateUrl})
: super(template: template, templateUrl: templateUrl, asDsx: true);
}

View file

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

View file

@ -11,7 +11,7 @@ import 'package:angel_framework/http.dart';
import 'package:angel_jinja/angel_jinja.dart';
import 'package:path/path.dart' as p;
main() async {
void main() async {
var app = Angel();
var http = AngelHttp(app);
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:path/path.dart' as p;
main() async {
void main() async {
var app = Angel();
var http = AngelHttp(app);
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]
/// or [Environment].
AngelConfigurer jinja({
Iterable<String> ext = const ['html'],
Set<String> ext = const {'html'},
String path = 'lib/src/templates',
bool followLinks = true,
String stmtOpen = '{%',
String stmtClose = '%}',
String blockStart = '{%',
String blockEnd = '%}',
String varOpen = '{{',
String varClose = '}}',
String commentOpen = '{#',
String commentClose = '#}',
String commentStart = '{#',
String commentEnd = '#}',
defaultValue,
bool autoReload = true,
//bool autoReload = true,
Map<String, Function> filters = const <String, Function>{},
Map<String, Function> tests = const <String, Function>{},
Loader Function() createLoader,
Loader Function()? createLoader,
}) {
return (app) {
createLoader ??= () {
return FileSystemLoader(
ext: ext.toList(),
extensions: ext,
path: path,
followLinks: followLinks,
);
};
var env = Environment(
loader: createLoader(),
stmtOpen: stmtOpen,
stmtClose: stmtClose,
varOpen: varOpen,
varClose: varClose,
commentOpen: commentOpen,
commentClose: commentClose,
loader: createLoader!(),
blockStart: blockStart,
blockEnd: blockEnd,
variableStart: varOpen,
variableEnd: varClose,
commentStart: commentStart,
commentEnd: commentEnd,
//defaultValue: defaultValue,
autoReload: autoReload,
//autoReload: autoReload,
filters: filters,
tests: tests,
);
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
version: 2.0.0
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
author: Tobe O <thosakwe@gmail.com>
publish_to: none
environment:
sdk: '>=2.10.0 <2.12.0'
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel_framework: #^2.0.0-alpha
path: ../framework
jinja: ^0.0.4
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/framework
jinja: ^0.3.1
dev_dependencies:
angel_test: #^2.0.0
path: ../test
path: ^1.0.0
pedantic: ^1.0.0
test: ^1.15.7
angel_test:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
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
* Angel 2 + Dart 2 updates.
* Use `package:file`.

View file

@ -5,7 +5,7 @@ import 'package:angel_framework/http.dart';
import 'package:angel_markdown/angel_markdown.dart';
import 'package:file/local.dart';
main() async {
void main() async {
var app = await createServer();
var http = AngelHttp(app);
var server = await http.startServer(InternetAddress.loopbackIPv4, 3000);
@ -14,7 +14,7 @@ main() async {
Future<Angel> createServer() async {
// Create a new server, and install the Markdown renderer.
var app = new Angel();
var app = Angel();
var fs = LocalFileSystem();
await app
.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: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].
///
@ -18,7 +18,8 @@ AngelConfigurer markdown(
Directory viewsDirectory, {
String extension,
ExtensionSet extensionSet,
FutureOr<String> template(String content, Map<String, dynamic> locals),
FutureOr<String> Function(String content, Map<String, dynamic> locals)
template,
}) {
extension ??= '.md';
extensionSet ??= ExtensionSet.gitHubWeb;
@ -40,9 +41,10 @@ AngelConfigurer markdown(
var split = expr.split('.');
var root = split[0];
if (locals?.containsKey(root) != true)
throw new UnimplementedError(
if (locals?.containsKey(root) != true) {
throw UnimplementedError(
'Expected a local named "$root", but none was provided. Expression text: "$text"');
}
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;
InstanceMirror mirror = reflect(target);
var mirror = reflect(target);
for (int i = 1; i < split.length; i++) {
mirror = mirror.getField(new Symbol(split[i]));
for (var i = 1; i < split.length; i++) {
mirror = mirror.getField(Symbol(split[i]));
}
return mirror.reflectee;

View file

@ -1,17 +1,23 @@
name: angel_markdown
version: 2.0.0
version: 3.0.0
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
publish_to: none
environment:
sdk: ">=2.10.0 <2.12.0"
sdk: ">=2.10.0 <3.0.0"
dependencies:
angel_framework: #^2.0.0-alpha
path: ../framework
file: ^5.0.0
markdown: ^2.0.0
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/framework
file: ^6.1.2
markdown: ^4.0.0
dev_dependencies:
angel_test: #^2.0.0
path: ../test
pedantic: ^1.0.0
test: ^1.15.7
angel_test:
git:
url: https://github.com/dukefirehawk/angel.git
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
* Angel 2 and Dart 2 support.

View file

@ -1,2 +1,4 @@
include: package:pedantic/analysis_options.yaml
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/local.dart';
const FileSystem fs = const LocalFileSystem();
const FileSystem fs = LocalFileSystem();
configureServer(Angel app) async {
void configureServer(Angel app) async {
// Run the plug-in
await app.configure(mustache(fs.directory('views')));

View file

@ -1,32 +1,38 @@
library angel_mustache;
import 'dart:async';
import 'package:angel_framework/angel_framework.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 'src/cache.dart';
import 'src/mustache_context.dart';
mustache(Directory viewsDirectory,
{String fileExtension: '.mustache', String partialsPath: './partials'}) {
Directory partialsDirectory = viewsDirectory.fileSystem
Future Function(Angel app) mustache(Directory viewsDirectory,
{String fileExtension = '.mustache', String partialsPath = './partials'}) {
var partialsDirectory = viewsDirectory.fileSystem
.directory(p.join(p.fromUri(viewsDirectory.uri), partialsPath));
MustacheContext context =
new MustacheContext(viewsDirectory, partialsDirectory, fileExtension);
var context =
MustacheContext(viewsDirectory, partialsDirectory, fileExtension);
MustacheViewCache cache = new MustacheViewCache(context);
var cache = MustacheViewCache(context);
return (Angel app) async {
app.viewGenerator = (String name, [Map data]) async {
var partialsProvider;
partialsProvider = (String name) {
String template = cache.getPartialSync(name, app);
return render(template, data ?? {}, partial: partialsProvider);
app.viewGenerator = (String name, [Map? data]) async {
//var partialsProvider;
var partialsProvider = (String name) {
var template = cache.getPartialSync(name, app)!;
//return render(template, data ?? {}, partial: partialsProvider);
return viewer.Template(template, name: name);
};
String viewTemplate = await cache.getView(name, app);
return await render(viewTemplate, data ?? {}, partial: partialsProvider);
var viewTemplate = await (cache.getView(name, app));
//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';
class MustacheViewCache {
/**
* The context for which views and partials are
* served from.
*/
MustacheContext context;
/// The context for which views and partials are
/// served from.
MustacheContext? context;
HashMap<String, String> viewCache = new HashMap();
HashMap<String, String> partialCache = new HashMap();
HashMap<String, String> viewCache = HashMap();
HashMap<String, String> partialCache = HashMap();
MustacheViewCache([this.context]);
Future<String> getView(String viewName, Angel app) async {
if (app.isProduction) {
Future<String?> getView(String viewName, Angel app) async {
if (app.environment.isProduction) {
if (viewCache.containsKey(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()) {
String viewTemplate = await viewFile.readAsString();
if (app.isProduction) {
this.viewCache[viewName] = viewTemplate;
var viewTemplate = await viewFile.readAsString();
if (app.environment.isProduction) {
viewCache[viewName] = viewTemplate;
}
return viewTemplate;
} else
throw new FileSystemException(
} else {
throw FileSystemException(
'View "$viewName" was not found.', viewFile.path);
}
}
String getPartialSync(String partialName, Angel app) {
if (app.isProduction) {
String? getPartialSync(String partialName, Angel app) {
if (app.environment.isProduction) {
if (partialCache.containsKey(partialName)) {
return partialCache[partialName];
}
}
File partialFile = context.resolvePartial(partialName);
var partialFile = context!.resolvePartial(partialName);
if (partialFile.existsSync()) {
String partialTemplate = partialFile.readAsStringSync();
if (app.isProduction) {
this.partialCache[partialName] = partialTemplate;
var partialTemplate = partialFile.readAsStringSync();
if (app.environment.isProduction) {
partialCache[partialName] = partialTemplate;
}
return partialTemplate;
} else
throw new FileSystemException(
} else {
throw FileSystemException(
'View "$partialName" was not found.', partialFile.path);
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,11 +1,11 @@
import 'package:angel_paginate/angel_paginate.dart';
main() {
void main() {
var iterable = [1, 2, 3, 4];
var p = new Paginator(iterable);
var p = Paginator(iterable);
// Get the current page (default: page 1)
var page = p.current;
var page = p.current!;
print(page.total);
print(page.startIndex);
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.
class Paginator<T> {
final Map<int, PaginationResult<T>> _cache = {};
PaginationResult<T> _current;
PaginationResult<T>? _current;
int _page = 0;
/// 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.
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.
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.
///
/// If [useCache] is `true` (default), then computations will be cached even after the page changes.
PaginationResult<T> get current {
if (_current != null)
PaginationResult<T>? get current {
if (_current != null) {
return _current;
else
} else {
return _current = _getPage();
}
}
PaginationResult<T> _computePage() {
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;
it = it.take(itemsPerPage);
var last = _lastPage();
// print('cur: $_page, last: $last');
return new _PaginationResultImpl(it,
return _PaginationResultImpl(it,
currentPage: _page + 1,
previousPage: _page <= 0 ? -1 : _page,
nextPage: _page >= last - 1 ? -1 : _page + 2,
@ -63,10 +65,11 @@ class Paginator<T> {
}
PaginationResult<T> _getPage() {
if (useCache != false)
if (useCache != false) {
return _cache.putIfAbsent(_page, () => _computePage());
else
} else {
return _computePage();
}
}
int _lastPage() {
@ -110,40 +113,40 @@ class Paginator<T> {
/// Stores the result of a pagination.
abstract class PaginationResult<T> {
factory PaginationResult.fromMap(Map<String, dynamic> map) =>
new _PaginationResultImpl((map['data'] as Iterable).cast<T>(),
currentPage: map['current_page'],
endIndex: map['end_index'],
itemsPerPage: map['items_per_page'],
nextPage: map['next_page'],
previousPage: map['previous_page'],
startIndex: map['start_index'],
total: map['total']);
_PaginationResultImpl((map['data'] as List).cast<T>(),
currentPage: map['current_page'] as int?,
endIndex: map['end_index'] as int?,
itemsPerPage: map['items_per_page'] as int?,
nextPage: map['next_page'] as int?,
previousPage: map['previous_page'] as int?,
startIndex: map['start_index'] as int?,
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();
}
class _PaginationResultImpl<T> implements PaginationResult<T> {
final Iterable<T> _data;
Iterable<T> _cachedData;
Iterable<T>? _cachedData;
@override
final int currentPage;
final int? currentPage;
_PaginationResultImpl(this._data,
{this.currentPage,
@ -155,25 +158,25 @@ class _PaginationResultImpl<T> implements PaginationResult<T> {
this.total});
@override
List<T> get data => _cachedData ?? (_cachedData = new List<T>.from(_data));
Iterable<T> get data => _cachedData ?? (_cachedData = List<T>.from(_data));
@override
final int endIndex;
final int? endIndex;
@override
final int itemsPerPage;
final int? itemsPerPage;
@override
final int nextPage;
final int? nextPage;
@override
final int previousPage;
final int? previousPage;
@override
final int startIndex;
final int? startIndex;
@override
final int total;
final int? total;
@override
Map<String, dynamic> toJson() {

View file

@ -1,15 +1,22 @@
name: angel_paginate
version: 2.0.0
version: 3.0.0
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
publish_to: none
environment:
sdk: ">=2.10.0 <2.12.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel_framework: #^2.0.0-alpha
path: ../framework
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/framework
dev_dependencies:
angel_test: #^2.0.0
path: ../test
logging: ^0.11.0
test: ^1.15.7
angel_test:
git:
url: https://github.com/dukefirehawk/angel.git
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 'paginate_test.dart' as paginate;
main() {
void main() {
group('bounds', bounds.main);
group('paginate', paginate.main);
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,16 +1,23 @@
name: angel_poll
version: 1.0.0
version: 2.0.0
description: package:angel_client support for "realtime" interactions with Angel via long polling.
author: Tobe O <thosakwe@gmail.com>
publish_to: none
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
homepage: https://github.com/angel-dart/poll
dependencies:
angel_client: # ^1.0.0
path: ../client
async: ">=1.10.0 <3.0.0"
collection: ^1.14.12
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/client
async: ^2.7.0
collection: ^1.15.0
dev_dependencies:
angel_test: # ^1.1.0
path: ../test
test: ^1.15.7
angel_test:
git:
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_container/mirrors.dart';
import 'package:angel_poll/angel_poll.dart';
import 'package:angel_test/angel_test.dart';
import 'package:async/async.dart';
@ -6,13 +7,18 @@ import 'package:logging/logging.dart';
import 'package:test/test.dart';
void main() {
srv.Service store;
TestClient client;
PollingService pollingService;
late srv.Service store;
late TestClient client;
late PollingService pollingService;
var created;
late StreamQueue onCreated;
late StreamQueue onModified;
late StreamQueue onRemoved;
setUp(() async {
var app = new srv.Angel();
app.logger = new Logger.detached('angel_poll')
var app = srv.Angel(reflector: MirrorsReflector());
app.logger = Logger.detached('angel_poll')
..onRecord.listen((rec) {
print(rec);
if (rec.error != null) {
@ -23,43 +29,37 @@ void main() {
store = app.use(
'/api/todos',
new srv.MapService(
srv.MapService(
autoIdAndDateFields: false,
),
);
client = await connectTo(app);
pollingService = new PollingService(
pollingService = PollingService(
client.service('api/todos'),
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', () {
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 {
var indexed = await pollingService.index();
print(indexed);
@ -80,7 +80,7 @@ void main() {
var result = await onModified.next;
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 {
@ -91,7 +91,7 @@ void main() {
var result = await onModified.next;
print(result);
expect(result, new Map.from(created)..['text'] = 'eat');
expect(result, Map.from({'': created})..['text'] = 'eat');
});
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
* 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
of this software and associated documentation files (the "Software"), to deal

View file

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

View file

@ -1,17 +1,18 @@
import 'package:angel_redis/angel_redis.dart';
import 'package:resp_client/resp_client.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 client = new RespClient(connection);
var service = new RedisService(new RespCommands(client), prefix: 'example');
var client = RespClient(connection);
var service = RedisService(RespCommandsTier2(client), prefix: 'example');
// Create an object
await service.create({'id': 'a', 'hello': 'world'});
// Read it...
var read = await service.read('a');
var read = await (service.read('a'));
print(read['hello']);
// 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.
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.
///
/// Consider using this if you are using several different Redis collections
/// within a single application.
final String prefix;
final String? 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
Future<List<Map<String, dynamic>>> index(
[Map<String, dynamic> params]) async {
[Map<String, dynamic>? params]) async {
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;
if (keys.isEmpty) return [];
result = await respCommands.client.writeArrayOfBulk(['MGET']..addAll(keys));
return result.payload
.map<Map<String, dynamic>>(
(RespType s) => json.decode(s.payload) as Map<String, dynamic>)
.toList();
//result = await respCommands.client.writeArrayOfBulk(['MGET']..addAll(keys));
result = await respCommands.tier1.tier0.execute(['MGET', ...keys]);
if (result.isArray) {
return (result as List<RespType>)
.map<Map<String, dynamic>>((RespType s) =>
json.decode(s.payload as String) as Map<String, dynamic>)
.toList();
} else {
// TODO: To be reviewed for handling none array objects
return [json.decode(result.payload as String) as Map<String, dynamic>];
}
}
@override
Future<Map<String, dynamic>> read(String id,
[Map<String, dynamic> params]) async {
var value = await respCommands.get(_applyPrefix(id));
[Map<String, dynamic>? params]) async {
var value = await respCommands.get(_applyPrefix(id)!);
if (value == null) {
throw new AngelHttpException.notFound(
message: 'No record found for ID $id');
throw AngelHttpException.notFound(message: 'No record found for ID $id');
} else {
return json.decode(value);
return json.decode(value) as Map<String, dynamic>;
}
}
@override
Future<Map<String, dynamic>> create(Map<String, dynamic> data,
[Map<String, dynamic> params]) async {
String id;
if (data['id'] != null)
id = data['id'] as String;
else {
var keyVar = await respCommands.client
.writeArrayOfBulk(['INCR', _applyPrefix('angel_redis:id')]);
Future<Map<String, dynamic>> create(Map<String, dynamic>? data,
[Map<String, dynamic>? params]) async {
String? id;
if (data!['id'] != null) {
id = data['id'] as String?;
} else {
var keyVar = await respCommands.tier1.tier0
.execute(['INCR', _applyPrefix('angel_redis:id')]);
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;
}
@override
Future<Map<String, dynamic>> modify(String id, Map<String, dynamic> data,
[Map<String, dynamic> params]) async {
var input = await read(id);
input.addAll(data);
Future<Map<String, dynamic>> modify(String id, Map<String, dynamic>? data,
[Map<String, dynamic>? params]) async {
var input = await (read(id));
input.addAll(data!);
return await update(id, input, params);
}
@override
Future<Map<String, dynamic>> update(String id, Map<String, dynamic> data,
[Map<String, dynamic> params]) async {
data = new Map<String, dynamic>.from(data)..['id'] = id;
await respCommands.set(_applyPrefix(id), json.encode(data));
Future<Map<String, dynamic>> update(String id, Map<String, dynamic>? data,
[Map<String, dynamic>? params]) async {
data = Map<String, dynamic>.from(data!)..['id'] = id;
await respCommands.set(_applyPrefix(id)!, json.encode(data));
return data;
}
@override
Future<Map<String, dynamic>> remove(String id,
[Map<String, dynamic> params]) async {
var client = respCommands.client;
await client.writeArrayOfBulk(['MULTI']);
await client.writeArrayOfBulk(['GET', _applyPrefix(id)]);
await client.writeArrayOfBulk(['DEL', _applyPrefix(id)]);
var result = await client.writeArrayOfBulk(['EXEC']);
[Map<String, dynamic>? params]) async {
var client = respCommands.tier1.tier0;
await client.execute(['MULTI']);
await client.execute(['GET', _applyPrefix(id)]);
await client.execute(['DEL', _applyPrefix(id)]);
var result = await client.execute(['EXEC']);
var str = result.payload[0] as RespBulkString;
if (str.payload == null)
throw new AngelHttpException.notFound(
message: 'No record found for ID $id');
else
return json.decode(str.payload);
if (str.payload == null) {
throw AngelHttpException.notFound(message: 'No record found for ID $id');
} else {
return json.decode(str.payload!) as Map<String, dynamic>;
}
}
}

View file

@ -1,15 +1,22 @@
name: angel_redis
version: 1.0.0
version: 2.0.0
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
publish_to: none
environment:
sdk: ">=2.10.0 <2.12.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
angel_framework: #^2.0.0-alpha
path: ../framework
angel_http_exception: #^1.0.0
path: ../http_exception
resp_client: ^0.1.6
angel_framework:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
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:
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:resp_client/resp_client.dart';
import 'package:resp_client/resp_commands.dart';
import 'package:resp_client/resp_server.dart';
import 'package:test/test.dart';
main() async {
RespServerConnection connection;
RedisService service;
void main() async {
late RespServerConnection connection;
late RedisService service;
setUp(() async {
connection = await connectSocket('localhost');
service = RedisService(RespCommands(RespClient(connection)),
service = RedisService(RespCommandsTier2(RespClient(connection)),
prefix: 'angel_redis_test');
});
@ -36,7 +37,7 @@ main() async {
test('create without id', () async {
var input = {'bar': 'baz'};
var output = await service.create(input);
var output = await (service.create(input));
print(output);
expect(output.keys, contains('id'));
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

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

View file

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

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