Skip to content
This repository was archived by the owner on Feb 26, 2024. It is now read-only.

Commit 6d51f49

Browse files
committed
refactor(*): model route config as CFG
See #131 Closes #103 Closes #104
1 parent 8ddff79 commit 6d51f49

File tree

12 files changed

+748
-388
lines changed

12 files changed

+748
-388
lines changed
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1-
'use strict';
2-
31
angular.module('example', [
42
'example.goodbye',
53
'example.welcome',
64
'ngAnimate',
75
'ngNewRouter'
8-
]).
9-
controller('AppController', ['$router', AppController]);
6+
])
7+
.controller('AppController', ['$router', AppController]);
108

9+
AppController.$routeConfig = [
10+
{ path: '/', redirectTo: '/welcome' },
11+
{ path: '/welcome', component: 'welcome' },
12+
{ path: '/goodbye', component: 'goodbye' }
13+
];
1114
function AppController($router) {
12-
$router.config([
13-
{ path: '/', component: 'welcome' },
14-
{ path: '/welcome', component: 'welcome' },
15-
{ path: '/goodbye', component: 'goodbye' }
16-
]);
15+
this.greeting = 'Hello';
1716
}

examples/angular-1/sibbling-routes/app.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ angular.module('myApp', [
33
])
44
.controller('AppController', ['$router', AppController]);
55

6-
function AppController($router) {
7-
$router.config([
8-
{ path: '/', redirectTo: '/users/posts' },
9-
{ path: '/users/posts', components: { left: 'users', right: 'posts' } },
10-
{ path: '/posts/users', components: { left: 'posts', right: 'users' } },
11-
]);
12-
}
6+
AppController.$routeConfig = [
7+
{ path: '/', redirectTo: '/users/posts' },
8+
{ path: '/users/posts', components: { left: 'users', right: 'posts' } },
9+
{ path: '/posts/users', components: { left: 'posts', right: 'users' } },
10+
];
11+
function AppController($router) {}

gulpfile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ gulp.task('transpile', function() {
3030
gulp.task('angularify', ['transpile'], function() {
3131
var directive = gulp.src('./src/*.es5.js');
3232

33-
var generated = gulp.src('./src/**.ats')
33+
var generated = gulp.src(['./src/router.ats', './src/grammar.ats'])
3434
.pipe(modulate({
3535
moduleName: 'ngNewRouter.generated'
3636
}))

scripts/angular-modulate.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ var MODULE_LOCATIONS = {
2323
'route-recognizer': 'node_modules/route-recognizer/lib/route-recognizer'
2424
};
2525

26+
/*
27+
* just add dep, don't try to transpile
28+
*/
29+
var STUB_LOCATIONS = {
30+
'./pipeline': true
31+
};
32+
2633

2734
var TRACEUR_CREATE_CLASS = new RegExp('\\$traceurRuntime\\.(createClass|superCall)', 'g');
2835
function detraceurify (contents) {

src/grammar.ats

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
import RouteRecognizer from 'route-recognizer';
2+
3+
var CHILD_ROUTE_SUFFIX = '/*childRoute';
4+
5+
/*
6+
* only one of these
7+
*/
8+
export class Grammar {
9+
constructor() {
10+
this.rules = {};
11+
}
12+
13+
config(name, config) {
14+
if (name === 'app') {
15+
name = '/';
16+
}
17+
if (!this.rules[name]) {
18+
this.rules[name] = new MiniRecognizer(name);
19+
}
20+
this.rules[name].config(config);
21+
}
22+
23+
recognize(url:string, componentName = '/') {
24+
25+
var componentRecognizer = this.rules[componentName];
26+
if (!componentRecognizer) {
27+
return;
28+
}
29+
30+
var context = componentRecognizer.recognize(url);
31+
if (!context) {
32+
return;
33+
}
34+
35+
var lastContextChunk = context[context.length - 1];
36+
var lastHandler = lastContextChunk.handler;
37+
var lastParams = lastContextChunk.params;
38+
39+
var instruction = {
40+
viewports: {},
41+
params: lastParams
42+
};
43+
44+
if (lastParams && lastParams.childRoute) {
45+
var childUrl = '/' + lastParams.childRoute;
46+
// TODO: handle multiple children
47+
instruction.canonicalUrl = lastHandler.rewroteUrl.substr(0, lastHandler.rewroteUrl.length - (lastParams.childRoute.length + 1));
48+
49+
forEach(lastHandler.components, (componentName, viewportName) => {
50+
instruction.viewports[viewportName] = this.recognize(childUrl, componentName);
51+
});
52+
53+
instruction.canonicalUrl += instruction.viewports[Object.keys(instruction.viewports)[0]].canonicalUrl;
54+
} else {
55+
instruction.canonicalUrl = lastHandler.rewroteUrl;
56+
forEach(lastHandler.components, (componentName, viewportName) => {
57+
instruction.viewports[viewportName] = {
58+
viewports: {}
59+
};
60+
});
61+
}
62+
63+
forEach(instruction.viewports, (instruction, componentName) => {
64+
instruction.component = lastHandler.components[componentName];
65+
instruction.params = lastParams;
66+
});
67+
68+
return instruction;
69+
}
70+
71+
generate(name, params) {
72+
var path = '';
73+
var solution;
74+
do {
75+
solution = null;
76+
forEach(this.rules, recognizer => {
77+
if (recognizer.hasRoute(name)) {
78+
path = recognizer.generate(name, params) + path;
79+
solution = recognizer;
80+
}
81+
});
82+
if (!solution) {
83+
return '';
84+
}
85+
name = solution.name;
86+
} while (solution.name !== '/');
87+
88+
return path;
89+
}
90+
}
91+
92+
/*
93+
* includes redirect rules
94+
*/
95+
class MiniRecognizer {
96+
constructor(name) {
97+
this.name = name;
98+
this.rewrites = {};
99+
this.recognizer = new RouteRecognizer();
100+
}
101+
102+
config(mapping) {
103+
if (mapping instanceof Array) {
104+
mapping.forEach(nav => this.configOne(nav));
105+
} else {
106+
this.configOne(mapping);
107+
}
108+
}
109+
110+
getCanonicalUrl(url) {
111+
if (url[0] === '.') {
112+
url = url.substr(1);
113+
}
114+
115+
if (url === '' || url[0] !== '/') {
116+
url = '/' + url;
117+
}
118+
119+
// TODO: normalize this
120+
forEach(this.rewrites, function (toUrl, fromUrl) {
121+
if (fromUrl === '/') {
122+
if (url === '/') {
123+
url = toUrl;
124+
}
125+
} else if (url.indexOf(fromUrl) === 0) {
126+
url = url.replace(fromUrl, toUrl);
127+
}
128+
});
129+
130+
return url;
131+
}
132+
133+
configOne(mapping) {
134+
if (mapping.redirectTo) {
135+
if (this.rewrites[mapping.path]) {
136+
throw new Error('"' + mapping.path + '" already maps to "' + this.rewrites[mapping.path] + '"');
137+
}
138+
this.rewrites[mapping.path] = mapping.redirectTo;
139+
return;
140+
}
141+
142+
// normalize "component" and "components" in config
143+
if (mapping.component) {
144+
if (mapping.components) {
145+
throw new Error('A route config should have either a "component" or "components" property, but not both.');
146+
}
147+
mapping.components = mapping.component;
148+
delete mapping.component;
149+
}
150+
if (typeof mapping.components === 'string') {
151+
mapping.components = { default: mapping.components };
152+
}
153+
var aliases;
154+
if (mapping.as) {
155+
aliases = [mapping.as];
156+
} else {
157+
aliases = mapObj(mapping.components,
158+
(componentName, viewportName) => viewportName + ':' + componentName);
159+
160+
if (mapping.components.default) {
161+
aliases.push(mapping.components.default);
162+
}
163+
}
164+
aliases.forEach(alias => this.recognizer.add([{ path: mapping.path, handler: mapping }], { as: alias }));
165+
166+
var withChild = copy(mapping);
167+
withChild.path += CHILD_ROUTE_SUFFIX;
168+
this.recognizer.add([{
169+
path: withChild.path,
170+
handler: withChild
171+
}]);
172+
}
173+
174+
recognize(url) {
175+
var canonicalUrl = this.getCanonicalUrl(url);
176+
var context = this.recognizer.recognize(canonicalUrl);
177+
if (context) {
178+
context[0].handler.rewroteUrl = canonicalUrl;
179+
}
180+
return context;
181+
}
182+
183+
generate(name, params) {
184+
return this.recognizer.generate(name, params);
185+
}
186+
187+
hasRoute(name) {
188+
return this.recognizer.hasRoute(name);
189+
}
190+
}
191+
192+
193+
function copy(obj) {
194+
return JSON.parse(JSON.stringify(obj));
195+
}
196+
197+
function forEach(obj, fn) {
198+
Object.keys(obj).forEach(key => fn(obj[key], key));
199+
}
200+
201+
function mapObj(obj, fn) {
202+
var result = [];
203+
Object.keys(obj).forEach(key => result.push(fn(obj[key], key)));
204+
return result;
205+
}

src/pipeline.ats

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export class Pipeline {
2+
init(){}
3+
}

0 commit comments

Comments
 (0)