Updated Angel Route to Null Safety
This commit is contained in:
parent
d62a4aa7ac
commit
5d722a6041
16 changed files with 174 additions and 193 deletions
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/route.iml" filepath="$PROJECT_DIR$/route.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -1,8 +0,0 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="tests in route" type="DartTestRunConfigurationType" factoryName="Dart Test" singleton="true" nameIsGenerated="true">
|
||||
<option name="filePath" value="$PROJECT_DIR$" />
|
||||
<option name="scope" value="FOLDER" />
|
||||
<option name="testRunnerOptions" value="-j4" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -12,7 +12,7 @@ main() {
|
|||
router.get('/ordinal/int:n([0-9]+)st', () {});
|
||||
|
||||
print(router.resolveAbsolute('/whois/~thosakwe').first.allParams);
|
||||
print(router.resolveAbsolute('/wild_thornberrys').first.route.path);
|
||||
print(router.resolveAbsolute('/wild_thornberrys').first.route!.path);
|
||||
print(router.resolveAbsolute('/ordinal/1st').first.allParams);
|
||||
|
||||
router.get('/users', () {});
|
||||
|
|
|
@ -11,10 +11,10 @@ final RegExp _straySlashes = RegExp(r'(^/+)|(/+$)');
|
|||
/// A variation of the [Router] support both hash routing and push state.
|
||||
abstract class BrowserRouter<T> extends Router<T> {
|
||||
/// Fires whenever the active route changes. Fires `null` if none is selected (404).
|
||||
Stream<RoutingResult<T>> get onResolve;
|
||||
Stream<RoutingResult<T>?> get onResolve;
|
||||
|
||||
/// Fires whenever the active route changes. Fires `null` if none is selected (404).
|
||||
Stream<Route<T>> get onRoute;
|
||||
Stream<Route<T>?> get onRoute;
|
||||
|
||||
/// Set `hash` to true to use hash routing instead of push state.
|
||||
/// `listen` as `true` will call `listen` after initialization.
|
||||
|
@ -26,7 +26,7 @@ abstract class BrowserRouter<T> extends Router<T> {
|
|||
|
||||
BrowserRouter._() : super();
|
||||
|
||||
void _goTo(String path);
|
||||
void _goTo(String? path);
|
||||
|
||||
/// Navigates to the path generated by calling
|
||||
/// [navigate] with the given [linkParams].
|
||||
|
@ -41,26 +41,26 @@ abstract class BrowserRouter<T> extends Router<T> {
|
|||
void listen();
|
||||
|
||||
/// Identical to [all].
|
||||
Route on(String path, T handler, {Iterable<T> middleware});
|
||||
Route on(String path, T handler, {Iterable<T>? middleware});
|
||||
}
|
||||
|
||||
abstract class _BrowserRouterImpl<T> extends Router<T>
|
||||
implements BrowserRouter<T> {
|
||||
bool _listening = false;
|
||||
Route _current;
|
||||
StreamController<RoutingResult<T>> _onResolve =
|
||||
StreamController<RoutingResult<T>>();
|
||||
StreamController<Route<T>> _onRoute = StreamController<Route<T>>();
|
||||
Route? _current;
|
||||
StreamController<RoutingResult<T>?> _onResolve =
|
||||
StreamController<RoutingResult<T>?>();
|
||||
StreamController<Route<T>?> _onRoute = StreamController<Route<T>?>();
|
||||
|
||||
Route get currentRoute => _current;
|
||||
Route? get currentRoute => _current;
|
||||
|
||||
@override
|
||||
Stream<RoutingResult<T>> get onResolve => _onResolve.stream;
|
||||
Stream<RoutingResult<T>?> get onResolve => _onResolve.stream;
|
||||
|
||||
@override
|
||||
Stream<Route<T>> get onRoute => _onRoute.stream;
|
||||
Stream<Route<T>?> get onRoute => _onRoute.stream;
|
||||
|
||||
_BrowserRouterImpl({bool listen}) : super() {
|
||||
_BrowserRouterImpl({bool? listen}) : super() {
|
||||
if (listen != false) this.listen();
|
||||
prepareAnchors();
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ abstract class _BrowserRouterImpl<T> extends Router<T>
|
|||
@override
|
||||
void go(Iterable linkParams) => _goTo(navigate(linkParams));
|
||||
|
||||
Route on(String path, T handler, {Iterable<T> middleware}) =>
|
||||
Route on(String path, T handler, {Iterable<T>? middleware}) =>
|
||||
all(path, handler, middleware: middleware);
|
||||
|
||||
void prepareAnchors() {
|
||||
|
@ -105,18 +105,18 @@ abstract class _BrowserRouterImpl<T> extends Router<T>
|
|||
}
|
||||
|
||||
class _HashRouter<T> extends _BrowserRouterImpl<T> {
|
||||
_HashRouter({bool listen}) : super(listen: listen) {
|
||||
_HashRouter({required bool listen}) : super(listen: listen) {
|
||||
if (listen) this.listen();
|
||||
}
|
||||
|
||||
@override
|
||||
void _goTo(String uri) {
|
||||
void _goTo(String? uri) {
|
||||
window.location.hash = '#$uri';
|
||||
}
|
||||
|
||||
void handleHash([_]) {
|
||||
final path = window.location.hash.replaceAll(_hash, '');
|
||||
var allResolved = resolveAbsolute(path);
|
||||
Iterable<RoutingResult<T>> allResolved = resolveAbsolute(path);
|
||||
|
||||
final resolved = allResolved.isEmpty ? null : allResolved.first;
|
||||
|
||||
|
@ -130,7 +130,7 @@ class _HashRouter<T> extends _BrowserRouterImpl<T> {
|
|||
}
|
||||
|
||||
void handlePath(String path) {
|
||||
final resolved = resolveAbsolute(path).first;
|
||||
final RoutingResult<T> resolved = resolveAbsolute(path).first;
|
||||
|
||||
if (resolved == null) {
|
||||
_onResolve.add(null);
|
||||
|
@ -149,9 +149,9 @@ class _HashRouter<T> extends _BrowserRouterImpl<T> {
|
|||
}
|
||||
|
||||
class _PushStateRouter<T> extends _BrowserRouterImpl<T> {
|
||||
String _basePath;
|
||||
String? _basePath;
|
||||
|
||||
_PushStateRouter({bool listen, Route root}) : super(listen: listen) {
|
||||
_PushStateRouter({required bool listen, Route? root}) : super(listen: listen) {
|
||||
var $base = window.document.querySelector('base[href]') as BaseElement;
|
||||
|
||||
if ($base?.href?.isNotEmpty != true) {
|
||||
|
@ -163,19 +163,19 @@ class _PushStateRouter<T> extends _BrowserRouterImpl<T> {
|
|||
}
|
||||
|
||||
@override
|
||||
void _goTo(String uri) {
|
||||
final resolved = resolveAbsolute(uri).first;
|
||||
void _goTo(String? uri) {
|
||||
final RoutingResult<T> resolved = resolveAbsolute(uri).first;
|
||||
var relativeUri = uri;
|
||||
|
||||
if (_basePath?.isNotEmpty == true) {
|
||||
relativeUri = p.join(_basePath, uri.replaceAll(_straySlashes, ''));
|
||||
relativeUri = p.join(_basePath!, uri!.replaceAll(_straySlashes, ''));
|
||||
}
|
||||
|
||||
if (resolved == null) {
|
||||
_onResolve.add(null);
|
||||
_onRoute.add(_current = null);
|
||||
} else {
|
||||
final route = resolved.route;
|
||||
final route = resolved.route!;
|
||||
window.history.pushState({'path': route.path, 'params': {}},
|
||||
route.name ?? route.path, relativeUri);
|
||||
_onResolve.add(resolved);
|
||||
|
@ -186,7 +186,7 @@ class _PushStateRouter<T> extends _BrowserRouterImpl<T> {
|
|||
void handleState(state) {
|
||||
if (state is Map && state.containsKey('path')) {
|
||||
var path = state['path'].toString();
|
||||
final resolved = resolveAbsolute(path).first;
|
||||
final RoutingResult<T> resolved = resolveAbsolute(path).first;
|
||||
|
||||
if (resolved != null && resolved.route != _current) {
|
||||
//properties.addAll(state['properties'] ?? {});
|
||||
|
|
|
@ -4,14 +4,14 @@ class RouteGrammar {
|
|||
static const String notSlashRgx = r'([^/]+)';
|
||||
//static final RegExp rgx = RegExp(r'\((.+)\)');
|
||||
static final Parser<String> notSlash =
|
||||
match<String>(RegExp(notSlashRgx)).value((r) => r.span.text);
|
||||
match<String>(RegExp(notSlashRgx)).value((r) => r.span!.text);
|
||||
|
||||
static final Parser<Match> regExp =
|
||||
match<Match>(RegExp(r'\(([^\n)]+)\)([^/]+)?'))
|
||||
static final Parser<Match?> regExp =
|
||||
match<Match?>(RegExp(r'\(([^\n)]+)\)([^/]+)?'))
|
||||
.value((r) => r.scanner.lastMatch);
|
||||
|
||||
static final Parser<Match> parameterName =
|
||||
match<Match>(RegExp('$notSlashRgx?' r':([A-Za-z0-9_]+)' r'([^(/\n])?'))
|
||||
static final Parser<Match?> parameterName =
|
||||
match<Match?>(RegExp('$notSlashRgx?' r':([A-Za-z0-9_]+)' r'([^(/\n])?'))
|
||||
.value((r) => r.scanner.lastMatch);
|
||||
|
||||
static final Parser<ParameterSegment> parameterSegment = chain([
|
||||
|
@ -19,12 +19,12 @@ class RouteGrammar {
|
|||
match<bool>('?').value((r) => true).opt(),
|
||||
regExp.opt(),
|
||||
]).map((r) {
|
||||
var match = r.value[0] as Match;
|
||||
var rgxMatch = r.value[2] as Match;
|
||||
var match = r.value![0] as Match;
|
||||
var rgxMatch = r.value![2] as Match?;
|
||||
|
||||
var pre = match[1] ?? '';
|
||||
var post = match[3] ?? '';
|
||||
RegExp rgx;
|
||||
RegExp? rgx;
|
||||
|
||||
if (rgxMatch != null) {
|
||||
rgx = RegExp('(${rgxMatch[1]})');
|
||||
|
@ -41,23 +41,23 @@ class RouteGrammar {
|
|||
}
|
||||
|
||||
var s = ParameterSegment(match[2], rgx);
|
||||
return r.value[1] == true ? OptionalSegment(s) : s;
|
||||
return r.value![1] == true ? OptionalSegment(s) : s;
|
||||
});
|
||||
|
||||
static final Parser<ParsedParameterSegment> parsedParameterSegment = chain([
|
||||
match(RegExp(r'(int|num|double)'),
|
||||
errorMessage: 'Expected "int","double", or "num".')
|
||||
.map((r) => r.span.text),
|
||||
.map((r) => r.span!.text),
|
||||
parameterSegment,
|
||||
]).map((r) {
|
||||
return ParsedParameterSegment(
|
||||
r.value[0] as String, r.value[1] as ParameterSegment);
|
||||
r.value![0] as String, r.value![1] as ParameterSegment);
|
||||
});
|
||||
|
||||
static final Parser<WildcardSegment> wildcardSegment =
|
||||
match<WildcardSegment>(RegExp('$notSlashRgx?' r'\*' '$notSlashRgx?'))
|
||||
.value((r) {
|
||||
var m = r.scanner.lastMatch;
|
||||
var m = r.scanner.lastMatch!;
|
||||
var pre = m[1] ?? '';
|
||||
var post = m[2] ?? '';
|
||||
return WildcardSegment(pre, post);
|
||||
|
@ -95,8 +95,8 @@ class RouteDefinition {
|
|||
|
||||
RouteDefinition(this.segments);
|
||||
|
||||
Parser<RouteResult> compile() {
|
||||
Parser<RouteResult> out;
|
||||
Parser<RouteResult?>? compile() {
|
||||
Parser<RouteResult?>? out;
|
||||
|
||||
for (int i = 0; i < segments.length; i++) {
|
||||
var s = segments[i];
|
||||
|
@ -116,7 +116,7 @@ class RouteDefinition {
|
|||
abstract class RouteSegment {
|
||||
Parser<RouteResult> compile(bool isLast);
|
||||
|
||||
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast);
|
||||
Parser<RouteResult?> compileNext(Parser<RouteResult> p, bool isLast);
|
||||
}
|
||||
|
||||
class SlashSegment implements RouteSegment {
|
||||
|
@ -139,7 +139,7 @@ class SlashSegment implements RouteSegment {
|
|||
}
|
||||
|
||||
class ConstantSegment extends RouteSegment {
|
||||
final String text;
|
||||
final String? text;
|
||||
|
||||
ConstantSegment(this.text);
|
||||
|
||||
|
@ -150,7 +150,7 @@ class ConstantSegment extends RouteSegment {
|
|||
|
||||
@override
|
||||
Parser<RouteResult> compile(bool isLast) {
|
||||
return match(text).map((r) => RouteResult({}));
|
||||
return match(text!).map((r) => RouteResult({}));
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -183,13 +183,13 @@ class WildcardSegment extends RouteSegment {
|
|||
@override
|
||||
Parser<RouteResult> compile(bool isLast) {
|
||||
return match(_compile(isLast))
|
||||
.map((r) => RouteResult({}, tail: r.scanner.lastMatch[1]));
|
||||
.map((r) => RouteResult({}, tail: r.scanner.lastMatch![1]));
|
||||
}
|
||||
|
||||
@override
|
||||
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
|
||||
return p.then(compile(isLast)).map((r) {
|
||||
var items = r.value.cast<RouteResult>();
|
||||
var items = r.value!.cast<RouteResult>();
|
||||
var a = items[0], b = items[1];
|
||||
return a
|
||||
..addAll(b?.params ?? {})
|
||||
|
@ -214,44 +214,44 @@ class OptionalSegment extends ParameterSegment {
|
|||
}
|
||||
|
||||
@override
|
||||
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
|
||||
Parser<RouteResult?> compileNext(Parser<RouteResult> p, bool isLast) {
|
||||
return p.then(_compile().opt()).map((r) {
|
||||
if (r.value[1] == null) return r.value[0] as RouteResult;
|
||||
return (r.value[0] as RouteResult)
|
||||
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
|
||||
if (r.value![1] == null) return r.value![0] as RouteResult?;
|
||||
return (r.value![0] as RouteResult)
|
||||
..addAll({name: Uri.decodeComponent(r.value![1] as String)});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ParameterSegment extends RouteSegment {
|
||||
final String name;
|
||||
final RegExp regExp;
|
||||
final String? name;
|
||||
final RegExp? regExp;
|
||||
|
||||
ParameterSegment(this.name, this.regExp);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (regExp != null) return 'Param: $name (${regExp.pattern})';
|
||||
if (regExp != null) return 'Param: $name (${regExp!.pattern})';
|
||||
return 'Param: $name';
|
||||
}
|
||||
|
||||
Parser<String> _compile() {
|
||||
Parser<String?> _compile() {
|
||||
return regExp != null
|
||||
? match<String>(regExp).value((r) => r.scanner.lastMatch[1])
|
||||
? match<String?>(regExp!).value((r) => r.scanner.lastMatch![1])
|
||||
: RouteGrammar.notSlash;
|
||||
}
|
||||
|
||||
@override
|
||||
Parser<RouteResult> compile(bool isLast) {
|
||||
return _compile()
|
||||
.map((r) => RouteResult({name: Uri.decodeComponent(r.value)}));
|
||||
.map((r) => RouteResult({name: Uri.decodeComponent(r.value!)}));
|
||||
}
|
||||
|
||||
@override
|
||||
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
|
||||
Parser<RouteResult?> compileNext(Parser<RouteResult> p, bool isLast) {
|
||||
return p.then(_compile()).map((r) {
|
||||
return (r.value[0] as RouteResult)
|
||||
..addAll({name: Uri.decodeComponent(r.value[1] as String)});
|
||||
return (r.value![0] as RouteResult)
|
||||
..addAll({name: Uri.decodeComponent(r.value![1] as String)});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -276,15 +276,15 @@ class ParsedParameterSegment extends RouteSegment {
|
|||
@override
|
||||
Parser<RouteResult> compile(bool isLast) {
|
||||
return parameter._compile().map((r) => RouteResult(
|
||||
{parameter.name: getValue(Uri.decodeComponent(r.span.text))}));
|
||||
{parameter.name: getValue(Uri.decodeComponent(r.span!.text))}));
|
||||
}
|
||||
|
||||
@override
|
||||
Parser<RouteResult> compileNext(Parser<RouteResult> p, bool isLast) {
|
||||
return p.then(parameter._compile()).map((r) {
|
||||
return (r.value[0] as RouteResult)
|
||||
return (r.value![0] as RouteResult)
|
||||
..addAll({
|
||||
parameter.name: getValue(Uri.decodeComponent(r.value[1] as String))
|
||||
parameter.name: getValue(Uri.decodeComponent(r.value![1] as String))
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ import 'router.dart';
|
|||
class MiddlewarePipeline<T> {
|
||||
/// All the possible routes that matched the given path.
|
||||
final Iterable<RoutingResult<T>> routingResults;
|
||||
List<T> _handlers;
|
||||
List<T>? _handlers;
|
||||
|
||||
/// An ordered list of every handler delegated to handle this request.
|
||||
List<T> get handlers {
|
||||
List<T>? get handlers {
|
||||
if (_handlers != null) return _handlers;
|
||||
final handlers = <T>[];
|
||||
|
||||
|
|
|
@ -2,17 +2,17 @@ part of angel_route.src.router;
|
|||
|
||||
/// Represents a virtual location within an application.
|
||||
class Route<T> {
|
||||
final String method;
|
||||
final String? method;
|
||||
final String path;
|
||||
final List<T> handlers;
|
||||
final List<T>? handlers;
|
||||
final Map<String, Map<String, dynamic>> _cache = {};
|
||||
final RouteDefinition _routeDefinition;
|
||||
String name;
|
||||
Parser<RouteResult> _parser;
|
||||
final RouteDefinition? _routeDefinition;
|
||||
String? name;
|
||||
Parser<RouteResult?>? _parser;
|
||||
|
||||
Route(this.path, {@required this.method, @required this.handlers})
|
||||
Route(this.path, {required this.method, required this.handlers})
|
||||
: _routeDefinition = RouteGrammar.routeDefinition
|
||||
.parse(SpanScanner(path.replaceAll(_straySlashes, '')))
|
||||
.parse(SpanScanner(path.replaceAll(_straySlashes, '')))!
|
||||
.value {
|
||||
if (_routeDefinition?.segments?.isNotEmpty != true) {
|
||||
_parser = match('').map((r) => RouteResult({}));
|
||||
|
@ -26,7 +26,7 @@ class Route<T> {
|
|||
method: b.method, handlers: b.handlers);
|
||||
}
|
||||
|
||||
Parser<RouteResult> get parser => _parser ??= _routeDefinition.compile();
|
||||
Parser<RouteResult?>? get parser => _parser ??= _routeDefinition!.compile();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
|
@ -42,7 +42,7 @@ class Route<T> {
|
|||
var b = StringBuffer();
|
||||
int i = 0;
|
||||
|
||||
for (var seg in _routeDefinition.segments) {
|
||||
for (var seg in _routeDefinition!.segments) {
|
||||
if (i++ > 0) b.write('/');
|
||||
if (seg is ConstantSegment) {
|
||||
b.write(seg.text);
|
||||
|
@ -50,7 +50,7 @@ class Route<T> {
|
|||
if (!params.containsKey(seg.name)) {
|
||||
throw ArgumentError('Missing parameter "${seg.name}".');
|
||||
}
|
||||
b.write(params[seg.name]);
|
||||
b.write(params[seg.name!]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,19 +61,19 @@ class Route<T> {
|
|||
/// The result of matching an individual route.
|
||||
class RouteResult {
|
||||
/// The parsed route parameters.
|
||||
final Map<String, dynamic> params;
|
||||
final Map<String?, dynamic> params;
|
||||
|
||||
/// Optional. An explicit "tail" value to set.
|
||||
String get tail => _tail;
|
||||
String? get tail => _tail;
|
||||
|
||||
String _tail;
|
||||
String? _tail;
|
||||
|
||||
RouteResult(this.params, {String tail}) : _tail = tail;
|
||||
RouteResult(this.params, {String? tail}) : _tail = tail;
|
||||
|
||||
void _setTail(String v) => _tail ??= v;
|
||||
void _setTail(String? v) => _tail ??= v;
|
||||
|
||||
/// Adds parameters.
|
||||
void addAll(Map<String, dynamic> map) {
|
||||
void addAll(Map<String?, dynamic> map) {
|
||||
params.addAll(map);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,8 +65,8 @@ class Router<T> {
|
|||
/// Adds a route that responds to the given path
|
||||
/// for requests with the given method (case-insensitive).
|
||||
/// Provide '*' as the method to respond to all methods.
|
||||
Route<T> addRoute(String method, String path, T handler,
|
||||
{Iterable<T> middleware}) {
|
||||
Route<T> addRoute(String? method, String path, T handler,
|
||||
{Iterable<T>? middleware}) {
|
||||
middleware ??= <T>[];
|
||||
if (_useCache == true) {
|
||||
throw StateError('Cannot add routes after caching is enabled.');
|
||||
|
@ -115,7 +115,7 @@ class Router<T> {
|
|||
/// Creates a visual representation of the route hierarchy and
|
||||
/// passes it to a callback. If none is provided, `print` is called.
|
||||
void dumpTree(
|
||||
{callback(String tree),
|
||||
{callback(String tree)?,
|
||||
String header = 'Dumping route tree:',
|
||||
String tab = ' '}) {
|
||||
final buf = StringBuffer();
|
||||
|
@ -147,7 +147,7 @@ class Router<T> {
|
|||
buf.writeln();
|
||||
dumpRouter(route.router);
|
||||
} else {
|
||||
buf.writeln(' => ${route.handlers.length} handler(s)');
|
||||
buf.writeln(' => ${route.handlers!.length} handler(s)');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,7 @@ class Router<T> {
|
|||
/// Returns the created route.
|
||||
/// You can also register middleware within the router.
|
||||
SymlinkRoute<T> group(String path, void callback(Router<T> router),
|
||||
{Iterable<T> middleware, String name}) {
|
||||
{Iterable<T>? middleware, String? name}) {
|
||||
middleware ??= <T>[];
|
||||
final router = Router<T>().._middleware.addAll(middleware);
|
||||
callback(router);
|
||||
|
@ -175,7 +175,7 @@ class Router<T> {
|
|||
/// Asynchronous equivalent of [group].
|
||||
Future<SymlinkRoute<T>> groupAsync(
|
||||
String path, FutureOr<void> callback(Router<T> router),
|
||||
{Iterable<T> middleware, String name}) async {
|
||||
{Iterable<T>? middleware, String? name}) async {
|
||||
middleware ??= <T>[];
|
||||
final router = Router<T>().._middleware.addAll(middleware);
|
||||
await callback(router);
|
||||
|
@ -212,7 +212,7 @@ class Router<T> {
|
|||
String navigate(Iterable linkParams, {bool absolute = true}) {
|
||||
final List<String> segments = [];
|
||||
Router search = this;
|
||||
Route lastRoute;
|
||||
Route? lastRoute;
|
||||
|
||||
for (final param in linkParams) {
|
||||
bool resolved = false;
|
||||
|
@ -238,7 +238,7 @@ class Router<T> {
|
|||
var scanner = SpanScanner(param.replaceAll(_straySlashes, ''));
|
||||
for (Route route in search.routes) {
|
||||
int pos = scanner.position;
|
||||
if (route.parser.parse(scanner).successful && scanner.isDone) {
|
||||
if (route.parser!.parse(scanner)!.successful && scanner.isDone) {
|
||||
segments.add(route.path.replaceAll(_straySlashes, ''));
|
||||
lastRoute = route;
|
||||
|
||||
|
@ -281,10 +281,10 @@ class Router<T> {
|
|||
|
||||
/// Finds the first [Route] that matches the given path,
|
||||
/// with the given method.
|
||||
bool resolve(String absolute, String relative, List<RoutingResult<T>> out,
|
||||
bool resolve(String? absolute, String? relative, List<RoutingResult<T>> out,
|
||||
{String method = 'GET', bool strip = true}) {
|
||||
final cleanRelative =
|
||||
strip == false ? relative : stripStraySlashes(relative);
|
||||
strip == false ? relative! : stripStraySlashes(relative!);
|
||||
var scanner = SpanScanner(cleanRelative);
|
||||
|
||||
bool crawl(Router<T> r) {
|
||||
|
@ -294,22 +294,22 @@ class Router<T> {
|
|||
int pos = scanner.position;
|
||||
|
||||
if (route is SymlinkRoute<T>) {
|
||||
if (route.parser.parse(scanner).successful) {
|
||||
if (route.parser!.parse(scanner)!.successful) {
|
||||
var s = crawl(route.router);
|
||||
if (s) success = true;
|
||||
}
|
||||
|
||||
scanner.position = pos;
|
||||
} else if (route.method == '*' || route.method == method) {
|
||||
var parseResult = route.parser.parse(scanner);
|
||||
var parseResult = route.parser!.parse(scanner)!;
|
||||
|
||||
if (parseResult.successful && scanner.isDone) {
|
||||
var result = RoutingResult<T>(
|
||||
parseResult: parseResult,
|
||||
params: parseResult.value.params,
|
||||
params: parseResult.value!.params,
|
||||
shallowRoute: route,
|
||||
shallowRouter: this,
|
||||
tail: (parseResult.value.tail ?? '') + scanner.rest);
|
||||
tail: (parseResult.value!.tail ?? '') + scanner.rest);
|
||||
out.add(result);
|
||||
success = true;
|
||||
}
|
||||
|
@ -326,13 +326,13 @@ class Router<T> {
|
|||
|
||||
/// Returns the result of [resolve] with [path] passed as
|
||||
/// both `absolute` and `relative`.
|
||||
Iterable<RoutingResult<T>> resolveAbsolute(String path,
|
||||
Iterable<RoutingResult<T>> resolveAbsolute(String? path,
|
||||
{String method = 'GET', bool strip = true}) =>
|
||||
resolveAll(path, path, method: method, strip: strip);
|
||||
|
||||
/// Finds every possible [Route] that matches the given path,
|
||||
/// with the given method.
|
||||
Iterable<RoutingResult<T>> resolveAll(String absolute, String relative,
|
||||
Iterable<RoutingResult<T>> resolveAll(String? absolute, String? relative,
|
||||
{String method = 'GET', bool strip = true}) {
|
||||
if (_useCache == true) {
|
||||
return _cache.putIfAbsent('$method$absolute',
|
||||
|
@ -342,7 +342,7 @@ class Router<T> {
|
|||
return _resolveAll(absolute, relative, method: method, strip: strip);
|
||||
}
|
||||
|
||||
Iterable<RoutingResult<T>> _resolveAll(String absolute, String relative,
|
||||
Iterable<RoutingResult<T>> _resolveAll(String? absolute, String? relative,
|
||||
{String method = 'GET', bool strip = true}) {
|
||||
var results = <RoutingResult<T>>[];
|
||||
resolve(absolute, relative, results, method: method, strip: strip);
|
||||
|
@ -363,61 +363,61 @@ class Router<T> {
|
|||
}
|
||||
|
||||
/// Adds a route that responds to any request matching the given path.
|
||||
Route<T> all(String path, T handler, {Iterable<T> middleware}) {
|
||||
Route<T> all(String path, T handler, {Iterable<T>? middleware}) {
|
||||
return addRoute('*', path, handler, middleware: middleware);
|
||||
}
|
||||
|
||||
/// Adds a route that responds to a DELETE request.
|
||||
Route<T> delete(String path, T handler, {Iterable<T> middleware}) {
|
||||
Route<T> delete(String path, T handler, {Iterable<T>? middleware}) {
|
||||
return addRoute('DELETE', path, handler, middleware: middleware);
|
||||
}
|
||||
|
||||
/// Adds a route that responds to a GET request.
|
||||
Route<T> get(String path, T handler, {Iterable<T> middleware}) {
|
||||
Route<T> get(String path, T handler, {Iterable<T>? middleware}) {
|
||||
return addRoute('GET', path, handler, middleware: middleware);
|
||||
}
|
||||
|
||||
/// Adds a route that responds to a HEAD request.
|
||||
Route<T> head(String path, T handler, {Iterable<T> middleware}) {
|
||||
Route<T> head(String path, T handler, {Iterable<T>? middleware}) {
|
||||
return addRoute('HEAD', path, handler, middleware: middleware);
|
||||
}
|
||||
|
||||
/// Adds a route that responds to a OPTIONS request.
|
||||
Route<T> options(String path, T handler, {Iterable<T> middleware}) {
|
||||
Route<T> options(String path, T handler, {Iterable<T>? middleware}) {
|
||||
return addRoute('OPTIONS', path, handler, middleware: middleware);
|
||||
}
|
||||
|
||||
/// Adds a route that responds to a POST request.
|
||||
Route<T> post(String path, T handler, {Iterable<T> middleware}) {
|
||||
Route<T> post(String path, T handler, {Iterable<T>? middleware}) {
|
||||
return addRoute('POST', path, handler, middleware: middleware);
|
||||
}
|
||||
|
||||
/// Adds a route that responds to a PATCH request.
|
||||
Route<T> patch(String path, T handler, {Iterable<T> middleware}) {
|
||||
Route<T> patch(String path, T handler, {Iterable<T>? middleware}) {
|
||||
return addRoute('PATCH', path, handler, middleware: middleware);
|
||||
}
|
||||
|
||||
/// Adds a route that responds to a PUT request.
|
||||
Route put(String path, T handler, {Iterable<T> middleware}) {
|
||||
Route put(String path, T handler, {Iterable<T>? middleware}) {
|
||||
return addRoute('PUT', path, handler, middleware: middleware);
|
||||
}
|
||||
}
|
||||
|
||||
class _ChainedRouter<T> extends Router<T> {
|
||||
final List<T> _handlers = <T>[];
|
||||
Router _root;
|
||||
Router? _root;
|
||||
|
||||
_ChainedRouter.empty();
|
||||
|
||||
_ChainedRouter(Router root, Iterable<T> middleware) {
|
||||
_ChainedRouter(Router? root, Iterable<T> middleware) {
|
||||
this._root = root;
|
||||
_handlers.addAll(middleware);
|
||||
}
|
||||
|
||||
@override
|
||||
Route<T> addRoute(String method, String path, handler,
|
||||
{Iterable<T> middleware}) {
|
||||
var route = super.addRoute(method, path, handler,
|
||||
Route<T> addRoute(String? method, String path, handler,
|
||||
{Iterable<T>? middleware}) {
|
||||
Route<T> route = super.addRoute(method, path, handler,
|
||||
middleware: []..addAll(_handlers)..addAll(middleware ?? []));
|
||||
//_root._routes.add(route);
|
||||
return route;
|
||||
|
@ -425,7 +425,7 @@ class _ChainedRouter<T> extends Router<T> {
|
|||
|
||||
@override
|
||||
SymlinkRoute<T> group(String path, void callback(Router<T> router),
|
||||
{Iterable<T> middleware, String name}) {
|
||||
{Iterable<T>? middleware, String? name}) {
|
||||
final router = _ChainedRouter<T>(
|
||||
_root, []..addAll(_handlers)..addAll(middleware ?? []));
|
||||
callback(router);
|
||||
|
@ -435,7 +435,7 @@ class _ChainedRouter<T> extends Router<T> {
|
|||
@override
|
||||
Future<SymlinkRoute<T>> groupAsync(
|
||||
String path, FutureOr<void> callback(Router<T> router),
|
||||
{Iterable<T> middleware, String name}) async {
|
||||
{Iterable<T>? middleware, String? name}) async {
|
||||
final router = _ChainedRouter<T>(
|
||||
_root, []..addAll(_handlers)..addAll(middleware ?? []));
|
||||
await callback(router);
|
||||
|
@ -444,7 +444,7 @@ class _ChainedRouter<T> extends Router<T> {
|
|||
|
||||
@override
|
||||
SymlinkRoute<T> mount(String path, Router<T> router) {
|
||||
final route = super.mount(path, router);
|
||||
final SymlinkRoute<T> route = super.mount(path, router);
|
||||
route.router._middleware.insertAll(0, _handlers);
|
||||
//_root._routes.add(route);
|
||||
return route;
|
||||
|
@ -473,13 +473,13 @@ Router<T> flatten<T>(Router<T> router) {
|
|||
var path = route.path.replaceAll(_straySlashes, '');
|
||||
var joined = '$base/$path'.replaceAll(_straySlashes, '');
|
||||
flattened.addRoute(route.method, joined.replaceAll(_straySlashes, ''),
|
||||
route.handlers.last,
|
||||
route.handlers!.last,
|
||||
middleware:
|
||||
route.handlers.take(route.handlers.length - 1).toList());
|
||||
route.handlers!.take(route.handlers!.length - 1).toList());
|
||||
}
|
||||
} else {
|
||||
flattened.addRoute(route.method, route.path, route.handlers.last,
|
||||
middleware: route.handlers.take(route.handlers.length - 1).toList());
|
||||
flattened.addRoute(route.method, route.path, route.handlers!.last,
|
||||
middleware: route.handlers!.take(route.handlers!.length - 1).toList());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,49 +3,49 @@ part of angel_route.src.router;
|
|||
/// Represents a complex result of navigating to a path.
|
||||
class RoutingResult<T> {
|
||||
/// The parse result that matched the given sub-path.
|
||||
final ParseResult<RouteResult> parseResult;
|
||||
final ParseResult<RouteResult?>? parseResult;
|
||||
|
||||
/// A nested instance, if a sub-path was matched.
|
||||
final Iterable<RoutingResult<T>> nested;
|
||||
final Iterable<RoutingResult<T>>? nested;
|
||||
|
||||
/// All route params matching this route on the current sub-path.
|
||||
final Map<String, dynamic> params = {};
|
||||
final Map<String?, dynamic> params = {};
|
||||
|
||||
/// The [Route] that answered this sub-path.
|
||||
///
|
||||
/// This is mostly for internal use, and useless in production.
|
||||
final Route<T> shallowRoute;
|
||||
final Route<T>? shallowRoute;
|
||||
|
||||
/// The [Router] that answered this sub-path.
|
||||
///
|
||||
/// Only really for internal use.
|
||||
final Router<T> shallowRouter;
|
||||
final Router<T>? shallowRouter;
|
||||
|
||||
/// The remainder of the full path that was not matched, and was passed to [nested] routes.
|
||||
final String tail;
|
||||
|
||||
/// The [RoutingResult] that matched the most specific sub-path.
|
||||
RoutingResult<T> get deepest {
|
||||
var search = this;
|
||||
RoutingResult<T> search = this;
|
||||
|
||||
while (search?.nested?.isNotEmpty == true) {
|
||||
search = search.nested.first;
|
||||
search = search.nested!.first;
|
||||
}
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
/// The most specific route.
|
||||
Route<T> get route => deepest.shallowRoute;
|
||||
Route<T>? get route => deepest.shallowRoute;
|
||||
|
||||
/// The most specific router.
|
||||
Router<T> get router => deepest.shallowRouter;
|
||||
Router<T>? get router => deepest.shallowRouter;
|
||||
|
||||
/// The handlers at this sub-path.
|
||||
List<T> get handlers {
|
||||
return <T>[]
|
||||
..addAll(shallowRouter.middleware)
|
||||
..addAll(shallowRoute.handlers);
|
||||
..addAll(shallowRouter!.middleware)
|
||||
..addAll(shallowRoute!.handlers!);
|
||||
}
|
||||
|
||||
/// All handlers on this sub-path and its children.
|
||||
|
@ -56,7 +56,7 @@ class RoutingResult<T> {
|
|||
handlers.addAll(result.handlers);
|
||||
|
||||
if (result.nested?.isNotEmpty == true) {
|
||||
for (var r in result.nested) {
|
||||
for (var r in result.nested!) {
|
||||
crawl(r);
|
||||
}
|
||||
}
|
||||
|
@ -68,14 +68,14 @@ class RoutingResult<T> {
|
|||
}
|
||||
|
||||
/// All parameters on this sub-path and its children.
|
||||
Map<String, dynamic> get allParams {
|
||||
final Map<String, dynamic> params = {};
|
||||
Map<String?, dynamic> get allParams {
|
||||
final Map<String?, dynamic> params = {};
|
||||
|
||||
void crawl(RoutingResult result) {
|
||||
params.addAll(result.params);
|
||||
|
||||
if (result.nested?.isNotEmpty == true) {
|
||||
for (var r in result.nested) {
|
||||
for (var r in result.nested!) {
|
||||
crawl(r);
|
||||
}
|
||||
}
|
||||
|
@ -87,11 +87,11 @@ class RoutingResult<T> {
|
|||
|
||||
RoutingResult(
|
||||
{this.parseResult,
|
||||
Map<String, dynamic> params = const {},
|
||||
Map<String?, dynamic> params = const {},
|
||||
this.nested,
|
||||
this.shallowRoute,
|
||||
this.shallowRouter,
|
||||
@required this.tail}) {
|
||||
required this.tail}) {
|
||||
this.params.addAll(params ?? {});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
name: angel_route
|
||||
description: A powerful, isomorphic routing library for Dart. It is mainly used in the Angel framework, but can be used in Flutter and on the Web.
|
||||
version: 4.0.0
|
||||
version: 5.0.0
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_route
|
||||
publish_to: none
|
||||
environment:
|
||||
sdk: ">=2.10.0 <3.0.0"
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dependencies:
|
||||
combinator: ^1.0.0
|
||||
#meta: ^1.0.0
|
||||
#path: ^1.0.0
|
||||
combinator:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/combinator
|
||||
string_scanner: ^1.0.0
|
||||
dev_dependencies:
|
||||
build_runner: ^1.11.5
|
||||
|
|
|
@ -11,7 +11,7 @@ void main() {
|
|||
..dumpTree();
|
||||
|
||||
test('nested route groups with chain', () {
|
||||
var r = router.resolveAbsolute('/b/e/f')?.first?.route;
|
||||
var r = router.resolveAbsolute('/b/e/f').first.route!;
|
||||
expect(r, isNotNull);
|
||||
expect(r.handlers, hasLength(4));
|
||||
expect(r.handlers, equals(['a', 'c', 'd', 'g']));
|
||||
|
|
|
@ -7,9 +7,9 @@ void main() {
|
|||
..get('/double/double:id', '')
|
||||
..get('/num/num:id', '');
|
||||
|
||||
num getId(String path) {
|
||||
num? getId(String path) {
|
||||
var result = router.resolveAbsolute(path).first;
|
||||
return result.allParams['id'] as num;
|
||||
return result.allParams['id'] as num?;
|
||||
}
|
||||
|
||||
test('parse', () {
|
||||
|
|
|
@ -9,11 +9,11 @@ const List<Map<String, String>> people = [
|
|||
];
|
||||
|
||||
void main() {
|
||||
http.Client client;
|
||||
http.Client? client;
|
||||
|
||||
final router = Router();
|
||||
HttpServer server;
|
||||
String url;
|
||||
late HttpServer server;
|
||||
String? url;
|
||||
|
||||
router.get('/', (req, res) {
|
||||
res.write('Root');
|
||||
|
@ -87,12 +87,12 @@ void main() {
|
|||
router.resolveAbsolute(req.uri.toString(), method: req.method);
|
||||
final pipeline = MiddlewarePipeline(results);
|
||||
|
||||
if (pipeline.handlers.isEmpty) {
|
||||
if (pipeline.handlers!.isEmpty) {
|
||||
res
|
||||
..statusCode = 404
|
||||
..writeln('404 Not Found');
|
||||
} else {
|
||||
for (final handler in pipeline.handlers) {
|
||||
for (final handler in pipeline.handlers!) {
|
||||
if (!((await handler(req, res)) as bool)) break;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ void main() {
|
|||
|
||||
tearDown(() async {
|
||||
await server.close(force: true);
|
||||
client.close();
|
||||
client!.close();
|
||||
client = null;
|
||||
url = null;
|
||||
});
|
||||
|
@ -111,13 +111,13 @@ void main() {
|
|||
group('top-level', () {
|
||||
group('get', () {
|
||||
test('root', () async {
|
||||
final res = await client.get(Uri.parse(url));
|
||||
final res = await client!.get(Uri.parse(url!));
|
||||
print('Response: ${res.body}');
|
||||
expect(res.body, equals('Root'));
|
||||
});
|
||||
|
||||
test('path', () async {
|
||||
final res = await client.get(Uri.parse('$url/hello'));
|
||||
final res = await client!.get(Uri.parse('$url/hello'));
|
||||
print('Response: ${res.body}');
|
||||
expect(res.body, equals('World'));
|
||||
});
|
||||
|
@ -127,20 +127,20 @@ void main() {
|
|||
group('group', () {
|
||||
group('top-level', () {
|
||||
test('root', () async {
|
||||
final res = await client.get(Uri.parse('$url/people'));
|
||||
final res = await client!.get(Uri.parse('$url/people'));
|
||||
print('Response: ${res.body}');
|
||||
expect(json.decode(res.body), equals(people));
|
||||
});
|
||||
|
||||
group('param', () {
|
||||
test('root', () async {
|
||||
final res = await client.get(Uri.parse('$url/people/0'));
|
||||
final res = await client!.get(Uri.parse('$url/people/0'));
|
||||
print('Response: ${res.body}');
|
||||
expect(json.decode(res.body), equals(people.first));
|
||||
});
|
||||
|
||||
test('path', () async {
|
||||
final res = await client.get(Uri.parse('$url/people/0/name'));
|
||||
final res = await client!.get(Uri.parse('$url/people/0/name'));
|
||||
print('Response: ${res.body}');
|
||||
expect(json.decode(res.body), equals(people.first['name']));
|
||||
});
|
||||
|
@ -151,20 +151,20 @@ void main() {
|
|||
group('mount', () {
|
||||
group('path', () {
|
||||
test('top-level', () async {
|
||||
final res = await client.post(Uri.parse('$url/beatles/spinal_clacker'));
|
||||
final res = await client!.post(Uri.parse('$url/beatles/spinal_clacker'));
|
||||
print('Response: ${res.body}');
|
||||
expect(res.body, equals('come together'));
|
||||
});
|
||||
|
||||
test('fallback', () async {
|
||||
final res = await client.patch(Uri.parse('$url/beatles/muddy_water'));
|
||||
final res = await client!.patch(Uri.parse('$url/beatles/muddy_water'));
|
||||
print('Response: ${res.body}');
|
||||
expect(res.body, equals('together'));
|
||||
});
|
||||
|
||||
test('fallback', () async {
|
||||
final res =
|
||||
await client.patch(Uri.parse('$url/beatles/spanil_clakcer'));
|
||||
await client!.patch(Uri.parse('$url/beatles/spanil_clakcer'));
|
||||
print('Response: ${res.body}');
|
||||
expect(res.body, equals('together'));
|
||||
});
|
||||
|
@ -172,7 +172,7 @@ void main() {
|
|||
|
||||
test('deep nested', () async {
|
||||
final res =
|
||||
await client.get(Uri.parse('$url/beatles/big/yellow/submarine'));
|
||||
await client!.get(Uri.parse('$url/beatles/big/yellow/submarine'));
|
||||
print('Response: ${res.body}');
|
||||
expect(res.body, equals('we all live in a'));
|
||||
});
|
||||
|
@ -187,17 +187,17 @@ void main() {
|
|||
});
|
||||
|
||||
test('path', () async {
|
||||
await expect404(client.get(Uri.parse('$url/foo')));
|
||||
await expect404(client.get(Uri.parse('$url/bye')));
|
||||
await expect404(client.get(Uri.parse('$url/people/0/age')));
|
||||
await expect404(client.get(Uri.parse('$url/beatles2')));
|
||||
await expect404(client!.get(Uri.parse('$url/foo')));
|
||||
await expect404(client!.get(Uri.parse('$url/bye')));
|
||||
await expect404(client!.get(Uri.parse('$url/people/0/age')));
|
||||
await expect404(client!.get(Uri.parse('$url/beatles2')));
|
||||
});
|
||||
|
||||
test('method', () async {
|
||||
await expect404(client.head(Uri.parse(url)));
|
||||
await expect404(client.patch(Uri.parse('$url/people')));
|
||||
await expect404(client.post(Uri.parse('$url/people/0')));
|
||||
await expect404(client.delete(Uri.parse('$url/beatles2/spinal_clacker')));
|
||||
await expect404(client!.head(Uri.parse(url!)));
|
||||
await expect404(client!.patch(Uri.parse('$url/people')));
|
||||
await expect404(client!.post(Uri.parse('$url/people/0')));
|
||||
await expect404(client!.delete(Uri.parse('$url/beatles2/spinal_clacker')));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -26,21 +26,21 @@ void main() {
|
|||
test('tail explicitly set intermediate', () {
|
||||
var results = router.resolveAbsolute('/songs/in_the/key');
|
||||
var result = results.first;
|
||||
print(results.map((r) => {r.route.path: r.tail}));
|
||||
print(results.map((r) => {r.route!.path: r.tail}));
|
||||
expect(result.tail, 'in_the');
|
||||
});
|
||||
|
||||
test('tail explicitly set at end', () {
|
||||
var results = router.resolveAbsolute('/isnt/she/epic');
|
||||
var result = results.first;
|
||||
print(results.map((r) => {r.route.path: r.tail}));
|
||||
print(results.map((r) => {r.route!.path: r.tail}));
|
||||
expect(result.tail, 'epic');
|
||||
});
|
||||
|
||||
test('tail with trailing', () {
|
||||
var results = router.resolveAbsolute('/isnt/she/epic/fail');
|
||||
var result = results.first;
|
||||
print(results.map((r) => {r.route.path: r.tail}));
|
||||
print(results.map((r) => {r.route!.path: r.tail}));
|
||||
expect(result.tail, 'epic/fail');
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,15 +9,15 @@ basic(BrowserRouter router) {
|
|||
final route = result?.route;
|
||||
|
||||
if (route == null) {
|
||||
$h1.text = 'No Active Route';
|
||||
$ul.children
|
||||
$h1!.text = 'No Active Route';
|
||||
$ul!.children
|
||||
..clear()
|
||||
..add(LIElement()..text = '(empty)');
|
||||
} else {
|
||||
$h1.text = 'Active Route: ${route.name ?? route.path}';
|
||||
$ul.children
|
||||
$h1!.text = 'Active Route: ${route.name ?? route.path}';
|
||||
$ul!.children
|
||||
..clear()
|
||||
..addAll(result.allHandlers
|
||||
..addAll(result!.allHandlers
|
||||
.map((handler) => LIElement()..text = handler.toString()));
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue