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

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

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

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

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

@ -12,51 +12,44 @@ import 'no_service.dart';
/// * [localKey]: `userId`
/// * [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);
}
};
}

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