Skip to content

Commit

Permalink
422f466 refactor(router): improve recognition and generation pipeline
Browse files Browse the repository at this point in the history
  • Loading branch information
btford committed Nov 25, 2015
1 parent e1f9f8f commit 0384122
Show file tree
Hide file tree
Showing 35 changed files with 3,586 additions and 1,423 deletions.
4 changes: 2 additions & 2 deletions BUILD_INFO
@@ -1,2 +1,2 @@
Wed Nov 25 22:21:04 UTC 2015
cf157b99d3186f568a2afea10c6cd74f9c81bb44
Wed Nov 25 22:32:38 UTC 2015
422f46652832fa3a8b58e9a9350e1fe1a58dffd5
533 changes: 269 additions & 264 deletions _analyzer.dart

Large diffs are not rendered by default.

33 changes: 4 additions & 29 deletions lib/router.dart
Expand Up @@ -9,7 +9,8 @@ export "src/router/router.dart" show Router;
export "src/router/router_outlet.dart" show RouterOutlet;
export "src/router/router_link.dart" show RouterLink;
export "src/router/instruction.dart" show RouteParams, RouteData;
export "src/router/route_registry.dart" show RouteRegistry;
export "src/router/route_registry.dart"
show RouteRegistry, ROUTER_PRIMARY_COMPONENT;
export "src/router/location_strategy.dart" show LocationStrategy, APP_BASE_HREF;
export "src/router/hash_location_strategy.dart" show HashLocationStrategy;
export "src/router/path_location_strategy.dart" show PathLocationStrategy;
Expand All @@ -26,39 +27,13 @@ import "src/router/path_location_strategy.dart" show PathLocationStrategy;
import "src/router/router.dart" show Router, RootRouter;
import "src/router/router_outlet.dart" show RouterOutlet;
import "src/router/router_link.dart" show RouterLink;
import "src/router/route_registry.dart" show RouteRegistry;
import "src/router/route_registry.dart"
show RouteRegistry, ROUTER_PRIMARY_COMPONENT;
import "src/router/location.dart" show Location;
import "package:angular2/core.dart"
show ApplicationRef, provide, OpaqueToken, Provider;
import "package:angular2/src/facade/exceptions.dart" show BaseException;

/**
* Token used to bind the component with the top-level [RouteConfig]s for the
* application.
*
* ### Example ([live demo](http://plnkr.co/edit/iRUP8B5OUbxCWQ3AcIDm))
*
* ```
* import {Component} from 'angular2/angular2';
* import {
* ROUTER_DIRECTIVES,
* ROUTER_PROVIDERS,
* RouteConfig
* } from 'angular2/router';
*
* @Component({directives: [ROUTER_DIRECTIVES]})
* @RouteConfig([
* {...},
* ])
* class AppCmp {
* // ...
* }
*
* bootstrap(AppCmp, [ROUTER_PROVIDERS]);
* ```
*/
const OpaqueToken ROUTER_PRIMARY_COMPONENT =
const OpaqueToken("RouterPrimaryComponent");
/**
* A list of directives. To use the router directives like [RouterOutlet] and
* [RouterLink], add this to your `directives` array in the [View] decorator of your
Expand Down
9 changes: 6 additions & 3 deletions lib/src/router/async_route_handler.dart
@@ -1,16 +1,19 @@
library angular2.src.router.async_route_handler;

import "route_handler.dart" show RouteHandler;
import "package:angular2/src/facade/async.dart" show Future, PromiseWrapper;
import "package:angular2/src/facade/lang.dart" show isPresent, Type;
import "route_handler.dart" show RouteHandler;
import "instruction.dart" show RouteData, BLANK_ROUTE_DATA;

class AsyncRouteHandler implements RouteHandler {
Function _loader;
Map<String, dynamic> data;
/** @internal */
Future<dynamic> _resolvedComponent = null;
Type componentType;
AsyncRouteHandler(this._loader, [this.data]) {}
RouteData data;
AsyncRouteHandler(this._loader, [Map<String, dynamic> data = null]) {
this.data = isPresent(data) ? new RouteData(data) : BLANK_ROUTE_DATA;
}
Future<dynamic> resolveComponentType() {
if (isPresent(this._resolvedComponent)) {
return this._resolvedComponent;
Expand Down
143 changes: 143 additions & 0 deletions lib/src/router/component_recognizer.dart
@@ -0,0 +1,143 @@
library angular2.src.router.component_recognizer;

import "package:angular2/src/facade/lang.dart" show isBlank, isPresent;
import "package:angular2/src/facade/exceptions.dart"
show BaseException, WrappedException;
import "package:angular2/src/facade/collection.dart"
show Map, MapWrapper, ListWrapper, StringMapWrapper;
import "package:angular2/src/facade/async.dart" show Future, PromiseWrapper;
import "route_recognizer.dart"
show AbstractRecognizer, RouteRecognizer, RedirectRecognizer, RouteMatch;
import "route_config_impl.dart"
show Route, AsyncRoute, AuxRoute, Redirect, RouteDefinition;
import "async_route_handler.dart" show AsyncRouteHandler;
import "sync_route_handler.dart" show SyncRouteHandler;
import "url_parser.dart" show Url;
import "instruction.dart" show ComponentInstruction;

/**
* `ComponentRecognizer` is responsible for recognizing routes for a single component.
* It is consumed by `RouteRegistry`, which knows how to recognize an entire hierarchy of
* components.
*/
class ComponentRecognizer {
var names = new Map<String, RouteRecognizer>();
// map from name to recognizer
var auxNames = new Map<String, RouteRecognizer>();
// map from starting path to recognizer
var auxRoutes = new Map<String, RouteRecognizer>();
// TODO: optimize this into a trie
List<AbstractRecognizer> matchers = [];
RouteRecognizer defaultRoute = null;
/**
* returns whether or not the config is terminal
*/
bool config(RouteDefinition config) {
var handler;
if (isPresent(config.name) &&
config.name[0].toUpperCase() != config.name[0]) {
var suggestedName =
config.name[0].toUpperCase() + config.name.substring(1);
throw new BaseException(
'''Route "${ config . path}" with name "${ config . name}" does not begin with an uppercase letter. Route names should be CamelCase like "${ suggestedName}".''');
}
if (config is AuxRoute) {
handler = new SyncRouteHandler(config.component, config.data);
var path =
config.path.startsWith("/") ? config.path.substring(1) : config.path;
var recognizer = new RouteRecognizer(config.path, handler);
this.auxRoutes[path] = recognizer;
if (isPresent(config.name)) {
this.auxNames[config.name] = recognizer;
}
return recognizer.terminal;
}
var useAsDefault = false;
if (config is Redirect) {
var redirector = new RedirectRecognizer(config.path, config.redirectTo);
this._assertNoHashCollision(redirector.hash, config.path);
this.matchers.add(redirector);
return true;
}
if (config is Route) {
handler = new SyncRouteHandler(config.component, config.data);
useAsDefault = isPresent(config.useAsDefault) && config.useAsDefault;
} else if (config is AsyncRoute) {
handler = new AsyncRouteHandler(config.loader, config.data);
useAsDefault = isPresent(config.useAsDefault) && config.useAsDefault;
}
var recognizer = new RouteRecognizer(config.path, handler);
this._assertNoHashCollision(recognizer.hash, config.path);
if (useAsDefault) {
if (isPresent(this.defaultRoute)) {
throw new BaseException('''Only one route can be default''');
}
this.defaultRoute = recognizer;
}
this.matchers.add(recognizer);
if (isPresent(config.name)) {
this.names[config.name] = recognizer;
}
return recognizer.terminal;
}

_assertNoHashCollision(String hash, path) {
this.matchers.forEach((matcher) {
if (hash == matcher.hash) {
throw new BaseException(
'''Configuration \'${ path}\' conflicts with existing route \'${ matcher . path}\'''');
}
});
}

/**
* Given a URL, returns a list of `RouteMatch`es, which are partial recognitions for some route.
*/
List<Future<RouteMatch>> recognize(Url urlParse) {
var solutions = [];
this.matchers.forEach((AbstractRecognizer routeRecognizer) {
var pathMatch = routeRecognizer.recognize(urlParse);
if (isPresent(pathMatch)) {
solutions.add(pathMatch);
}
});
return solutions;
}

List<Future<RouteMatch>> recognizeAuxiliary(Url urlParse) {
RouteRecognizer routeRecognizer = this.auxRoutes[urlParse.path];
if (isPresent(routeRecognizer)) {
return [routeRecognizer.recognize(urlParse)];
}
return [PromiseWrapper.resolve(null)];
}

bool hasRoute(String name) {
return this.names.containsKey(name);
}

bool componentLoaded(String name) {
return this.hasRoute(name) &&
isPresent(this.names[name].handler.componentType);
}

Future<dynamic> loadComponent(String name) {
return this.names[name].handler.resolveComponentType();
}

ComponentInstruction generate(String name, dynamic params) {
RouteRecognizer pathRecognizer = this.names[name];
if (isBlank(pathRecognizer)) {
return null;
}
return pathRecognizer.generate(params);
}

ComponentInstruction generateAuxiliary(String name, dynamic params) {
RouteRecognizer pathRecognizer = this.auxNames[name];
if (isBlank(pathRecognizer)) {
return null;
}
return pathRecognizer.generate(params);
}
}

0 comments on commit 0384122

Please sign in to comment.