Merged from sdk-2.12.x_nnbd
This commit is contained in:
parent
3adf9d62bc
commit
62f2235a6d
228 changed files with 3849 additions and 3305 deletions
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
12
packages/body_parser/AUTHORS.md
Normal file
12
packages/body_parser/AUTHORS.md
Normal 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.
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 []});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>>(
|
||||
|
|
|
@ -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
|
|
@ -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'];
|
||||
|
|
|
@ -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
12
packages/html/AUTHORS.md
Normal 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.
|
|
@ -1,2 +1,5 @@
|
|||
# 3.0.0
|
||||
* Migrated to support Dart SDK 2.12.x NNBD
|
||||
|
||||
# 2.0.0
|
||||
* Angel 2 + Dart 2 updates.
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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'));
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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) {}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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?;
|
||||
|
|
|
@ -33,7 +33,7 @@ class _Parser {
|
|||
bool _headerMode = true;
|
||||
int _contentLength = -1;
|
||||
|
||||
StreamSubscription _subscription;
|
||||
late StreamSubscription _subscription;
|
||||
|
||||
_Parser(Stream<List<int>> stream) {
|
||||
_subscription =
|
||||
|
|
|
@ -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?);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}, [])
|
||||
]);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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}) =>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import 'dom_node.dart';
|
||||
|
||||
abstract class Component<State> extends DomNode {
|
||||
State state;
|
||||
late State state;
|
||||
|
||||
DomNode render();
|
||||
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
12
packages/jinja/AUTHORS.md
Normal 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.
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
12
packages/markdown/AUTHORS.md
Normal file
12
packages/markdown/AUTHORS.md
Normal 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.
|
|
@ -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`.
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
12
packages/mustache/AUTHORS.md
Normal file
12
packages/mustache/AUTHORS.md
Normal 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.
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode: true
|
||||
strong-mode:
|
||||
implicit-casts: false
|
|
@ -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')));
|
||||
|
||||
|
|
|
@ -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 ?? {});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!'));
|
||||
});
|
||||
}
|
||||
|
|
12
packages/paginate/AUTHORS.md
Normal file
12
packages/paginate/AUTHORS.md
Normal 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.
|
|
@ -1,2 +1,5 @@
|
|||
# 3.0.0
|
||||
* Migrated to support Dart SDK 2.12.x NNBD
|
||||
|
||||
# 2.0.0
|
||||
* Dart2 + Angel2 update.
|
|
@ -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.
|
||||
|
|
4
packages/paginate/analysis_options.yaml
Normal file
4
packages/paginate/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()}');
|
||||
|
||||
|
|
|
@ -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
12
packages/poll/AUTHORS.md
Normal 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.
|
|
@ -1,2 +1,5 @@
|
|||
# 2.0.0
|
||||
* Migrated to support Dart SDK 2.12.x NNBD
|
||||
|
||||
# 1.0.0
|
||||
* Created package + tests
|
|
@ -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.
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode: true
|
||||
strong-mode:
|
||||
implicit-casts: false
|
|
@ -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'),
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
12
packages/redis/AUTHORS.md
Normal 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.
|
|
@ -1,2 +1,5 @@
|
|||
# 2.0.0
|
||||
* Migrated to support Dart SDK 2.12.x NNBD
|
||||
|
||||
# 1.0.0
|
||||
* First version.
|
|
@ -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.
|
||||
|
|
4
packages/redis/analysis_options.yaml
Normal file
4
packages/redis/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
|
@ -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.
|
||||
|
|
|
@ -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>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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'));
|
||||
|
|
12
packages/relations/AUTHORS.md
Normal file
12
packages/relations/AUTHORS.md
Normal 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.
|
5
packages/relations/CHANGELOG.md
Normal file
5
packages/relations/CHANGELOG.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# 2.0.0
|
||||
* Migrated to support Dart SDK 2.12.x NNBD
|
||||
|
||||
# 1.0.0
|
||||
* Initial checkin
|
|
@ -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.
|
||||
|
|
4
packages/relations/analysis_options.yaml
Normal file
4
packages/relations/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
|
@ -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
Loading…
Reference in a new issue