Compare commits
69 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
acebd1e5c0 | ||
|
d678d066c3 | ||
|
2245de31d7 | ||
|
065b500561 | ||
|
75b130ef91 | ||
|
72598f09e1 | ||
|
d7cfd6d9f3 | ||
|
21f5dea449 | ||
|
a1e56dc6c3 | ||
|
34b7580d18 | ||
|
4ab3fa2626 | ||
|
5b45250bf5 | ||
|
787a22ed44 | ||
|
79062fe337 | ||
|
f90743d4cf | ||
|
5620117e6c | ||
|
b22725acd0 | ||
|
abc9ffc405 | ||
|
a9b90040bd | ||
|
41b4a5ed19 | ||
|
29f07a7259 | ||
|
c6fa4e2919 | ||
|
3fad8330f6 | ||
|
03c6284aa8 | ||
|
316e8d1ac0 | ||
|
c35bb0dd69 | ||
|
92cae2c0a4 | ||
|
b2ab39b942 | ||
|
02b3fc1edb | ||
|
465c53cd4f | ||
|
a170883d93 | ||
|
12621d0883 | ||
|
ff83e279f1 | ||
|
00f5a9d1cc | ||
|
6659ca98b7 | ||
|
2702bb8ff6 | ||
|
474274c71f | ||
|
ef16262c24 | ||
|
0949461127 | ||
|
c7310adf2a | ||
|
9fc6a6fb2b | ||
|
ab2feb1c30 | ||
|
009ff8fdb9 | ||
|
fc3f7e1d7d | ||
|
b463c05733 | ||
|
27da6f94bf | ||
|
893d39e765 | ||
|
8dfd3731fd | ||
|
1d10bfe2fa | ||
|
a5da97c3bd | ||
|
4cc0a9f511 | ||
|
79ac822adb | ||
|
089098a94f | ||
|
f15cfed2e9 | ||
|
5d75c4a259 | ||
|
dd1d972f57 | ||
|
965dd5f08d | ||
|
b820c2c6d0 | ||
|
e17d46ad20 | ||
|
a8edd83f86 | ||
|
74bcccec72 | ||
|
be20f98498 | ||
|
2bcf695198 | ||
|
390cd54a48 | ||
|
9c7681e2b4 | ||
|
534f529b0c | ||
|
9223011ebe | ||
|
c42133a421 | ||
|
6d2dd79194 |
9
.codeclimate.yml
Normal file
9
.codeclimate.yml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
engines:
|
||||||
|
duplication:
|
||||||
|
enabled: true
|
||||||
|
config:
|
||||||
|
languages:
|
||||||
|
- javascript
|
||||||
|
ratings:
|
||||||
|
paths:
|
||||||
|
- "lib/**/*"
|
@ -1,2 +1,5 @@
|
|||||||
|
frontexpress.js
|
||||||
|
frontexpress.min.js
|
||||||
|
frontexpress.min.js.map
|
||||||
coverage
|
coverage
|
||||||
dist
|
dist
|
@ -1,2 +0,0 @@
|
|||||||
test
|
|
||||||
coverage
|
|
@ -6,4 +6,4 @@ before_script:
|
|||||||
- npm install coveralls
|
- npm install coveralls
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
- cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
|
||||||
|
405
README.md
405
README.md
@ -1,41 +1,272 @@
|
|||||||

|

|
||||||
|
|
||||||
Minimalist front end router framework à la [express](http://expressjs.com/)
|
A simple vanilla JavaScript router a la [ExpressJS](http://expressjs.com/).
|
||||||
|
|
||||||
|
Code the front-end like the back-end.
|
||||||
|
|
||||||
|
[frontexpress demo](https://github.com/camelaissani/frontexpress-demo)
|
||||||
|
|
||||||
[](https://travis-ci.org/camelaissani/frontexpress)
|
[](https://travis-ci.org/camelaissani/frontexpress)
|
||||||
[](https://codeclimate.com/github/camelaissani/frontexpress)
|
[](https://codeclimate.com/github/camelaissani/frontexpress)
|
||||||
[](https://coveralls.io/github/camelaissani/frontexpress?branch=master)
|
[](https://coveralls.io/github/camelaissani/frontexpress?branch=master)
|
||||||

|

|
||||||
|

|
||||||
```js
|
|
||||||
import frontexpress from 'frontexpress';
|
|
||||||
const app = frontexpress();
|
|
||||||
|
|
||||||
// listen HTTP GET request on path (/)
|
|
||||||
app.get('/', (req, res) => {
|
|
||||||
window.alert('Hello World');
|
|
||||||
});
|
|
||||||
|
|
||||||
// listen HTTP GET request on API (/api/xxx)
|
|
||||||
// update page content with response
|
|
||||||
app.get(/^\/api\//, (req, res, next) => {
|
|
||||||
document.querySelector('.content').innerHTML = res.responseText;
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
// start listening frontend application requests
|
|
||||||
app.listen();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
### From npm repository
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npm install frontexpress
|
$ npm install frontexpress
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
### From bower repository
|
||||||
|
|
||||||
The quickest way to get started with frontexpress is to clone the [frontexpress-demo](https://github.com/camelaissani/frontexpress-demo) repository.
|
```bash
|
||||||
|
$ bower install frontexpress
|
||||||
|
```
|
||||||
|
|
||||||
|
### From CDN
|
||||||
|
|
||||||
|
On [jsDelivr](https://cdn.jsdelivr.net/npm/frontexpress@1.2.0/frontexpress.min.js)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import frontexpress from 'frontexpress';
|
||||||
|
|
||||||
|
// Front-end application
|
||||||
|
const app = frontexpress();
|
||||||
|
|
||||||
|
// front-end logic on navigation path "/page1"
|
||||||
|
app.get('/page1', (req, res) => {
|
||||||
|
document.querySelector('.content').innerHTML = res.responseText;
|
||||||
|
});
|
||||||
|
|
||||||
|
// front-end logic on navigation path "/page2"
|
||||||
|
app.get('/page2', (req, res) => {
|
||||||
|
document.querySelector('.content').innerHTML = res.responseText;
|
||||||
|
});
|
||||||
|
|
||||||
|
// start front-end application
|
||||||
|
app.listen();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Routes
|
||||||
|
|
||||||
|
Listen GET requests on path /hello:
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.get('/hello', (req, res) => {
|
||||||
|
window.alert('Hello World');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Listen POST requests on path /item:
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.post('/item', (req, res) => {
|
||||||
|
window.alert('Got a POST request at /item');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Listen GET requests on path starting with /api/:
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.get(/^api\//, (req, res) => {
|
||||||
|
console.log(`api was requested ${req.uri}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Get parameters from path
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.get('/product/:id', (req, res) => {
|
||||||
|
// if we have /product/42 then
|
||||||
|
// req.params.id = 42
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.get('/user/:firstname?/:lastname', (req, res) => {
|
||||||
|
// if we have /user/camel/aissani then
|
||||||
|
// req.params.firstname = 'camel'
|
||||||
|
// req.params.lastname = 'aissani'
|
||||||
|
|
||||||
|
// if we have /user/aissani then
|
||||||
|
// req.params.firstname = undefined
|
||||||
|
// req.params.lastname = 'aissani'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.get('/user/:id', (req, res) => {
|
||||||
|
// if we have /user/1,2,3 then
|
||||||
|
// req.params.id = [1,2,3]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
You can have the full capabilities of Express-style path with this plugin [frontexpress-path-to-regexp](https://github.com/camelaissani/frontexpress-path-to-regexp)
|
||||||
|
|
||||||
|
### Middleware object
|
||||||
|
|
||||||
|
The middleware object gives access to more hooks
|
||||||
|
|
||||||
|
```js
|
||||||
|
class MyMiddleware = new Middleware {
|
||||||
|
entered(req) {
|
||||||
|
// before request sent
|
||||||
|
}
|
||||||
|
|
||||||
|
updated(req, res) {
|
||||||
|
// after request sent
|
||||||
|
// res has the request response
|
||||||
|
window.alert('Hello World');
|
||||||
|
}
|
||||||
|
|
||||||
|
exited(req) {
|
||||||
|
// before a new request sent
|
||||||
|
}
|
||||||
|
|
||||||
|
failed(req, res) {
|
||||||
|
// on request failed
|
||||||
|
}
|
||||||
|
|
||||||
|
next() {
|
||||||
|
// for chaining
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
app.get('/hello', new MyMiddleware());
|
||||||
|
```
|
||||||
|
|
||||||
|
### Chain handlers
|
||||||
|
|
||||||
|
You can provide multiple handlers functions on a navigation path. Invoking ```next()``` function allows to chain the handlers.
|
||||||
|
At the opposite, when the ```next()``` method is not called the handler chain is stopped.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const h1 = (req, res, next) => { console.log('h1!'); next(); };
|
||||||
|
const h2 = (req, res, next) => { console.log('h2!') };
|
||||||
|
const h3 = (req, res, next) => { console.log('h3!'); next(); };
|
||||||
|
|
||||||
|
app.get('/example/a', h1);
|
||||||
|
app.get('/example/a', h2);
|
||||||
|
app.get('/example/a', h3);
|
||||||
|
```
|
||||||
|
|
||||||
|
On navigation on path /example/a, the browser console displays the following:
|
||||||
|
|
||||||
|
```
|
||||||
|
h1!
|
||||||
|
h2!
|
||||||
|
```
|
||||||
|
|
||||||
|
h3 is ignored because ```next()``` function was not invoked.
|
||||||
|
|
||||||
|
#### app.route()
|
||||||
|
|
||||||
|
You can create chainable route handlers for a route path by using ```app.route()```.
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.route('/book')
|
||||||
|
.get((req, res) => { console.log('Get a random book') })
|
||||||
|
.post((req, res) => { console.log('Add a book') })
|
||||||
|
.put((req, res) => { console.log('Update the book') });
|
||||||
|
```
|
||||||
|
|
||||||
|
#### frontexpress.Router
|
||||||
|
|
||||||
|
Use the ```frontexpress.Router``` class to create modular, mountable route handlers.
|
||||||
|
|
||||||
|
Create a router file named ```birds.js```:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import frontexpress from 'frontexpress';
|
||||||
|
|
||||||
|
const router = frontexpress.Router();
|
||||||
|
|
||||||
|
// specific middleware for this router
|
||||||
|
router.use((req, res, next) => {
|
||||||
|
console.log(`Time: ${Date.now()}`);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
// listen navigation on the home page
|
||||||
|
router.get('/', (req, res) => {
|
||||||
|
document.querySelector('.content').innerHTML = '<p>Birds home page</p>';
|
||||||
|
});
|
||||||
|
|
||||||
|
// listen navigation on the about page
|
||||||
|
router.get('/about', (req, res) => {
|
||||||
|
document.querySelector('.content').innerHTML = '<p>About birds</p>';
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, load the router module in the app:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import birds from './birds';
|
||||||
|
...
|
||||||
|
app.use('/birds', birds);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
|
### Extend frontexpress via plugins:
|
||||||
|
|
||||||
|
- [frontexpress-path-to-regexp](https://github.com/camelaissani/frontexpress-path-to-regexp): Add the ability to support Express-style path string such as /user/:name, /user*...
|
||||||
|
|
||||||
|
Others are coming
|
||||||
|
|
||||||
|
### Write your own plugin
|
||||||
|
|
||||||
|
It consists to simply create an object with two properties:
|
||||||
|
- **name**: the name of your plugin
|
||||||
|
- **plugin**: the function containing the implementation
|
||||||
|
|
||||||
|
Let's assume that we have implemented this plugin in the `frontexpress-my-plugin.js` file as below:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default {
|
||||||
|
name: 'My plugin',
|
||||||
|
plugin(app) {
|
||||||
|
// the plugin implementation goes here
|
||||||
|
|
||||||
|
// Some ideas
|
||||||
|
// you can get settings
|
||||||
|
// const transformer = app.get('http GET transformer');
|
||||||
|
//
|
||||||
|
// you can set settings
|
||||||
|
// app.set('http requester', {
|
||||||
|
// fetch() {
|
||||||
|
// ...
|
||||||
|
// }});
|
||||||
|
//
|
||||||
|
// you can complete routes
|
||||||
|
// app.get(...)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
To use it:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import frontexpress from 'frontexpress';
|
||||||
|
import myPlugin from 'frontexpress-my-plugin';
|
||||||
|
|
||||||
|
// Front-end application
|
||||||
|
const app = frontexpress();
|
||||||
|
|
||||||
|
// tell to frontexpress to use your plugin
|
||||||
|
app.use(myPlugin);
|
||||||
|
```
|
||||||
|
|
||||||
|
## More
|
||||||
|
|
||||||
|
[API](https://github.com/camelaissani/frontexpress/blob/master/docs/api.md)
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
@ -53,130 +284,6 @@ $ npm install
|
|||||||
$ npm test
|
$ npm test
|
||||||
```
|
```
|
||||||
|
|
||||||
## Disclaimer
|
|
||||||
|
|
||||||
>
|
|
||||||
> In this first version of frontexpress, the API is not completely the miror of the expressjs one.
|
|
||||||
>
|
|
||||||
> There are some missing methods. Currently, the use, get, post... methods having a middlewares array as parameter are not available.
|
|
||||||
> The string pattern to define route paths is not yet implemented.
|
|
||||||
>
|
|
||||||
> Obviously, the objective is to have the same API as expressjs when the methods make sense browser side.
|
|
||||||
>
|
|
||||||
|
|
||||||
## Routing
|
|
||||||
|
|
||||||
### Basic routing
|
|
||||||
|
|
||||||
Routing allows to link the frontend application with HTTP requests to a particular URI (or path).
|
|
||||||
The link can be specific to an HTTP request method (GET, POST, and so on).
|
|
||||||
|
|
||||||
The following examples illustrate how to define simple routes.
|
|
||||||
|
|
||||||
Listen an HTTP GET request on URI (/):
|
|
||||||
|
|
||||||
```js
|
|
||||||
app.get('/', (req, res) => {
|
|
||||||
window.alert('Hello World');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Listen an HTTP POST request on URI (/):
|
|
||||||
|
|
||||||
```js
|
|
||||||
app.post('/', (req, res) => {
|
|
||||||
window.alert('Got a POST request at /');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Route paths
|
|
||||||
|
|
||||||
Route paths, in combination with a request method, define the endpoints at which requests can be made.
|
|
||||||
Route paths can be strings (see basic routing section), or regular expressions.
|
|
||||||
|
|
||||||
This route path matches all GET request paths which start with (/api/):
|
|
||||||
|
|
||||||
```js
|
|
||||||
app.get(/^api\//, (req, res) => {
|
|
||||||
console.log(`api was requested ${req.uri}`);
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Route handlers
|
|
||||||
|
|
||||||
You can provide multiple callback functions to handle a request. Invoking ```next()``` function allows to pass the control to subsequent routes.
|
|
||||||
Whether ```next()``` method is not called the handler chain is stoped
|
|
||||||
|
|
||||||
```js
|
|
||||||
const h1 = (req, res, next) => { console.log('h1!'); next(); };
|
|
||||||
const h2 = (req, res, next) => { console.log('h2!') };
|
|
||||||
const h3 = (req, res, next) => { console.log('h3!'); next(); };
|
|
||||||
|
|
||||||
app.get('/example/a', h1);
|
|
||||||
app.get('/example/a', h2);
|
|
||||||
app.get('/example/a', h3);
|
|
||||||
```
|
|
||||||
|
|
||||||
A response to a GET request on path (/example/a) displays:
|
|
||||||
|
|
||||||
```
|
|
||||||
h1!
|
|
||||||
h2!
|
|
||||||
```
|
|
||||||
|
|
||||||
h3 is ignored because ```next()``` function was not invoked.
|
|
||||||
|
|
||||||
### app.route()
|
|
||||||
|
|
||||||
You can create chainable route handlers for a route path by using ```app.route()```.
|
|
||||||
|
|
||||||
```js
|
|
||||||
app.route('/book')
|
|
||||||
.get((req, res) => { console.log('Get a random book') })
|
|
||||||
.post((req, res) => { console.log('Add a book') })
|
|
||||||
.put((req, res) => { console.log('Update the book') });
|
|
||||||
```
|
|
||||||
|
|
||||||
### frontexpress.Router
|
|
||||||
|
|
||||||
Use the ```frontexpress.Router``` class to create modular, mountable route handlers.
|
|
||||||
|
|
||||||
Create a router file named ```birds.js``` in the app directory, with the following content:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import frontexpress from 'frontexpress';
|
|
||||||
|
|
||||||
const router = frontexpress.Router();
|
|
||||||
|
|
||||||
// middleware that is specific to this router
|
|
||||||
router.use((req, res, next) => {
|
|
||||||
console.log(`Time: ${Date.now()}`);
|
|
||||||
next();
|
|
||||||
});
|
|
||||||
|
|
||||||
// react on home page route
|
|
||||||
router.get('/', (req, res) => {
|
|
||||||
document.querySelector('.content').innerHTML = '<p>Birds home page</p>';
|
|
||||||
});
|
|
||||||
|
|
||||||
// react on about route
|
|
||||||
router.get('/about', (req, res) => {
|
|
||||||
document.querySelector('.content').innerHTML = '<p>About birds</p>';
|
|
||||||
});
|
|
||||||
|
|
||||||
export default router;
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, load the router module in the app:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import birds from './birds';
|
|
||||||
...
|
|
||||||
app.use('/birds', birds);
|
|
||||||
```
|
|
||||||
|
|
||||||
The app will now be able to react on requests (/birds) and (/birds/about)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
33
bower.json
Normal file
33
bower.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "frontexpress",
|
||||||
|
"description": "Frontexpress manages routes in browser like ExpressJS on Node",
|
||||||
|
"main": "dist/frontexpress.js",
|
||||||
|
"authors": [
|
||||||
|
"Camel Aissani <camel.aissani@gmail.com> (https://nuageprive.fr)"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"front",
|
||||||
|
"framework",
|
||||||
|
"web",
|
||||||
|
"router",
|
||||||
|
"middleware",
|
||||||
|
"app",
|
||||||
|
"api",
|
||||||
|
"express",
|
||||||
|
"frontexpress"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/camelaissani/frontexpress",
|
||||||
|
"ignore": [
|
||||||
|
"**/.*",
|
||||||
|
"index.js",
|
||||||
|
"gzipsize.js",
|
||||||
|
"rollup.config.dev.js",
|
||||||
|
"rollup.config.prod.js",
|
||||||
|
"node_modules",
|
||||||
|
"bower_components",
|
||||||
|
"test",
|
||||||
|
"tests",
|
||||||
|
"coverage"
|
||||||
|
]
|
||||||
|
}
|
109
docs/api.md
Normal file
109
docs/api.md
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
# API
|
||||||
|
|
||||||
|
| | Method | Short description |
|
||||||
|
| :------------- | :--------------| :----------------- |
|
||||||
|
|Frontexpress |||
|
||||||
|
||[frontexpress()](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpress-1)|Creates an instance of application|
|
||||||
|
||[frontexpress.Router()](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpressrouter)|Creates a Router object|
|
||||||
|
||[frontexpress.Middleware](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpressmiddleware)|Returns the Middleware class |
|
||||||
|
||||
|
||||||
|
|Application |||
|
||||||
|
||[set(setting, value)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationsetsetting-val)|Assigns a setting|
|
||||||
|
||[listen(callback)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationlistencallback)|Starts the application|
|
||||||
|
||[route(uri)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationrouteuri)|Gets a Router initialized with a root path|
|
||||||
|
||[use(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationuseuri-middleware)|Sets a middleware|
|
||||||
|
||||
|
||||||
|
||[get(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a GET request|
|
||||||
|
||[post(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a POST request|
|
||||||
|
||[put(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a PUT request|
|
||||||
|
||[delete(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a DELETE request|
|
||||||
|
||||
|
||||||
|
||[httpGet(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a GET ajax request|
|
||||||
|
||[httpPost(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a POST ajax request|
|
||||||
|
||[httpPut(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a PUT ajax request|
|
||||||
|
||[httpDelete(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a DELETE ajax request|
|
||||||
|
||||
|
||||||
|
|Router |||
|
||||||
|
||[use(middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routerusemiddleware)|Sets a middleware|
|
||||||
|
||[all(middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routerallmiddleware)|Sets a middleware on all HTTP method requests|
|
||||||
|
||||
|
||||||
|
||[get(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a GET request|
|
||||||
|
||[post(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a POST request|
|
||||||
|
||[put(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a PUT request|
|
||||||
|
||[delete(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a DELETE request|
|
||||||
|
||||
|
||||||
|
|Middleware |||
|
||||||
|
||[entered(request)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareenteredrequest)|Invoked by the app before an ajax request is sent|
|
||||||
|
||[exited(request)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareexitedrequest)|Invoked by the app before a new ajax request is sent|
|
||||||
|
||[updated(request, response)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareupdatedrequest-response)|Invoked by the app after an ajax request has responded|
|
||||||
|
||[failed(request, response)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewarefailedrequest-response)|Invoked by the app after an ajax request has failed|
|
||||||
|
||[next()](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewarenext)|Allows to break the middleware chain execution|
|
||||||
|
|
||||||
|
# middleware function
|
||||||
|
|
||||||
|
After registering a middleware function, the application invokes it with these parameters:
|
||||||
|
|
||||||
|
```js
|
||||||
|
(request, response, next) => {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**request**: `Object`, the ajax request information sent by the app
|
||||||
|
|
||||||
|
**response**: `Object`, the response of request
|
||||||
|
|
||||||
|
**next**: `Function`, the `next()` function to call to not break the middleware execution chain
|
||||||
|
|
||||||
|
# request object
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
method,
|
||||||
|
uri,
|
||||||
|
headers,
|
||||||
|
data,
|
||||||
|
history: {
|
||||||
|
state,
|
||||||
|
title,
|
||||||
|
uri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**method**: `String`, HTTP methods 'GET', 'POST'...
|
||||||
|
|
||||||
|
**uri**: `String`, path
|
||||||
|
|
||||||
|
**headers**: `Object`, custom HTTP headers
|
||||||
|
|
||||||
|
**data**: `Object`, data attached to the request
|
||||||
|
|
||||||
|
**history**: `Object`, object with properties state, title and uri
|
||||||
|
|
||||||
|
>**If the history object is set, it will activate the browser history management.** See [browser pushState() method](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_pushState()_method) for more information about state, title, and uri (url).
|
||||||
|
> uri and history.uri can be different.
|
||||||
|
|
||||||
|
**params**: `Object`, object containing the path parameters
|
||||||
|
|
||||||
|
# response object
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
status,
|
||||||
|
statusText,
|
||||||
|
responseText,
|
||||||
|
errorThrown,
|
||||||
|
errors
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**status**: `Number`, HTTP status 200, 404, 401, 500...
|
||||||
|
|
||||||
|
**statusText**: `String`
|
||||||
|
|
||||||
|
**responseText**: `String` response content
|
||||||
|
|
||||||
|
**errorThrown**: `Object` exception thrown (if request fails)
|
||||||
|
|
||||||
|
**errors**: `String` error description (if request fails)
|
133
docs/application.md
Normal file
133
docs/application.md
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
# Application
|
||||||
|
|
||||||
|
|
||||||
|
## Application.set(setting, val)
|
||||||
|
|
||||||
|
Assign `setting` to `val`, or return `setting`'s value.
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.set('foo', 'bar');
|
||||||
|
app.set('foo');
|
||||||
|
// => "bar"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**setting**: `String`, setting name
|
||||||
|
|
||||||
|
**val**: `*`, setting value
|
||||||
|
|
||||||
|
**Returns**: `app`, for chaining
|
||||||
|
|
||||||
|
|
||||||
|
## Application.listen(callback)
|
||||||
|
|
||||||
|
Listen to the DOM initialization and the browser history state changes.
|
||||||
|
|
||||||
|
The callback function is called once the DOM has
|
||||||
|
the `document.readyState` equals to 'interactive'.
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.listen(()=> {
|
||||||
|
console.log('App is listening requests');
|
||||||
|
console.log('DOM is ready!');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**callback**: `function`, DOM is ready callback
|
||||||
|
|
||||||
|
|
||||||
|
## Application.route(uri)
|
||||||
|
|
||||||
|
Create a new `Router` instance for the _uri_.
|
||||||
|
See the Router api docs for details.
|
||||||
|
|
||||||
|
```js
|
||||||
|
app.route('/');
|
||||||
|
// => new Router instance
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**uri**: `String`, path
|
||||||
|
|
||||||
|
**Returns**: `Router`, for chaining
|
||||||
|
|
||||||
|
|
||||||
|
## Application.use(uri, middleware)
|
||||||
|
|
||||||
|
Use the given middleware function or object, with optional _uri_.
|
||||||
|
Default _uri_ is "/".
|
||||||
|
|
||||||
|
```js
|
||||||
|
// middleware function will be applied on path "/"
|
||||||
|
app.use((req, res, next) => {console.log('Hello')});
|
||||||
|
|
||||||
|
// middleware object will be applied on path "/"
|
||||||
|
app.use(new Middleware());
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**uri**: `String`, path
|
||||||
|
|
||||||
|
**middleware**: `Middleware | function`, Middleware object or function
|
||||||
|
|
||||||
|
**Returns**: `app`, for chaining
|
||||||
|
|
||||||
|
|
||||||
|
## Application.get(uri, middleware), Application.post(uri, middleware)...
|
||||||
|
|
||||||
|
Use the given middleware function or object, with optional _uri_ on
|
||||||
|
HTTP methods: get, post, put, delete...
|
||||||
|
Default _uri_ is "/".
|
||||||
|
|
||||||
|
```js
|
||||||
|
// middleware function will be applied on path "/"
|
||||||
|
app.get((req, res, next) => {console.log('Hello')});
|
||||||
|
|
||||||
|
// middleware object will be applied on path "/" and
|
||||||
|
app.get(new Middleware());
|
||||||
|
|
||||||
|
// get a setting value
|
||||||
|
app.set('foo', 'bar');
|
||||||
|
app.get('foo');
|
||||||
|
// => "bar"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**uri**: `String`, path (or setting only for get method)
|
||||||
|
|
||||||
|
**middleware**: `Middleware | function`, Middleware object or function
|
||||||
|
|
||||||
|
**Returns**: `app`, for chaining
|
||||||
|
|
||||||
|
|
||||||
|
## Application.httpGet(request, success, failure), Application.httpPost(request, success, failure)...
|
||||||
|
|
||||||
|
Make an ajax request (get, post, put, delete...).
|
||||||
|
|
||||||
|
```js
|
||||||
|
// HTTP GET method
|
||||||
|
httpGet('/route1');
|
||||||
|
|
||||||
|
// HTTP GET method
|
||||||
|
httpGet({uri: '/route1', data: {'p1': 'val1'});
|
||||||
|
// uri invoked => /route1?p1=val1
|
||||||
|
|
||||||
|
// HTTP GET method with browser history management
|
||||||
|
httpGet({uri: '/api/users', history: {state: {foo: "bar"}, title: 'users page', uri: '/view/users'});
|
||||||
|
```
|
||||||
|
|
||||||
|
Samples above can be applied on other HTTP methods.
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**request**: `String | Object` uri or object containing uri, http headers, data, history
|
||||||
|
|
||||||
|
**success**: `Function` success callback
|
||||||
|
|
||||||
|
**failure**: `Function` failure callback
|
22
docs/frontexpress.md
Normal file
22
docs/frontexpress.md
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Frontexpress
|
||||||
|
|
||||||
|
|
||||||
|
## frontexpress()
|
||||||
|
|
||||||
|
Create a frontexpress application.
|
||||||
|
|
||||||
|
**Returns**: `Application`, the application
|
||||||
|
|
||||||
|
|
||||||
|
## frontexpress.Router()
|
||||||
|
|
||||||
|
Expose the Router constructor
|
||||||
|
|
||||||
|
**Returns**: `Router`
|
||||||
|
|
||||||
|
|
||||||
|
## frontexpress.Middleware
|
||||||
|
|
||||||
|
Expose the Middleware class
|
||||||
|
|
||||||
|
**Returns**: `Middleware`
|
63
docs/middleware.md
Normal file
63
docs/middleware.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Middleware
|
||||||
|
|
||||||
|
## Middleware.entered(request)
|
||||||
|
|
||||||
|
Invoked by the app before ajax request are sent or
|
||||||
|
during the DOM loading (document.readyState === 'loading').
|
||||||
|
See Application#_callMiddlewareEntered documentation for details.
|
||||||
|
|
||||||
|
Override this method to add your custom behaviour
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**request**: `Object`
|
||||||
|
|
||||||
|
|
||||||
|
## Middleware.exited(request)
|
||||||
|
|
||||||
|
Invoked by the app before a new ajax request is sent or before the DOM unloading.
|
||||||
|
See Application#_callMiddlewareExited documentation for details.
|
||||||
|
|
||||||
|
Override this method to add your custom behaviour
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**request**: `Object`
|
||||||
|
|
||||||
|
|
||||||
|
## Middleware.updated(request, response)
|
||||||
|
|
||||||
|
Invoked on ajax request responding or on DOM ready
|
||||||
|
(document.readyState === 'interactive').
|
||||||
|
See Application#_callMiddlewareUpdated documentation for details.
|
||||||
|
|
||||||
|
Override this method to add your custom behaviour
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**request**: `Object`
|
||||||
|
|
||||||
|
**response**: `Object`
|
||||||
|
|
||||||
|
|
||||||
|
## Middleware.failed(request, response)
|
||||||
|
|
||||||
|
Invoked when ajax request fails.
|
||||||
|
|
||||||
|
Override this method to add your custom behaviour
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**request**: `Object`
|
||||||
|
|
||||||
|
**response**: `Object`
|
||||||
|
|
||||||
|
|
||||||
|
## Middleware.next()
|
||||||
|
|
||||||
|
Allow the hand over to the next middleware object or function.
|
||||||
|
|
||||||
|
Override this method and return `false` to break execution of
|
||||||
|
middleware chain.
|
||||||
|
|
||||||
|
**Returns**: `Boolean`, `true` by default
|
69
docs/router.md
Normal file
69
docs/router.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# Router
|
||||||
|
|
||||||
|
|
||||||
|
## Router.use(middleware)
|
||||||
|
|
||||||
|
Use the given middleware function or object on this router.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// middleware function
|
||||||
|
router.use((req, res, next) => {console.log('Hello')});
|
||||||
|
|
||||||
|
// middleware object
|
||||||
|
router.use(new Middleware());
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**middleware**: `Middleware | function`, Middleware object or function
|
||||||
|
|
||||||
|
**Returns**: `Router`, for chaining
|
||||||
|
|
||||||
|
|
||||||
|
## Router.all(middleware)
|
||||||
|
|
||||||
|
Use the given middleware function or object on this router for
|
||||||
|
all HTTP methods.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// middleware function
|
||||||
|
router.all((req, res, next) => {console.log('Hello')});
|
||||||
|
|
||||||
|
// middleware object
|
||||||
|
router.all(new Middleware());
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**middleware**: `Middleware | function`, Middleware object or function
|
||||||
|
|
||||||
|
**Returns**: `Router`, for chaining
|
||||||
|
|
||||||
|
|
||||||
|
## Router.get(uri, middleware), Router.post(uri, middleware)...
|
||||||
|
|
||||||
|
Use the given middleware function or object, with optional _uri_ on
|
||||||
|
HTTP methods: get, post, put, delete...
|
||||||
|
Default _uri_ is "/".
|
||||||
|
|
||||||
|
```js
|
||||||
|
// middleware function will be applied on path "/"
|
||||||
|
router.get((req, res, next) => {console.log('Hello')});
|
||||||
|
|
||||||
|
// middleware object will be applied on path "/" and
|
||||||
|
router.get(new Middleware());
|
||||||
|
|
||||||
|
// middleware function will be applied on path "/user"
|
||||||
|
router.post('/user', (req, res, next) => {console.log('Hello')});
|
||||||
|
|
||||||
|
// middleware object will be applied on path "/user" and
|
||||||
|
router.post('/user', new Middleware());
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
**uri**: `String`, path
|
||||||
|
|
||||||
|
**middleware**: `Middleware | function`, Middleware object or function
|
||||||
|
|
||||||
|
**Returns**: `Router`, for chaining
|
1264
frontexpress.js
Normal file
1264
frontexpress.js
Normal file
File diff suppressed because it is too large
Load Diff
2
frontexpress.min.js
vendored
Normal file
2
frontexpress.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
frontexpress.min.js.map
Normal file
1
frontexpress.min.js.map
Normal file
File diff suppressed because one or more lines are too long
5
gzipsize.js
Normal file
5
gzipsize.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import bytesize from 'bytesize';
|
||||||
|
|
||||||
|
bytesize.gzipSize(__dirname + '/frontexpress.min.js', true, (err, size) => {
|
||||||
|
console.log(`frontexpress size: ${size}(min+gzip)`);
|
||||||
|
});
|
1
index.js
Normal file
1
index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
import frontexpress from 'expose?frontexpress!./lib/frontexpress.js';
|
@ -1,199 +1,292 @@
|
|||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
import HTTP_METHODS from './methods';
|
import HTTP_METHODS from './methods';
|
||||||
import Settings from './settings';
|
import Settings from './settings';
|
||||||
import Router, {Route} from './router';
|
import Router, {Route} from './router';
|
||||||
import Middleware from './middleware';
|
import Middleware from './middleware';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Application class.
|
||||||
|
*/
|
||||||
|
|
||||||
export default class Application {
|
export default class Application {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the application.
|
||||||
|
*
|
||||||
|
* - setup default configuration
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.routers = [];
|
this.routers = [];
|
||||||
this.DOMLoading = false;
|
|
||||||
this.settings = new Settings();
|
this.settings = new Settings();
|
||||||
|
this.plugins = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
// Settings
|
/**
|
||||||
set(name, value) {
|
* Assign `setting` to `val`, or return `setting`'s value.
|
||||||
this.settings.set(name, value);
|
*
|
||||||
|
* app.set('foo', 'bar');
|
||||||
|
* app.set('foo');
|
||||||
|
* // => "bar"
|
||||||
|
*
|
||||||
|
* @param {String} setting
|
||||||
|
* @param {*} [val]
|
||||||
|
* @return {app} for chaining
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
set(...args) {
|
||||||
|
// get behaviour
|
||||||
|
if (args.length === 1) {
|
||||||
|
return this.settings.get([args]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set behaviour
|
||||||
|
this.settings.set(...args);
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
// Start listening requests
|
/**
|
||||||
|
* Listen for DOM initialization and history state changes.
|
||||||
|
*
|
||||||
|
* The callback function is called once the DOM has
|
||||||
|
* the `document.readyState` equals to 'interactive'.
|
||||||
|
*
|
||||||
|
* app.listen(()=> {
|
||||||
|
* console.log('App is listening requests');
|
||||||
|
* console.log('DOM is ready!');
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param {Function} callback
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
listen(callback) {
|
listen(callback) {
|
||||||
document.onreadystatechange = () => {
|
const request = {method: 'GET', uri: window.location.pathname + window.location.search};
|
||||||
const uri = window.location.pathname + window.location.search;
|
const response = {status: 200, statusText: 'OK'};
|
||||||
const method = 'GET';
|
const currentRoutes = this._routes(request);
|
||||||
const request = {method, uri};
|
|
||||||
const response = {status: 200, statusText: 'OK'};
|
|
||||||
|
|
||||||
// gathers all routes impacted by the current browser location
|
this._callMiddlewareMethod('entered', currentRoutes, request);
|
||||||
const currentRoutes = this._routes(uri, method);
|
|
||||||
|
|
||||||
// listen dom events
|
// manage history
|
||||||
if (document.readyState === 'loading') {
|
window.onpopstate = (event) => {
|
||||||
this.DOMLoading = true;
|
if (event.state) {
|
||||||
this._callMiddlewareEntered(currentRoutes, request);
|
const {request, response} = event.state;
|
||||||
} else if (document.readyState === 'interactive') {
|
[
|
||||||
if (!this.DOMLoading) {
|
'exited',
|
||||||
this._callMiddlewareEntered(currentRoutes, request);
|
'entered',
|
||||||
}
|
'updated'
|
||||||
this._callMiddlewareUpdated(currentRoutes, request, response);
|
].forEach(middlewareMethod => this._callMiddlewareMethod(middlewareMethod, this._routes(request), request, response));
|
||||||
if (callback) {
|
|
||||||
callback(request, response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener('beforeunload', () => {
|
// manage page loading/refreshing
|
||||||
this._callMiddlewareExited();
|
window.onbeforeunload = () => {
|
||||||
});
|
this._callMiddlewareMethod('exited');
|
||||||
|
};
|
||||||
|
|
||||||
|
const whenPageIsInteractiveFn = () => {
|
||||||
|
this.plugins.forEach(pluginObject => pluginObject.plugin(this));
|
||||||
|
this._callMiddlewareMethod('updated', currentRoutes, request, response);
|
||||||
|
if (callback) {
|
||||||
|
callback(request, response);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.onreadystatechange = () => {
|
||||||
|
// DOM ready state
|
||||||
|
if (document.readyState === 'interactive') {
|
||||||
|
whenPageIsInteractiveFn();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
|
||||||
|
whenPageIsInteractiveFn();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
|
||||||
// Routes
|
/**
|
||||||
|
* Returns a new `Router` instance for the _uri_.
|
||||||
|
* See the Router api docs for details.
|
||||||
|
*
|
||||||
|
* app.route('/');
|
||||||
|
* // => new Router instance
|
||||||
|
*
|
||||||
|
* @param {String} uri
|
||||||
|
* @return {Router} for chaining
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
route(uri) {
|
route(uri) {
|
||||||
const router = new Router(uri);
|
const router = new Router(uri);
|
||||||
this.routers.push(router);
|
this.routers.push(router);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the given middleware function or object, with optional _uri_.
|
||||||
|
* Default _uri_ is "/".
|
||||||
|
* Or use the given plugin
|
||||||
|
*
|
||||||
|
* // middleware function will be applied on path "/"
|
||||||
|
* app.use((req, res, next) => {console.log('Hello')});
|
||||||
|
*
|
||||||
|
* // middleware object will be applied on path "/"
|
||||||
|
* app.use(new Middleware());
|
||||||
|
*
|
||||||
|
* // use a plugin
|
||||||
|
* app.use({
|
||||||
|
* name: 'My plugin name',
|
||||||
|
* plugin(application) {
|
||||||
|
* // here plugin implementation
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* @param {String} uri
|
||||||
|
* @param {Middleware|Function|plugin} middleware object, middleware function, plugin
|
||||||
|
* @return {app} for chaining
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
use(...args) {
|
use(...args) {
|
||||||
if (args.length === 0) {
|
let {baseUri, router, middleware, plugin} = toParameters(args);
|
||||||
throw new TypeError('use method takes at least a middleware or a router');
|
if (plugin) {
|
||||||
}
|
this.plugins.push(plugin);
|
||||||
|
|
||||||
let baseUri, middleware, router, which;
|
|
||||||
|
|
||||||
if (args.length === 1) {
|
|
||||||
[which,] = args;
|
|
||||||
} else {
|
} else {
|
||||||
[baseUri, which,] = args;
|
if (router) {
|
||||||
}
|
router.baseUri = baseUri;
|
||||||
|
} else if (middleware) {
|
||||||
if (!(which instanceof Middleware) && (typeof which !== 'function') && !(which instanceof Router)) {
|
router = new Router(baseUri);
|
||||||
throw new TypeError('use method takes at least a middleware or a router');
|
HTTP_METHODS.forEach((method) => {
|
||||||
}
|
router[method.toLowerCase()](middleware);
|
||||||
|
});
|
||||||
if (which instanceof Router) {
|
|
||||||
router = which;
|
|
||||||
router.baseUri = baseUri;
|
|
||||||
} else {
|
|
||||||
middleware = which;
|
|
||||||
router = new Router(baseUri);
|
|
||||||
for (const method of HTTP_METHODS) {
|
|
||||||
router[method.toLowerCase()](middleware);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.routers.push(router);
|
|
||||||
}
|
|
||||||
|
|
||||||
_routes(uri, method) {
|
|
||||||
const currentRoutes = [];
|
|
||||||
for (const router of this.routers) {
|
|
||||||
const routes = router.routes(uri, method);
|
|
||||||
currentRoutes.push(...routes);
|
|
||||||
}
|
|
||||||
return currentRoutes;
|
|
||||||
}
|
|
||||||
|
|
||||||
_callMiddlewareEntered(currentRoutes, request) {
|
|
||||||
for (const route of currentRoutes) {
|
|
||||||
if (route.middleware.entered) {
|
|
||||||
route.middleware.entered(request);
|
|
||||||
}
|
|
||||||
if (route.middleware.next && !route.middleware.next()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_callMiddlewareUpdated(currentRoutes, request, response) {
|
|
||||||
for (const route of currentRoutes) {
|
|
||||||
route.visited = request;
|
|
||||||
// calls middleware updated method
|
|
||||||
if (route.middleware.updated) {
|
|
||||||
route.middleware.updated(request, response);
|
|
||||||
if (route.middleware.next && !route.middleware.next()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// calls middleware method
|
throw new TypeError('method takes at least a middleware or a router');
|
||||||
|
}
|
||||||
|
this.routers.push(router);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather routes from all routers filtered by _uri_ and HTTP _method_.
|
||||||
|
* See Router#routes() documentation for details.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
_routes(request) {
|
||||||
|
return this.routers.reduce((acc, router) => {
|
||||||
|
acc.push(...router.routes(this, request));
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call `Middleware` method or middleware function on _currentRoutes_.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
_callMiddlewareMethod(meth, currentRoutes, request, response) {
|
||||||
|
if (meth === 'exited') {
|
||||||
|
// currentRoutes, request, response params not needed
|
||||||
|
this.routers.forEach((router) => {
|
||||||
|
router.visited().forEach((route) => {
|
||||||
|
if (route.middleware.exited) {
|
||||||
|
route.middleware.exited(route.visited);
|
||||||
|
route.visited = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentRoutes.some((route) => {
|
||||||
|
if (meth === 'updated') {
|
||||||
|
route.visited = request;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.middleware[meth]) {
|
||||||
|
route.middleware[meth](request, response);
|
||||||
|
if (route.middleware.next && !route.middleware.next()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (meth !== 'entered') {
|
||||||
|
// calls middleware method
|
||||||
let breakMiddlewareLoop = true;
|
let breakMiddlewareLoop = true;
|
||||||
const next = () => {
|
const next = () => {
|
||||||
breakMiddlewareLoop = false;
|
breakMiddlewareLoop = false;
|
||||||
};
|
};
|
||||||
route.middleware(request, response, next);
|
route.middleware(request, response, next);
|
||||||
if (breakMiddlewareLoop) {
|
if (breakMiddlewareLoop) {
|
||||||
break;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_callMiddlewareExited() {
|
|
||||||
// calls middleware exited method
|
|
||||||
for (const router of this.routers) {
|
|
||||||
const routes = router.visited();
|
|
||||||
for (const route of routes) {
|
|
||||||
if (route.middleware.exited) {
|
|
||||||
route.middleware.exited(route.visited);
|
|
||||||
route.visited = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_callMiddlewareFailed(currentRoutes, request, response) {
|
/**
|
||||||
for (const route of currentRoutes) {
|
* Make an ajax request. Manage History#pushState if history object set.
|
||||||
// calls middleware failed method
|
*
|
||||||
if (route.middleware.failed) {
|
* @private
|
||||||
route.middleware.failed(request, response);
|
*/
|
||||||
if (route.middleware.next && !route.middleware.next()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// calls middleware method
|
|
||||||
let breakMiddlewareLoop = true;
|
|
||||||
const next = () => {
|
|
||||||
breakMiddlewareLoop = false;
|
|
||||||
};
|
|
||||||
route.middleware(request, response, next);
|
|
||||||
if (breakMiddlewareLoop) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
_fetch(req, resolve, reject) {
|
||||||
// Ajax request
|
let {method, uri, headers, data, history} = req;
|
||||||
_fetch({method, uri, headers, data}, resolve, reject) {
|
|
||||||
|
|
||||||
const httpMethodTransformer = this.get(`http ${method} transformer`);
|
const httpMethodTransformer = this.get(`http ${method} transformer`);
|
||||||
if (httpMethodTransformer) {
|
if (httpMethodTransformer) {
|
||||||
uri = httpMethodTransformer.uri ? httpMethodTransformer.uri({uri, headers, data}) : uri;
|
const {uri: _uriFn, headers: _headersFn, data: _dataFn } = httpMethodTransformer;
|
||||||
headers = httpMethodTransformer.headers ? httpMethodTransformer.headers({uri, headers, data}) : headers;
|
req.uri = _uriFn ? _uriFn({uri, headers, data}) : uri;
|
||||||
data = httpMethodTransformer.data ? httpMethodTransformer.data({uri, headers, data}) : data;
|
req.headers = _headersFn ? _headersFn({uri, headers, data}) : headers;
|
||||||
|
req.data = _dataFn ? _dataFn({uri, headers, data}) : data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// calls middleware exited method
|
// calls middleware exited method
|
||||||
this._callMiddlewareExited();
|
this._callMiddlewareMethod('exited');
|
||||||
|
|
||||||
// gathers all routes impacted by the uri
|
// gathers all routes impacted by the uri
|
||||||
const currentRoutes = this._routes(uri, method);
|
const currentRoutes = this._routes(req);
|
||||||
|
|
||||||
// calls middleware entered method
|
// calls middleware entered method
|
||||||
this._callMiddlewareEntered(currentRoutes, {method, uri, headers, data});
|
this._callMiddlewareMethod('entered', currentRoutes, req);
|
||||||
|
|
||||||
// invokes http request
|
// invokes http request
|
||||||
this.settings.get('http requester').fetch({method, uri, headers, data},
|
this.settings.get('http requester').fetch(req,
|
||||||
(request, response) => {
|
(request, response) => {
|
||||||
this._callMiddlewareUpdated(currentRoutes, request, response);
|
if (history) {
|
||||||
|
window.history.pushState({request, response}, history.title, history.uri);
|
||||||
|
}
|
||||||
|
this._callMiddlewareMethod('updated', currentRoutes, request, response);
|
||||||
if (resolve) {
|
if (resolve) {
|
||||||
resolve(request, response);
|
resolve(request, response);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(request, response) => {
|
(request, response) => {
|
||||||
this._callMiddlewareFailed(currentRoutes, request, response);
|
this._callMiddlewareMethod('failed', currentRoutes, request, response);
|
||||||
if (reject) {
|
if (reject) {
|
||||||
reject(request, response);
|
reject(request, response);
|
||||||
}
|
}
|
||||||
@ -202,45 +295,70 @@ export default class Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
HTTP_METHODS.reduce((reqProto, method) => {
|
HTTP_METHODS.reduce((reqProto, method) => {
|
||||||
// Middleware methods
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the given middleware function or object, with optional _uri_ on
|
||||||
|
* HTTP methods: get, post, put, delete...
|
||||||
|
* Default _uri_ is "/".
|
||||||
|
*
|
||||||
|
* // middleware function will be applied on path "/"
|
||||||
|
* app.get((req, res, next) => {console.log('Hello')});
|
||||||
|
*
|
||||||
|
* // middleware object will be applied on path "/" and
|
||||||
|
* app.get(new Middleware());
|
||||||
|
*
|
||||||
|
* // get a setting value
|
||||||
|
* app.set('foo', 'bar');
|
||||||
|
* app.get('foo');
|
||||||
|
* // => "bar"
|
||||||
|
*
|
||||||
|
* @param {String} uri or setting
|
||||||
|
* @param {Middleware|Function} middleware object or function
|
||||||
|
* @return {app} for chaining
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
const middlewareMethodName = method.toLowerCase();
|
const middlewareMethodName = method.toLowerCase();
|
||||||
reqProto[middlewareMethodName] = function(...args) {
|
reqProto[middlewareMethodName] = function(...args) {
|
||||||
if (middlewareMethodName === 'get') {
|
let {baseUri, middleware, which} = toParameters(args);
|
||||||
if (args.length === 0) {
|
if (middlewareMethodName === 'get' && typeof which === 'string') {
|
||||||
throw new TypeError(`${middlewareMethodName} method takes at least a string or a middleware`);
|
return this.settings.get(which);
|
||||||
} else if (args.length === 1) {
|
|
||||||
const [name] = args;
|
|
||||||
if (typeof name === 'string') {
|
|
||||||
return this.settings.get(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (args.length === 0) {
|
|
||||||
throw new TypeError(`${middlewareMethodName} method takes at least a middleware`);
|
|
||||||
}
|
}
|
||||||
|
if (!middleware) {
|
||||||
let baseUri, middleware, which;
|
throw new TypeError(`method takes a middleware ${middlewareMethodName === 'get' ? 'or a string' : ''}`);
|
||||||
|
|
||||||
if (args.length === 1) {
|
|
||||||
[which,] = args;
|
|
||||||
} else {
|
|
||||||
[baseUri, which,] = args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(which instanceof Middleware) && (typeof which !== 'function')) {
|
|
||||||
throw new TypeError(`${middlewareMethodName} method takes at least a middleware`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
middleware = which;
|
|
||||||
router[middlewareMethodName](baseUri, middleware);
|
router[middlewareMethodName](baseUri, middleware);
|
||||||
|
|
||||||
this.routers.push(router);
|
this.routers.push(router);
|
||||||
|
|
||||||
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
// HTTP methods
|
/**
|
||||||
|
* Ajax request (get, post, put, delete...).
|
||||||
|
*
|
||||||
|
* // HTTP GET method
|
||||||
|
* httpGet('/route1');
|
||||||
|
*
|
||||||
|
* // HTTP GET method
|
||||||
|
* httpGet({uri: '/route1', data: {'p1': 'val1'});
|
||||||
|
* // uri invoked => /route1?p1=val1
|
||||||
|
*
|
||||||
|
* // HTTP GET method with browser history management
|
||||||
|
* httpGet({uri: '/api/users', history: {state: {foo: "bar"}, title: 'users page', uri: '/view/users'});
|
||||||
|
*
|
||||||
|
* Samples above can be applied on other HTTP methods.
|
||||||
|
*
|
||||||
|
* @param {String|Object} uri or object containing uri, http headers, data, history
|
||||||
|
* @param {Function} success callback
|
||||||
|
* @param {Function} failure callback
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
const httpMethodName = 'http'+method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
const httpMethodName = 'http'+method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
||||||
reqProto[httpMethodName] = function(request, resolve, reject) {
|
reqProto[httpMethodName] = function(request, resolve, reject) {
|
||||||
let {uri, headers, data} = request;
|
let {uri, headers, data, history} = request;
|
||||||
if (!uri) {
|
if (!uri) {
|
||||||
uri = request;
|
uri = request;
|
||||||
}
|
}
|
||||||
@ -248,9 +366,27 @@ HTTP_METHODS.reduce((reqProto, method) => {
|
|||||||
uri,
|
uri,
|
||||||
method,
|
method,
|
||||||
headers,
|
headers,
|
||||||
data
|
data,
|
||||||
|
history
|
||||||
}, resolve, reject);
|
}, resolve, reject);
|
||||||
};
|
};
|
||||||
|
|
||||||
return reqProto;
|
return reqProto;
|
||||||
}, Application.prototype);
|
}, Application.prototype);
|
||||||
|
|
||||||
|
|
||||||
|
export function toParameters(args) {
|
||||||
|
let baseUri, middleware, router, plugin, which;
|
||||||
|
|
||||||
|
args.length === 1 ? [which,] = args : [baseUri, which,] = args;
|
||||||
|
|
||||||
|
if (which instanceof Router) {
|
||||||
|
router = which;
|
||||||
|
} else if (which instanceof Middleware || typeof which === 'function') {
|
||||||
|
middleware = which;
|
||||||
|
} else if(which && which.plugin && typeof which.plugin === 'function') {
|
||||||
|
plugin = which;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {baseUri, middleware, router, plugin, which};
|
||||||
|
}
|
||||||
|
@ -1,9 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
*/
|
||||||
|
|
||||||
import Application from './application';
|
import Application from './application';
|
||||||
import Router from './router';
|
import Router from './router';
|
||||||
import Middleware from './middleware';
|
import Middleware from './middleware';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a frontexpress application.
|
||||||
|
*
|
||||||
|
* @return {Function}
|
||||||
|
* @api public
|
||||||
|
*/
|
||||||
|
|
||||||
const frontexpress = () => new Application();
|
const frontexpress = () => new Application();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expose Router, Middleware constructors.
|
||||||
|
*/
|
||||||
frontexpress.Router = (baseUri) => new Router(baseUri);
|
frontexpress.Router = (baseUri) => new Router(baseUri);
|
||||||
frontexpress.Middleware = (name) => new Middleware(name);
|
frontexpress.Middleware = Middleware;
|
||||||
|
|
||||||
export default frontexpress;
|
export default frontexpress;
|
||||||
|
@ -1 +1,8 @@
|
|||||||
export default ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'];
|
/**
|
||||||
|
* HTTP method list
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
||||||
|
// not supported yet
|
||||||
|
// HEAD', 'CONNECT', 'OPTIONS', 'TRACE';
|
||||||
|
@ -1,19 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* Middleware object.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
export default class Middleware {
|
export default class Middleware {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Middleware initialization
|
||||||
|
*
|
||||||
|
* @param {String} middleware name
|
||||||
|
*/
|
||||||
|
|
||||||
constructor(name='') {
|
constructor(name='') {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
entered(request) {
|
/**
|
||||||
}
|
* Invoked by the app before an ajax request is sent or
|
||||||
|
* during the DOM loading (document.readyState === 'loading').
|
||||||
|
* See Application#_callMiddlewareEntered documentation for details.
|
||||||
|
*
|
||||||
|
* Override this method to add your custom behaviour
|
||||||
|
*
|
||||||
|
* @param {Object} request
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
exited(request) {
|
entered(request) { }
|
||||||
}
|
|
||||||
|
|
||||||
updated(request, response) {
|
|
||||||
}
|
|
||||||
|
|
||||||
failed(request, response) {
|
/**
|
||||||
}
|
* Invoked by the app before a new ajax request is sent or before the DOM is unloaded.
|
||||||
|
* See Application#_callMiddlewareExited documentation for details.
|
||||||
|
*
|
||||||
|
* Override this method to add your custom behaviour
|
||||||
|
*
|
||||||
|
* @param {Object} request
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
exited(request) { }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked by the app after an ajax request has responded or on DOM ready
|
||||||
|
* (document.readyState === 'interactive').
|
||||||
|
* See Application#_callMiddlewareUpdated documentation for details.
|
||||||
|
*
|
||||||
|
* Override this method to add your custom behaviour
|
||||||
|
*
|
||||||
|
* @param {Object} request
|
||||||
|
* @param {Object} response
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
updated(request, response) { }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked by the app when an ajax request has failed.
|
||||||
|
*
|
||||||
|
* Override this method to add your custom behaviour
|
||||||
|
*
|
||||||
|
* @param {Object} request
|
||||||
|
* @param {Object} response
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
failed(request, response) { }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow the hand over to the next middleware object or function.
|
||||||
|
*
|
||||||
|
* Override this method and return `false` to break execution of
|
||||||
|
* middleware chain.
|
||||||
|
*
|
||||||
|
* @return {Boolean} `true` by default
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
next() {
|
next() {
|
||||||
return true;
|
return true;
|
||||||
|
101
lib/requester.js
101
lib/requester.js
@ -1,24 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
export default class Requester {
|
export default class Requester {
|
||||||
|
|
||||||
fetch({method, uri, headers, data}, resolve, reject) {
|
/**
|
||||||
|
* Make an ajax request.
|
||||||
|
*
|
||||||
|
* @param {Object} request
|
||||||
|
* @param {Function} success callback
|
||||||
|
* @param {Function} failure callback
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
fetch(request, resolve, reject) {
|
||||||
|
const {method, uri, headers, data} = request;
|
||||||
|
|
||||||
const success = (responseText) => {
|
const success = (responseText) => {
|
||||||
resolve(
|
resolve(
|
||||||
{method, uri, headers, data},
|
request,
|
||||||
{status: 200, statusText: 'OK', responseText}
|
{
|
||||||
|
status: 200,
|
||||||
|
statusText: 'OK',
|
||||||
|
responseText
|
||||||
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fail = ({status, statusText, errorThrown}) => {
|
const fail = ({status, statusText, errorThrown}) => {
|
||||||
const errors = this._analyzeErrors({status, statusText, errorThrown});
|
|
||||||
reject(
|
reject(
|
||||||
{method, uri, headers, data},
|
request,
|
||||||
{status, statusText, errorThrown, errors}
|
{
|
||||||
|
status,
|
||||||
|
statusText,
|
||||||
|
errorThrown,
|
||||||
|
errors: `HTTP ${status} ${statusText?statusText:''}`
|
||||||
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const xmlhttp = new XMLHttpRequest();
|
const xmlhttp = new XMLHttpRequest();
|
||||||
xmlhttp.onreadystatechange = () => {
|
xmlhttp.onreadystatechange = () => {
|
||||||
if (xmlhttp.readyState === 4) {
|
if (xmlhttp.readyState === 4) { //XMLHttpRequest.DONE
|
||||||
if (xmlhttp.status === 200) {
|
if (xmlhttp.status === 200) {
|
||||||
success(xmlhttp.responseText);
|
success(xmlhttp.responseText);
|
||||||
} else {
|
} else {
|
||||||
@ -29,9 +53,9 @@ export default class Requester {
|
|||||||
try {
|
try {
|
||||||
xmlhttp.open(method, uri, true);
|
xmlhttp.open(method, uri, true);
|
||||||
if (headers) {
|
if (headers) {
|
||||||
for (const header of Object.keys(headers)) {
|
Object.keys(headers).forEach((header) => {
|
||||||
xmlhttp.setRequestHeader(header, headers[header]);
|
xmlhttp.setRequestHeader(header, headers[header]);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
if (data) {
|
if (data) {
|
||||||
xmlhttp.send(data);
|
xmlhttp.send(data);
|
||||||
@ -42,38 +66,35 @@ export default class Requester {
|
|||||||
fail({errorThrown});
|
fail({errorThrown});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_analyzeErrors(response) {
|
export const httpGetTransformer = {
|
||||||
// manage exceptions
|
uri({uri, headers, data}) {
|
||||||
if (response.errorThrown) {
|
if (!data) {
|
||||||
if (response.errorThrown.name === 'SyntaxError') {
|
return uri;
|
||||||
return 'Problem during data decoding [JSON]';
|
|
||||||
}
|
|
||||||
if (response.errorThrown.name === 'TimeoutError') {
|
|
||||||
return 'Server is taking too long to reply';
|
|
||||||
}
|
|
||||||
if (response.errorThrown.name === 'AbortError') {
|
|
||||||
return 'Request cancelled on server';
|
|
||||||
}
|
|
||||||
if (response.errorThrown.name === 'NetworkError') {
|
|
||||||
return 'A network error occurred';
|
|
||||||
}
|
|
||||||
throw response.errorThrown;
|
|
||||||
}
|
}
|
||||||
|
let [uriWithoutAnchor, anchor] = [uri, ''];
|
||||||
// manage status
|
const match = /^(.*)(#.*)$/.exec(uri);
|
||||||
if (response.status === 0) {
|
if (match) {
|
||||||
return 'Server access problem. Check your network connection';
|
[,uriWithoutAnchor, anchor] = /^(.*)(#.*)$/.exec(uri);
|
||||||
}
|
}
|
||||||
if (response.status === 401) {
|
uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {
|
||||||
return 'Your session has expired, Please reconnect. [code: 401]';
|
gUri += `${(index === 0 && gUri.indexOf('?') === -1)?'?':'&'}${d}=${data[d]}`;
|
||||||
}
|
return gUri;
|
||||||
if (response.status === 404) {
|
}, uriWithoutAnchor);
|
||||||
return 'Page not found on server. [code: 404]';
|
return uriWithoutAnchor + anchor;
|
||||||
}
|
|
||||||
if (response.status === 500) {
|
|
||||||
return 'Internal server error. [code: 500]';
|
|
||||||
}
|
|
||||||
return `Unknown error. ${response.statusText?response.statusText:''}`;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
// export const httpPostTransformer = {
|
||||||
|
// headers({uri, headers, data}) {
|
||||||
|
// if (!data) {
|
||||||
|
// return headers;
|
||||||
|
// }
|
||||||
|
// const updatedHeaders = headers || {};
|
||||||
|
// if (!updatedHeaders['Content-Type']) {
|
||||||
|
// updatedHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||||
|
// }
|
||||||
|
// return updatedHeaders;
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
312
lib/router.js
312
lib/router.js
@ -1,7 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
import HTTP_METHODS from './methods';
|
import HTTP_METHODS from './methods';
|
||||||
|
import {toParameters} from './application';
|
||||||
import Middleware from './middleware';
|
import Middleware from './middleware';
|
||||||
|
|
||||||
class Route {
|
|
||||||
|
/**
|
||||||
|
* Route object.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class Route {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the route.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
constructor(router, uriPart, method, middleware) {
|
constructor(router, uriPart, method, middleware) {
|
||||||
this.router = router;
|
this.router = router;
|
||||||
this.uriPart = uriPart;
|
this.uriPart = uriPart;
|
||||||
@ -10,6 +30,13 @@ class Route {
|
|||||||
this.visited = false;
|
this.visited = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return route's uri.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
get uri() {
|
get uri() {
|
||||||
if (!this.uriPart && !this.method) {
|
if (!this.uriPart && !this.method) {
|
||||||
return undefined;
|
return undefined;
|
||||||
@ -23,26 +50,46 @@ class Route {
|
|||||||
return this.router.baseUri;
|
return this.router.baseUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.router.baseUri && this.uriPart) {
|
|
||||||
return (this.router.baseUri.trim() + this.uriPart.trim()).replace(/\/{2,}/, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.router.baseUri) {
|
if (this.router.baseUri) {
|
||||||
return this.router.baseUri.trim();
|
const baseUri = this.router.baseUri.trim();
|
||||||
|
if (this.uriPart) {
|
||||||
|
return ( baseUri + this.uriPart.trim()).replace(/\/{2,}/, '/');
|
||||||
|
}
|
||||||
|
return baseUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.uriPart;
|
return this.uriPart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Router object.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
const error_middleware_message = 'method takes at least a middleware';
|
||||||
export default class Router {
|
export default class Router {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the router.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
constructor(uri) {
|
constructor(uri) {
|
||||||
if (uri) {
|
this._baseUri = uri;
|
||||||
this._baseUri = uri;
|
|
||||||
}
|
|
||||||
this._routes = [];
|
this._routes = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do some checks and set _baseUri.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
set baseUri(uri) {
|
set baseUri(uri) {
|
||||||
if (!uri) {
|
if (!uri) {
|
||||||
return;
|
return;
|
||||||
@ -53,68 +100,79 @@ export default class Router {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._baseUri instanceof RegExp) {
|
if (typeof this._baseUri !== typeof uri) {
|
||||||
throw new TypeError(`the router already contains a regexp uri ${this._baseUri.toString()} It cannot be mixed with ${uri.toString()}`);
|
throw new TypeError('router cannot mix regexp and uri');
|
||||||
}
|
|
||||||
|
|
||||||
if (uri instanceof RegExp) {
|
|
||||||
throw new TypeError(`the router already contains an uri ${this._baseUri.toString()} It cannot be mixed with regexp ${uri.toString()}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return router's _baseUri.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
get baseUri() {
|
get baseUri() {
|
||||||
return this._baseUri;
|
return this._baseUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a route to the router.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
_add(route) {
|
_add(route) {
|
||||||
this._routes.push(route);
|
this._routes.push(route);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
routes(uri, method) {
|
|
||||||
|
/**
|
||||||
|
* Gather routes from routers filtered by _uri_ and HTTP _method_.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
routes(application, request) {
|
||||||
|
request.params = request.params || {};
|
||||||
|
const isRouteMatch = application.get('route matcher');
|
||||||
return this._routes.filter((route) => {
|
return this._routes.filter((route) => {
|
||||||
if (!route.uri && !route.method) {
|
return isRouteMatch(request, route);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (route.method !== method) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!route.uri) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let uriToCheck = uri;
|
|
||||||
|
|
||||||
//remove query string from uri to test
|
|
||||||
const questionMarkIndex = uriToCheck.indexOf('?');
|
|
||||||
if (questionMarkIndex >= 0) {
|
|
||||||
uriToCheck = uriToCheck.slice(0, questionMarkIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove anchor from uri to test
|
|
||||||
const hashIndex = uriToCheck.indexOf('#');
|
|
||||||
if (hashIndex >= 0) {
|
|
||||||
uriToCheck = uriToCheck.slice(0, hashIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (route.uri instanceof RegExp) {
|
|
||||||
return uriToCheck.match(route.uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
return route.uri === uriToCheck;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather visited routes from routers.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
visited() {
|
visited() {
|
||||||
return this._routes.filter((route) => {
|
return this._routes.filter(route => route.visited);
|
||||||
return route.visited;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the given middleware function or object on this router.
|
||||||
|
*
|
||||||
|
* // middleware function
|
||||||
|
* router.use((req, res, next) => {console.log('Hello')});
|
||||||
|
*
|
||||||
|
* // middleware object
|
||||||
|
* router.use(new Middleware());
|
||||||
|
*
|
||||||
|
* @param {Middleware|Function} middleware object or function
|
||||||
|
* @return {Router} for chaining
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
use(middleware) {
|
use(middleware) {
|
||||||
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
||||||
throw new TypeError('use method takes at least a middleware');
|
throw new TypeError(error_middleware_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._add(new Route(this, undefined, undefined, middleware));
|
this._add(new Route(this, undefined, undefined, middleware));
|
||||||
@ -122,53 +180,143 @@ export default class Router {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the given middleware function or object on this router for
|
||||||
|
* all HTTP methods.
|
||||||
|
*
|
||||||
|
* // middleware function
|
||||||
|
* router.all((req, res, next) => {console.log('Hello')});
|
||||||
|
*
|
||||||
|
* // middleware object
|
||||||
|
* router.all(new Middleware());
|
||||||
|
*
|
||||||
|
* @param {Middleware|Function} middleware object or function
|
||||||
|
* @return {Router} for chaining
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
all(...args) {
|
all(...args) {
|
||||||
if (args.length === 0) {
|
const {middleware} = toParameters(args);
|
||||||
throw new TypeError('use all method takes at least a middleware');
|
if (!middleware) {
|
||||||
}
|
throw new TypeError(error_middleware_message);
|
||||||
let middleware;
|
|
||||||
|
|
||||||
if (args.length === 1) {
|
|
||||||
[middleware,] = args;
|
|
||||||
} else {
|
|
||||||
[, middleware,] = args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
HTTP_METHODS.forEach((method) => {
|
||||||
throw new TypeError('use all method takes at least a middleware');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const method of HTTP_METHODS) {
|
|
||||||
this[method.toLowerCase()](...args);
|
this[method.toLowerCase()](...args);
|
||||||
}
|
});
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const method of HTTP_METHODS) {
|
HTTP_METHODS.forEach((method) => {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the given middleware function or object, with optional _uri_ on
|
||||||
|
* HTTP methods: get, post, put, delete...
|
||||||
|
* Default _uri_ is "/".
|
||||||
|
*
|
||||||
|
* // middleware function will be applied on path "/"
|
||||||
|
* router.get((req, res, next) => {console.log('Hello')});
|
||||||
|
*
|
||||||
|
* // middleware object will be applied on path "/" and
|
||||||
|
* router.get(new Middleware());
|
||||||
|
*
|
||||||
|
* // middleware function will be applied on path "/user"
|
||||||
|
* router.post('/user', (req, res, next) => {console.log('Hello')});
|
||||||
|
*
|
||||||
|
* // middleware object will be applied on path "/user" and
|
||||||
|
* router.post('/user', new Middleware());
|
||||||
|
*
|
||||||
|
* @param {String} uri
|
||||||
|
* @param {Middleware|Function} middleware object or function
|
||||||
|
* @return {Router} for chaining
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
const methodName = method.toLowerCase();
|
const methodName = method.toLowerCase();
|
||||||
Router.prototype[methodName] = function(...args) {
|
Router.prototype[methodName] = function(...args) {
|
||||||
if (args.length === 0) {
|
const {baseUri, middleware} = toParameters(args);
|
||||||
throw new TypeError(`use ${methodName} method takes at least a middleware`);
|
if (!middleware) {
|
||||||
}
|
throw new TypeError(error_middleware_message);
|
||||||
let uri, middleware;
|
|
||||||
|
|
||||||
if (args.length === 1) {
|
|
||||||
[middleware,] = args;
|
|
||||||
} else {
|
|
||||||
[uri, middleware,] = args;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {
|
if (baseUri && this._baseUri && this._baseUri instanceof RegExp) {
|
||||||
throw new TypeError(`use ${methodName} method takes at least a middleware`);
|
throw new TypeError('router cannot mix uri/regexp');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (uri && this._baseUri && this._baseUri instanceof RegExp) {
|
this._add(new Route(this, baseUri, method, middleware));
|
||||||
throw new TypeError('router contains a regexp cannot mix with route uri/regexp');
|
|
||||||
}
|
|
||||||
|
|
||||||
this._add(new Route(this, uri, method, middleware));
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
export function routeMatcher(request, route) {
|
||||||
|
// check if http method are equals
|
||||||
|
if (route.method && route.method !== request.method) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// route and uri not defined always match
|
||||||
|
if (!route.uri || !request.uri) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove query string and anchor from uri to test
|
||||||
|
const match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(request.uri);
|
||||||
|
const baseUriToCheck = match[1] || match[2] || match[3];
|
||||||
|
|
||||||
|
// if route is a regexp path
|
||||||
|
if (route.uri instanceof RegExp) {
|
||||||
|
return baseUriToCheck.match(route.uri) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if route is parameterized path
|
||||||
|
if (route.uri.indexOf(':') !== -1) {
|
||||||
|
|
||||||
|
const decodeParmeterValue = (v) => {
|
||||||
|
return !isNaN(parseFloat(v)) && isFinite(v) ? (Number.isInteger(v) ? Number.parseInt(v, 10) : Number.parseFloat(v)) : v;
|
||||||
|
};
|
||||||
|
|
||||||
|
// figure out key names
|
||||||
|
const keys = [];
|
||||||
|
const keysRE = /:([^\/\?]+)\??/g;
|
||||||
|
let keysMatch = keysRE.exec(route.uri);
|
||||||
|
while (keysMatch != null) {
|
||||||
|
keys.push(keysMatch[1]);
|
||||||
|
keysMatch = keysRE.exec(route.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
// change parameterized path to regexp
|
||||||
|
const regExpUri = route.uri
|
||||||
|
// :parameter?
|
||||||
|
.replace(/\/:[^\/]+\?/g, '(?:\/([^\/]+))?')
|
||||||
|
// :parameter
|
||||||
|
.replace(/:[^\/]+/g, '([^\/]+)')
|
||||||
|
// escape all /
|
||||||
|
.replace('/', '\\/');
|
||||||
|
|
||||||
|
// checks if uri match
|
||||||
|
const routeMatch = baseUriToCheck.match(new RegExp(`^${regExpUri}$`));
|
||||||
|
if (!routeMatch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update params in request with keys
|
||||||
|
request.params = Object.assign(request.params, keys.reduce((acc, key, index) => {
|
||||||
|
let value = routeMatch[index + 1];
|
||||||
|
if (value) {
|
||||||
|
value = value.indexOf(',') !== -1 ? value.split(',').map(v => decodeParmeterValue(v)) : value = decodeParmeterValue(value);
|
||||||
|
}
|
||||||
|
acc[key] = value;
|
||||||
|
return acc;
|
||||||
|
}, {}));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if route is a simple path
|
||||||
|
return route.uri === baseUriToCheck;
|
||||||
}
|
}
|
||||||
|
@ -1,52 +1,66 @@
|
|||||||
import Requester from './requester';
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
import {routeMatcher} from './router';
|
||||||
|
import Requester, {httpGetTransformer} from './requester';
|
||||||
|
|
||||||
|
|
||||||
|
function errorIfNotFunction(toTest, message) {
|
||||||
|
if(typeof toTest !== 'function') {
|
||||||
|
throw new TypeError(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings object.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class Settings {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the settings.
|
||||||
|
*
|
||||||
|
* - setup default configuration
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
export default class {
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// default settings
|
// default settings
|
||||||
this.settings = {
|
this.settings = {
|
||||||
'http requester': new Requester(),
|
'http requester': new Requester(),
|
||||||
|
'http GET transformer': httpGetTransformer,
|
||||||
'http GET transformer': {
|
// 'http POST transformer': httpPostTransformer,
|
||||||
uri({uri, headers, data}) {
|
'route matcher': routeMatcher
|
||||||
if (!data) {
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
let anchor = '';
|
|
||||||
let uriWithoutAnchor = uri;
|
|
||||||
const hashIndex = uri.indexOf('#');
|
|
||||||
if (hashIndex >=1) {
|
|
||||||
uriWithoutAnchor = uri.slice(0, hashIndex);
|
|
||||||
anchor = uri.slice(hashIndex, uri.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {
|
|
||||||
if (index === 0 && gUri.indexOf('?') === -1) {
|
|
||||||
gUri += '?';
|
|
||||||
} else {
|
|
||||||
gUri += '&';
|
|
||||||
}
|
|
||||||
gUri += `${d}=${data[d]}`;
|
|
||||||
return gUri;
|
|
||||||
}, uriWithoutAnchor);
|
|
||||||
|
|
||||||
return uriWithoutAnchor + anchor;
|
|
||||||
},
|
|
||||||
data({uri, headers, data}) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.rules = {
|
this.rules = {
|
||||||
'http requester': (requester) => {
|
'http requester': (requester) => {
|
||||||
if(typeof requester.fetch !== 'function') {
|
errorIfNotFunction(requester.fetch , 'setting http requester has no fetch function');
|
||||||
throw new TypeError('setting http requester has no fetch method');
|
},
|
||||||
|
'http GET transformer': (transformer) => {
|
||||||
|
if (!transformer || (!transformer.uri && !transformer.headers && !transformer.data)) {
|
||||||
|
throw new TypeError('setting http transformer one of functions: uri, headers, data is missing');
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'route matcher': (routeMatcher) => {
|
||||||
|
errorIfNotFunction(routeMatcher, 'setting route matcher is not a function');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign `setting` to `val`
|
||||||
|
*
|
||||||
|
* @param {String} setting
|
||||||
|
* @param {*} [val]
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
set(name, value) {
|
set(name, value) {
|
||||||
const checkRules = this.rules[name];
|
const checkRules = this.rules[name];
|
||||||
if (checkRules) {
|
if (checkRules) {
|
||||||
@ -55,7 +69,15 @@ export default class {
|
|||||||
this.settings[name] = value;
|
this.settings[name] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return `setting`'s value.
|
||||||
|
*
|
||||||
|
* @param {String} setting
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
get(name) {
|
get(name) {
|
||||||
return this.settings[name];
|
return this.settings[name];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
56
package.json
56
package.json
@ -1,18 +1,21 @@
|
|||||||
{
|
{
|
||||||
"name": "frontexpress",
|
"name": "frontexpress",
|
||||||
"version": "0.1.2",
|
"version": "1.2.0",
|
||||||
"description": "Minimalist front end router framework a la express",
|
"description": "Frontexpress manages routes in browser like ExpressJS on Node",
|
||||||
"main": "dist/frontexpress.js",
|
"main": "dist/frontexpress.js",
|
||||||
|
"jsnext:main": "lib/frontexpress.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"only-test": "mocha --compilers js:babel-core/register",
|
"test-only": "mocha --compilers js:babel-core/register",
|
||||||
"test": "npm run lint && babel-node node_modules/.bin/babel-istanbul cover node_modules/.bin/_mocha",
|
"test": "npm run lint && babel-node node_modules/.bin/babel-istanbul cover node_modules/.bin/_mocha",
|
||||||
"prepublish": "rimraf dist && babel lib -d dist"
|
"gzipsize": "babel-node gzipsize.js",
|
||||||
|
"frontpackage": "rollup -c rollup.config.dev.js && rollup -c rollup.config.prod.js && npm run gzipsize",
|
||||||
|
"prepublish": "rimraf dist && babel lib -d dist && npm run frontpackage"
|
||||||
},
|
},
|
||||||
"author": "Camel Aissani <camel.aissani@gmail.com> (https://nuageprive.fr)",
|
"author": "Camel Aissani <camel.aissani@gmail.com> (https://nuageprive.fr)",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=4.2.6"
|
||||||
},
|
},
|
||||||
"repository": "camelaissani/frontexpress",
|
"repository": "camelaissani/frontexpress",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -21,25 +24,38 @@
|
|||||||
"web",
|
"web",
|
||||||
"router",
|
"router",
|
||||||
"middleware",
|
"middleware",
|
||||||
"rest",
|
|
||||||
"restful",
|
|
||||||
"app",
|
"app",
|
||||||
"api",
|
"api",
|
||||||
"express",
|
"express",
|
||||||
"frontexpress"
|
"frontexpress"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-cli": "^6.10.1",
|
"babel-cli": "^6.18.0",
|
||||||
"babel-core": "^6.10.4",
|
"babel-core": "^6.21.0",
|
||||||
"babel-eslint": "^6.1.0",
|
"babel-eslint": "^7.1.1",
|
||||||
"babel-istanbul": "^0.8.0",
|
"babel-istanbul": "^0.12.1",
|
||||||
"babel-preset-es2015": "^6.9.0",
|
"babel-preset-es2015": "^6.18.0",
|
||||||
"chai": "^3.5.0",
|
"babel-preset-es2015-rollup": "^3.0.0",
|
||||||
"eslint": "^2.11.0",
|
"babel-register": "^6.18.0",
|
||||||
"eslint-loader": "^1.3.0",
|
"bytesize": "^0.2.0",
|
||||||
"istanbul": "^0.4.3",
|
"chai": "^4.*",
|
||||||
"mocha": "^2.5.3",
|
"eslint": "^3.*",
|
||||||
"rimraf": "^2.5.2",
|
"istanbul": "^0.4.5",
|
||||||
"sinon": "^1.17.4"
|
"mocha": "^3.2.0",
|
||||||
}
|
"rimraf": "^2.5.4",
|
||||||
|
"rollup": "^0.*",
|
||||||
|
"rollup-plugin-babel": "^2.7.1",
|
||||||
|
"rollup-plugin-uglify-es": "0.0.1",
|
||||||
|
"sinon": "^1.*"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/",
|
||||||
|
"docs/",
|
||||||
|
"lib/",
|
||||||
|
"README.md",
|
||||||
|
"LICENCE",
|
||||||
|
"frontexpress.js",
|
||||||
|
"frontexpress.min.js",
|
||||||
|
"frontexpress.min.js.map"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
12
rollup.config.dev.js
Normal file
12
rollup.config.dev.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import babel from 'rollup-plugin-babel';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
entry: 'lib/frontexpress.js',
|
||||||
|
format: 'iife',
|
||||||
|
moduleName:'frontexpress',
|
||||||
|
plugins: [babel({
|
||||||
|
babelrc: false,
|
||||||
|
presets: ['es2015-rollup']
|
||||||
|
})],
|
||||||
|
dest: 'frontexpress.js'
|
||||||
|
};
|
17
rollup.config.prod.js
Normal file
17
rollup.config.prod.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import uglify from 'rollup-plugin-uglify-es';
|
||||||
|
import babel from 'rollup-plugin-babel';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
entry: 'lib/frontexpress.js',
|
||||||
|
format: 'iife',
|
||||||
|
sourceMap: true,
|
||||||
|
moduleName:'frontexpress',
|
||||||
|
dest: 'frontexpress.min.js',
|
||||||
|
plugins: [
|
||||||
|
babel({
|
||||||
|
babelrc: false,
|
||||||
|
presets: ['es2015-rollup']
|
||||||
|
}),
|
||||||
|
uglify()
|
||||||
|
]
|
||||||
|
};
|
@ -1,10 +1,18 @@
|
|||||||
/*eslint-env mocha*/
|
/*eslint-env mocha*/
|
||||||
import chai, {assert} from 'chai';
|
import chai, {assert} from 'chai';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
//import jsdom from 'jsdom';
|
||||||
import frontexpress from '../lib/frontexpress';
|
import frontexpress from '../lib/frontexpress';
|
||||||
import Requester from '../lib/requester';
|
import Requester from '../lib/requester';
|
||||||
|
|
||||||
describe('Application', () => {
|
describe('Application', () => {
|
||||||
|
class MyMiddleware extends frontexpress.Middleware {
|
||||||
|
entered() {}
|
||||||
|
exited() {}
|
||||||
|
failed() {}
|
||||||
|
updated() {}
|
||||||
|
}
|
||||||
|
|
||||||
let requester;
|
let requester;
|
||||||
|
|
||||||
describe('generated methods', () => {
|
describe('generated methods', () => {
|
||||||
@ -25,18 +33,108 @@ describe('Application', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('listen method', () => {
|
// // JSDOM cannot manage pushState/onpopstate/window.location
|
||||||
let eventFn = {};
|
// see https://github.com/tmpvar/jsdom/issues/1565
|
||||||
|
//
|
||||||
|
// describe('listen method with JSDOM', () => {
|
||||||
|
// before(() => {
|
||||||
|
// // Init DOM with a fake document
|
||||||
|
// // <base> and uri (initial uri) allow to do pushState in jsdom
|
||||||
|
// jsdom.env({
|
||||||
|
// html:`
|
||||||
|
// <html>
|
||||||
|
// <head>
|
||||||
|
// <base href="http://localhost:8080/"></base>
|
||||||
|
// </head>
|
||||||
|
// </html>
|
||||||
|
// `,
|
||||||
|
// url: 'http://localhost:8080/',
|
||||||
|
// done(err, window) {
|
||||||
|
// global.window = window;
|
||||||
|
// global.document = window.document;
|
||||||
|
// window.console = global.console;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let requester;
|
||||||
|
// beforeEach(()=>{
|
||||||
|
// requester = new Requester();
|
||||||
|
// sinon.stub(requester, 'fetch', ({uri, method, headers, data}, resolve, reject) => {
|
||||||
|
// resolve(
|
||||||
|
// {uri, method, headers, data},
|
||||||
|
// {status: 200, statusText: 'OK', responseText:''}
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
// it('history management without state object', (done) => {
|
||||||
|
// const spy_pushState = sinon.spy(window.history, 'pushState');
|
||||||
|
|
||||||
|
// const app = frontexpress();
|
||||||
|
// const m = new MyMiddleware();
|
||||||
|
// const spy_middleware = sinon.stub(m, 'updated');
|
||||||
|
|
||||||
|
// app.set('http requester', requester);
|
||||||
|
// app.use('/api/route1', m);
|
||||||
|
// app.listen();
|
||||||
|
|
||||||
|
// const spy_onpopstate = sinon.spy(window, 'onpopstate');
|
||||||
|
|
||||||
|
// app.httpGet({uri:'/api/route1', history: {
|
||||||
|
// uri: '/route1',
|
||||||
|
// title: 'route1'
|
||||||
|
// }},
|
||||||
|
// (req, res) => {
|
||||||
|
// // On request succeeded
|
||||||
|
// assert(spy_onpopstate.callCount === 0);
|
||||||
|
// assert(spy_pushState.calledOnce);
|
||||||
|
// assert(spy_middleware.calledOnce);
|
||||||
|
|
||||||
|
// spy_middleware.reset();
|
||||||
|
// window.history.back();
|
||||||
|
// window.history.forward();
|
||||||
|
// assert(spy_onpopstate.calledOnce);
|
||||||
|
// assert(spy_middleware.calledOnce);
|
||||||
|
|
||||||
|
// done();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
|
||||||
|
describe('listen method', () => {
|
||||||
|
// Here I cannot use jsdon to make these tests :(
|
||||||
|
// JSDOM cannot simulate readyState changes
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
const browserHistory = [{uri: '/'}];
|
||||||
|
let browserHistoryIndex = 0;
|
||||||
|
|
||||||
global.document = {};
|
global.document = {};
|
||||||
global.window = {
|
global.window = {
|
||||||
addEventListener(eventType, callback) {
|
|
||||||
eventFn[eventType] = callback;
|
|
||||||
},
|
|
||||||
location: {
|
location: {
|
||||||
pathname: '/route1',
|
pathname: '/route1',
|
||||||
search: '?a=b'
|
search: '?a=b'
|
||||||
|
},
|
||||||
|
history: {
|
||||||
|
pushState(state, title, pathname) {
|
||||||
|
browserHistory.push({uri: pathname, state});
|
||||||
|
browserHistoryIndex++;
|
||||||
|
global.window.location.pathname = browserHistory[browserHistoryIndex].uri;
|
||||||
|
},
|
||||||
|
forward() {
|
||||||
|
browserHistoryIndex++;
|
||||||
|
global.window.location.pathname = browserHistory[browserHistoryIndex].uri;
|
||||||
|
if (browserHistory[browserHistoryIndex].state) {
|
||||||
|
window.onpopstate({state:browserHistory[browserHistoryIndex].state});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
back() {
|
||||||
|
browserHistoryIndex--;
|
||||||
|
global.window.location.pathname = browserHistory[browserHistoryIndex].uri;
|
||||||
|
if (browserHistory[browserHistoryIndex].state) {
|
||||||
|
window.onpopstate({state: browserHistory[browserHistoryIndex].state});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -55,7 +153,7 @@ describe('Application', () => {
|
|||||||
|
|
||||||
it('with middleware object readyState===loading', (done) => {
|
it('with middleware object readyState===loading', (done) => {
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
const m = frontexpress.Middleware();
|
const m = new MyMiddleware();
|
||||||
sinon.stub(m, 'entered', () => {
|
sinon.stub(m, 'entered', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -70,7 +168,7 @@ describe('Application', () => {
|
|||||||
|
|
||||||
it('with middleware object readyState===interactive', (done) => {
|
it('with middleware object readyState===interactive', (done) => {
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
const m = frontexpress.Middleware();
|
const m = new MyMiddleware();
|
||||||
sinon.stub(m, 'updated', () => {
|
sinon.stub(m, 'updated', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -85,7 +183,7 @@ describe('Application', () => {
|
|||||||
|
|
||||||
it('with middleware object event beforeunload', (done) => {
|
it('with middleware object event beforeunload', (done) => {
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
const m = frontexpress.Middleware();
|
const m = new MyMiddleware();
|
||||||
sinon.stub(m, 'exited', () => {
|
sinon.stub(m, 'exited', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
@ -93,13 +191,111 @@ describe('Application', () => {
|
|||||||
app.use('/route1', m);
|
app.use('/route1', m);
|
||||||
app.listen(() => {
|
app.listen(() => {
|
||||||
//simulate beforeunload
|
//simulate beforeunload
|
||||||
eventFn['beforeunload']();
|
window.onbeforeunload();
|
||||||
});
|
});
|
||||||
|
|
||||||
//simulate readystatechange
|
//simulate readystatechange
|
||||||
document.readyState = 'interactive';
|
document.readyState = 'interactive';
|
||||||
document.onreadystatechange();
|
document.onreadystatechange();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('history management without state object', (done) => {
|
||||||
|
let historyObj;
|
||||||
|
const requester = new Requester();
|
||||||
|
sinon.stub(requester, 'fetch', ({uri, method, headers, data, history}, resolve, reject) => {
|
||||||
|
resolve(
|
||||||
|
{uri, method, headers, data, history},
|
||||||
|
{status: 200, statusText: 'OK', responseText:''}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const spy_pushState = sinon.spy(window.history, 'pushState');
|
||||||
|
|
||||||
|
const app = frontexpress();
|
||||||
|
const m = new MyMiddleware();
|
||||||
|
const spy_middleware = sinon.stub(m, 'updated', (req, res) => {
|
||||||
|
historyObj = req.history;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.set('http requester', requester);
|
||||||
|
app.use('/api/route1', m);
|
||||||
|
app.listen();
|
||||||
|
|
||||||
|
const spy_onpopstate = sinon.spy(window, 'onpopstate');
|
||||||
|
|
||||||
|
app.httpGet({uri:'/api/route1', history: {
|
||||||
|
uri: '/route1',
|
||||||
|
title: 'route1'
|
||||||
|
}},
|
||||||
|
(req, res) => {
|
||||||
|
// On request succeeded
|
||||||
|
assert(spy_onpopstate.callCount === 0);
|
||||||
|
assert(spy_pushState.calledOnce);
|
||||||
|
assert(spy_middleware.calledOnce);
|
||||||
|
|
||||||
|
spy_middleware.reset();
|
||||||
|
window.history.back();
|
||||||
|
window.history.forward();
|
||||||
|
assert(spy_onpopstate.calledOnce);
|
||||||
|
assert(spy_middleware.calledOnce);
|
||||||
|
assert(historyObj);
|
||||||
|
assert(historyObj.uri === '/route1');
|
||||||
|
assert(historyObj.title === 'route1');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('history management with state object', (done) => {
|
||||||
|
let historyObj;
|
||||||
|
const requester = new Requester();
|
||||||
|
sinon.stub(requester, 'fetch', ({uri, method, headers, data, history}, resolve, reject) => {
|
||||||
|
resolve(
|
||||||
|
{uri, method, headers, data, history},
|
||||||
|
{status: 200, statusText: 'OK', responseText:''}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const spy_pushState = sinon.spy(window.history, 'pushState');
|
||||||
|
|
||||||
|
const app = frontexpress();
|
||||||
|
const m = new MyMiddleware();
|
||||||
|
const spy_middleware = sinon.stub(m, 'updated', (req, res) => {
|
||||||
|
historyObj = req.history;
|
||||||
|
});
|
||||||
|
|
||||||
|
app.set('http requester', requester);
|
||||||
|
app.use('/api/route1', m);
|
||||||
|
app.listen();
|
||||||
|
|
||||||
|
const spy_onpopstate = sinon.spy(window, 'onpopstate');
|
||||||
|
|
||||||
|
app.httpGet({uri:'/api/route1', history: {
|
||||||
|
uri: '/route1',
|
||||||
|
title: 'route1',
|
||||||
|
state: {a: 'b', c: 'd'}
|
||||||
|
}},
|
||||||
|
(req, res) => {
|
||||||
|
// On request succeeded
|
||||||
|
assert(spy_onpopstate.callCount === 0);
|
||||||
|
assert(spy_pushState.calledOnce);
|
||||||
|
assert(spy_middleware.calledOnce);
|
||||||
|
|
||||||
|
spy_middleware.reset();
|
||||||
|
window.history.back();
|
||||||
|
window.history.forward();
|
||||||
|
assert(spy_onpopstate.calledOnce);
|
||||||
|
assert(spy_middleware.calledOnce);
|
||||||
|
assert(historyObj);
|
||||||
|
assert(historyObj.uri === '/route1');
|
||||||
|
assert(historyObj.title === 'route1');
|
||||||
|
assert(historyObj.state);
|
||||||
|
assert(historyObj.state.a === 'b');
|
||||||
|
assert(historyObj.state.c === 'd');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('set/get setting method', () => {
|
describe('set/get setting method', () => {
|
||||||
@ -114,6 +310,7 @@ describe('Application', () => {
|
|||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
app.set('http requester', requester);
|
app.set('http requester', requester);
|
||||||
assert(app.get('http requester') === requester);
|
assert(app.get('http requester') === requester);
|
||||||
|
assert(app.set('http requester') === requester);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('bad core setting', () => {
|
it('bad core setting', () => {
|
||||||
@ -214,7 +411,7 @@ describe('Application', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('middleware as object on path /', (done) => {
|
it('middleware as object on path /', (done) => {
|
||||||
const middleware = frontexpress.Middleware('on path /');
|
const middleware = new MyMiddleware('on path /');
|
||||||
const spy = sinon.spy(middleware, 'updated');
|
const spy = sinon.spy(middleware, 'updated');
|
||||||
|
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
@ -232,7 +429,7 @@ describe('Application', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('middleware as object on path /route1', (done) => {
|
it('middleware as object on path /route1', (done) => {
|
||||||
const middleware = frontexpress.Middleware('on path /route1');
|
const middleware = new MyMiddleware('on path /route1');
|
||||||
const spy = sinon.spy(middleware, 'updated');
|
const spy = sinon.spy(middleware, 'updated');
|
||||||
|
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
@ -258,7 +455,7 @@ describe('Application', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const middleware = frontexpress.Middleware('on path /');
|
const middleware = new MyMiddleware('on path /');
|
||||||
const spy = sinon.spy(middleware, 'failed');
|
const spy = sinon.spy(middleware, 'failed');
|
||||||
|
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
@ -315,7 +512,7 @@ describe('Application', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('router with base uri', (done)=> {
|
it('router with base uri', (done)=> {
|
||||||
const middleware = frontexpress.Middleware('get middleware');
|
const middleware = new MyMiddleware('get middleware');
|
||||||
const spy = sinon.spy(middleware, 'updated');
|
const spy = sinon.spy(middleware, 'updated');
|
||||||
|
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
@ -338,12 +535,12 @@ describe('Application', () => {
|
|||||||
it('use two routers', (done)=> {
|
it('use two routers', (done)=> {
|
||||||
|
|
||||||
const router1 = frontexpress.Router();
|
const router1 = frontexpress.Router();
|
||||||
const m1 = frontexpress.Middleware();
|
const m1 = new MyMiddleware();
|
||||||
const spy1 = sinon.spy(m1, 'updated');
|
const spy1 = sinon.spy(m1, 'updated');
|
||||||
router1.get('/subroute1', m1);
|
router1.get('/subroute1', m1);
|
||||||
|
|
||||||
const router2 = frontexpress.Router();
|
const router2 = frontexpress.Router();
|
||||||
const m2 = frontexpress.Middleware();
|
const m2 = new MyMiddleware();
|
||||||
const spy2 = sinon.spy(m2, 'updated');
|
const spy2 = sinon.spy(m2, 'updated');
|
||||||
router2.get('/subroute2', m2);
|
router2.get('/subroute2', m2);
|
||||||
|
|
||||||
@ -431,7 +628,7 @@ describe('Application', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('middleware as object on path /', (done) => {
|
it('middleware as object on path /', (done) => {
|
||||||
const middleware = frontexpress.Middleware('on path /');
|
const middleware = new MyMiddleware('on path /');
|
||||||
const spy = sinon.spy(middleware, 'updated');
|
const spy = sinon.spy(middleware, 'updated');
|
||||||
|
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
@ -449,7 +646,7 @@ describe('Application', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('middleware as object on path /route1', (done) => {
|
it('middleware as object on path /route1', (done) => {
|
||||||
const middleware = frontexpress.Middleware('on path /route1');
|
const middleware = new MyMiddleware('on path /route1');
|
||||||
const spy = sinon.spy(middleware, 'updated');
|
const spy = sinon.spy(middleware, 'updated');
|
||||||
|
|
||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
@ -611,7 +808,7 @@ describe('Application', () => {
|
|||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
app.set('http requester', requester);
|
app.set('http requester', requester);
|
||||||
|
|
||||||
const getMiddleware = frontexpress.Middleware('get middleware');
|
const getMiddleware = new MyMiddleware('get middleware');
|
||||||
const spy_get_entered = sinon.spy(getMiddleware, 'entered');
|
const spy_get_entered = sinon.spy(getMiddleware, 'entered');
|
||||||
const spy_get_updated = sinon.spy(getMiddleware, 'updated');
|
const spy_get_updated = sinon.spy(getMiddleware, 'updated');
|
||||||
const spy_get_exited = sinon.spy(getMiddleware, 'exited');
|
const spy_get_exited = sinon.spy(getMiddleware, 'exited');
|
||||||
@ -639,17 +836,17 @@ describe('Application', () => {
|
|||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
app.set('http requester', requester);
|
app.set('http requester', requester);
|
||||||
|
|
||||||
const allMiddleware = frontexpress.Middleware('all middleware');
|
const allMiddleware = new MyMiddleware('all middleware');
|
||||||
const spy_all_entered = sinon.spy(allMiddleware, 'entered');
|
const spy_all_entered = sinon.spy(allMiddleware, 'entered');
|
||||||
const spy_all_updated = sinon.spy(allMiddleware, 'updated');
|
const spy_all_updated = sinon.spy(allMiddleware, 'updated');
|
||||||
const spy_all_exited = sinon.spy(allMiddleware, 'exited');
|
const spy_all_exited = sinon.spy(allMiddleware, 'exited');
|
||||||
|
|
||||||
const getMiddleware = frontexpress.Middleware('get middleware');
|
const getMiddleware = new MyMiddleware('get middleware');
|
||||||
const spy_get_entered = sinon.spy(getMiddleware, 'entered');
|
const spy_get_entered = sinon.spy(getMiddleware, 'entered');
|
||||||
const spy_get_updated = sinon.spy(getMiddleware, 'updated');
|
const spy_get_updated = sinon.spy(getMiddleware, 'updated');
|
||||||
const spy_get_exited = sinon.spy(getMiddleware, 'exited');
|
const spy_get_exited = sinon.spy(getMiddleware, 'exited');
|
||||||
|
|
||||||
const postMiddleware = frontexpress.Middleware('post middleware');
|
const postMiddleware = new MyMiddleware('post middleware');
|
||||||
const spy_post_entered = sinon.spy(postMiddleware, 'entered');
|
const spy_post_entered = sinon.spy(postMiddleware, 'entered');
|
||||||
const spy_post_updated = sinon.spy(postMiddleware, 'updated');
|
const spy_post_updated = sinon.spy(postMiddleware, 'updated');
|
||||||
const spy_post_exited = sinon.spy(postMiddleware, 'exited');
|
const spy_post_exited = sinon.spy(postMiddleware, 'exited');
|
||||||
@ -722,7 +919,7 @@ describe('Application', () => {
|
|||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
app.set('http requester', requester);
|
app.set('http requester', requester);
|
||||||
|
|
||||||
const getMiddleware = frontexpress.Middleware('get middleware');
|
const getMiddleware = new MyMiddleware('get middleware');
|
||||||
const spy_get_failed = sinon.spy(getMiddleware, 'failed');
|
const spy_get_failed = sinon.spy(getMiddleware, 'failed');
|
||||||
|
|
||||||
|
|
||||||
@ -741,10 +938,10 @@ describe('Application', () => {
|
|||||||
const app = frontexpress();
|
const app = frontexpress();
|
||||||
app.set('http requester', requester);
|
app.set('http requester', requester);
|
||||||
|
|
||||||
const m1 = frontexpress.Middleware('m1');
|
const m1 = new MyMiddleware('m1');
|
||||||
const m2 = frontexpress.Middleware('m2');
|
const m2 = new MyMiddleware('m2');
|
||||||
m2.next = () => false;
|
m2.next = () => false;
|
||||||
const m3 = frontexpress.Middleware('m3');
|
const m3 = new MyMiddleware('m3');
|
||||||
|
|
||||||
const spy_m1 = sinon.spy(m1, 'updated');
|
const spy_m1 = sinon.spy(m1, 'updated');
|
||||||
const spy_m2 = sinon.spy(m2, 'updated');
|
const spy_m2 = sinon.spy(m2, 'updated');
|
||||||
@ -792,4 +989,22 @@ describe('Application', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('plugin management', () => {
|
||||||
|
it('setup a plugin', (done) => {
|
||||||
|
const app = frontexpress();
|
||||||
|
app.use({
|
||||||
|
name: 'my plugin',
|
||||||
|
plugin(application) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen();
|
||||||
|
|
||||||
|
//simulate readystatechange
|
||||||
|
document.readyState = 'interactive';
|
||||||
|
document.onreadystatechange();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -18,10 +18,10 @@ describe('frontexpress', () => {
|
|||||||
|
|
||||||
it('test Middleware class exposed', () => {
|
it('test Middleware class exposed', () => {
|
||||||
assert(frontexpress.Middleware);
|
assert(frontexpress.Middleware);
|
||||||
assert(frontexpress.Middleware() instanceof Middleware);
|
assert(new frontexpress.Middleware() instanceof Middleware);
|
||||||
|
|
||||||
const m1 = frontexpress.Middleware();
|
const m1 = new frontexpress.Middleware();
|
||||||
const m2 = frontexpress.Middleware();
|
const m2 = new frontexpress.Middleware();
|
||||||
assert(m1 !== m2);
|
assert(m1 !== m2);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -33,4 +33,4 @@ describe('frontexpress', () => {
|
|||||||
const app2 = frontexpress();
|
const app2 = frontexpress();
|
||||||
assert(app1 !== app2);
|
assert(app1 !== app2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
20
test/middleware-test.js
Normal file
20
test/middleware-test.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*eslint-env mocha*/
|
||||||
|
import {assert} from 'chai';
|
||||||
|
import Middleware from '../lib/middleware';
|
||||||
|
|
||||||
|
describe('Middleware', () => {
|
||||||
|
it('check exposed methods', () => {
|
||||||
|
const middleware = new Middleware();
|
||||||
|
assert(middleware.entered);
|
||||||
|
assert(middleware.exited);
|
||||||
|
assert(middleware.updated);
|
||||||
|
assert(middleware.failed);
|
||||||
|
assert(middleware.next);
|
||||||
|
|
||||||
|
middleware.entered();
|
||||||
|
middleware.exited();
|
||||||
|
middleware.updated();
|
||||||
|
middleware.failed();
|
||||||
|
assert(middleware.next());
|
||||||
|
});
|
||||||
|
});
|
@ -111,7 +111,7 @@ describe('Test sample from README', () => {
|
|||||||
|
|
||||||
const router = frontexpress.Router();
|
const router = frontexpress.Router();
|
||||||
|
|
||||||
// middleware that is specific to this router
|
// middleware which is specific to this router
|
||||||
router.use((req, res, next) => {
|
router.use((req, res, next) => {
|
||||||
spy_log(`Time: ${Date.now()}`);
|
spy_log(`Time: ${Date.now()}`);
|
||||||
next();
|
next();
|
||||||
@ -148,4 +148,4 @@ describe('Test sample from README', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -378,15 +378,16 @@ describe('Requester', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('request returns unknown error', () => {
|
// Removed for reducing size of frontexpress
|
||||||
const requester = new Requester();
|
// it('request returns unknown error', () => {
|
||||||
|
// const requester = new Requester();
|
||||||
|
|
||||||
const {stub_open, stub_send} = xHttpWillThrow(xhttp, 'BlaBlaError');
|
// const {stub_open, stub_send} = xHttpWillThrow(xhttp, 'BlaBlaError');
|
||||||
|
|
||||||
chai.expect(() => {
|
// chai.expect(() => {
|
||||||
requester.fetch({method: 'GET', uri:'/route1'});
|
// requester.fetch({method: 'GET', uri:'/route1'});
|
||||||
}).to.throw(/BlaBlaError/);
|
// }).to.throw(/BlaBlaError/);
|
||||||
|
|
||||||
});
|
// });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,9 @@ import sinon from 'sinon';
|
|||||||
import frontexpress from '../lib/frontexpress';
|
import frontexpress from '../lib/frontexpress';
|
||||||
import HTTP_METHODS from '../lib/methods';
|
import HTTP_METHODS from '../lib/methods';
|
||||||
|
|
||||||
|
const application = frontexpress();
|
||||||
|
const routeMatcher = application.get('route matcher');
|
||||||
|
|
||||||
describe('Router', () => {
|
describe('Router', () => {
|
||||||
|
|
||||||
describe('generated methods', () => {
|
describe('generated methods', () => {
|
||||||
@ -12,6 +15,7 @@ describe('Router', () => {
|
|||||||
assert(typeof router.all === 'function');
|
assert(typeof router.all === 'function');
|
||||||
assert(typeof router.get === 'function');
|
assert(typeof router.get === 'function');
|
||||||
assert(typeof router.put === 'function');
|
assert(typeof router.put === 'function');
|
||||||
|
assert(typeof router.patch === 'function');
|
||||||
assert(typeof router.post === 'function');
|
assert(typeof router.post === 'function');
|
||||||
assert(typeof router.delete === 'function');
|
assert(typeof router.delete === 'function');
|
||||||
});
|
});
|
||||||
@ -46,7 +50,7 @@ describe('Router', () => {
|
|||||||
|
|
||||||
router.get(middleware);
|
router.get(middleware);
|
||||||
|
|
||||||
const r1 = router.routes('/', 'GET');
|
const r1 = router.routes(application, {uri: '/', method: 'GET'});
|
||||||
assert(r1.length === 1);
|
assert(r1.length === 1);
|
||||||
assert(r1[0].uri === undefined);
|
assert(r1[0].uri === undefined);
|
||||||
assert(r1[0].method === 'GET');
|
assert(r1[0].method === 'GET');
|
||||||
@ -64,37 +68,37 @@ describe('Router', () => {
|
|||||||
.post('/route2', middleware2)
|
.post('/route2', middleware2)
|
||||||
.all('/route3', middleware3);
|
.all('/route3', middleware3);
|
||||||
|
|
||||||
const r1 = router.routes('/route1', 'GET');
|
const r1 = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||||
assert(r1.length === 1);
|
assert(r1.length === 1);
|
||||||
assert(r1[0].uri === '/route1');
|
assert(r1[0].uri === '/route1');
|
||||||
assert(r1[0].method === 'GET');
|
assert(r1[0].method === 'GET');
|
||||||
assert(r1[0].middleware === middleware1);
|
assert(r1[0].middleware === middleware1);
|
||||||
|
|
||||||
const r2 = router.routes('/route2', 'POST');
|
const r2 = router.routes(application, {uri: '/route2', method: 'POST'});
|
||||||
assert(r2.length === 1);
|
assert(r2.length === 1);
|
||||||
assert(r2[0].uri === '/route2');
|
assert(r2[0].uri === '/route2');
|
||||||
assert(r2[0].method === 'POST');
|
assert(r2[0].method === 'POST');
|
||||||
assert(r2[0].middleware === middleware2);
|
assert(r2[0].middleware === middleware2);
|
||||||
|
|
||||||
let r3 = router.routes('/route3', 'GET');
|
let r3 = router.routes(application, {uri: '/route3', method: 'GET'});
|
||||||
assert(r3.length === 1);
|
assert(r3.length === 1);
|
||||||
assert(r3[0].uri === '/route3');
|
assert(r3[0].uri === '/route3');
|
||||||
assert(r3[0].method === 'GET');
|
assert(r3[0].method === 'GET');
|
||||||
assert(r3[0].middleware === middleware3);
|
assert(r3[0].middleware === middleware3);
|
||||||
|
|
||||||
r3 = router.routes('/route3', 'POST');
|
r3 = router.routes(application, {uri: '/route3', method: 'POST'});
|
||||||
assert(r3.length === 1);
|
assert(r3.length === 1);
|
||||||
assert(r3[0].uri === '/route3');
|
assert(r3[0].uri === '/route3');
|
||||||
assert(r3[0].method === 'POST');
|
assert(r3[0].method === 'POST');
|
||||||
assert(r3[0].middleware === middleware3);
|
assert(r3[0].middleware === middleware3);
|
||||||
|
|
||||||
r3 = router.routes('/route3', 'PUT');
|
r3 = router.routes(application, {uri: '/route3', method: 'PUT'});
|
||||||
assert(r3.length === 1);
|
assert(r3.length === 1);
|
||||||
assert(r3[0].uri === '/route3');
|
assert(r3[0].uri === '/route3');
|
||||||
assert(r3[0].method === 'PUT');
|
assert(r3[0].method === 'PUT');
|
||||||
assert(r3[0].middleware === middleware3);
|
assert(r3[0].middleware === middleware3);
|
||||||
|
|
||||||
r3 = router.routes('/route3', 'DELETE');
|
r3 = router.routes(application, {uri: '/route3', method: 'DELETE'});
|
||||||
assert(r3.length === 1);
|
assert(r3.length === 1);
|
||||||
assert(r3[0].uri === '/route3');
|
assert(r3[0].uri === '/route3');
|
||||||
assert(r3[0].method === 'DELETE');
|
assert(r3[0].method === 'DELETE');
|
||||||
@ -103,11 +107,11 @@ describe('Router', () => {
|
|||||||
|
|
||||||
it('no root path and regexp uri', ()=> {
|
it('no root path and regexp uri', ()=> {
|
||||||
const router = frontexpress.Router();
|
const router = frontexpress.Router();
|
||||||
const middleware = frontexpress.Middleware();
|
const middleware = new frontexpress.Middleware();
|
||||||
|
|
||||||
router.get(/^\/route1/, middleware);
|
router.get(/^\/route1/, middleware);
|
||||||
|
|
||||||
const r = router.routes('/route1', 'GET');
|
const r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri instanceof RegExp);
|
assert(r[0].uri instanceof RegExp);
|
||||||
assert(r[0].uri.toString() === new RegExp('^\/route1').toString());
|
assert(r[0].uri.toString() === new RegExp('^\/route1').toString());
|
||||||
@ -118,9 +122,9 @@ describe('Router', () => {
|
|||||||
it('with root path /route1 and path /subroute', () => {
|
it('with root path /route1 and path /subroute', () => {
|
||||||
const router = frontexpress.Router('/route1');
|
const router = frontexpress.Router('/route1');
|
||||||
|
|
||||||
router.get('/subroute', frontexpress.Middleware());
|
router.get('/subroute', new frontexpress.Middleware());
|
||||||
|
|
||||||
const r = router.routes('/route1/subroute', 'GET');
|
const r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1/subroute');
|
assert(r[0].uri === '/route1/subroute');
|
||||||
});
|
});
|
||||||
@ -128,9 +132,9 @@ describe('Router', () => {
|
|||||||
it('with root path /route1 and no path uri', () => {
|
it('with root path /route1 and no path uri', () => {
|
||||||
const router = frontexpress.Router('/route1');
|
const router = frontexpress.Router('/route1');
|
||||||
|
|
||||||
router.get(frontexpress.Middleware());
|
router.get(new frontexpress.Middleware());
|
||||||
|
|
||||||
const r = router.routes('/route1', 'GET');
|
const r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1');
|
assert(r[0].uri === '/route1');
|
||||||
});
|
});
|
||||||
@ -138,9 +142,9 @@ describe('Router', () => {
|
|||||||
it('duplicate / in route', () => {
|
it('duplicate / in route', () => {
|
||||||
const router = frontexpress.Router('/route1/');
|
const router = frontexpress.Router('/route1/');
|
||||||
|
|
||||||
router.get('/subroute', frontexpress.Middleware());
|
router.get('/subroute', new frontexpress.Middleware());
|
||||||
|
|
||||||
const r = router.routes('/route1/subroute', 'GET');
|
const r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1/subroute');
|
assert(r[0].uri === '/route1/subroute');
|
||||||
});
|
});
|
||||||
@ -148,9 +152,9 @@ describe('Router', () => {
|
|||||||
it('spaces in route', () => {
|
it('spaces in route', () => {
|
||||||
let router = frontexpress.Router(' /route1 ');
|
let router = frontexpress.Router(' /route1 ');
|
||||||
|
|
||||||
router.get('/subroute ', frontexpress.Middleware());
|
router.get('/subroute ', new frontexpress.Middleware());
|
||||||
|
|
||||||
let r = router.routes('/route1/subroute', 'GET');
|
let r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1/subroute');
|
assert(r[0].uri === '/route1/subroute');
|
||||||
|
|
||||||
@ -158,9 +162,9 @@ describe('Router', () => {
|
|||||||
|
|
||||||
router = frontexpress.Router(' /route1 ');
|
router = frontexpress.Router(' /route1 ');
|
||||||
|
|
||||||
router.get(frontexpress.Middleware());
|
router.get(new frontexpress.Middleware());
|
||||||
|
|
||||||
r = router.routes('/route1', 'GET');
|
r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1');
|
assert(r[0].uri === '/route1');
|
||||||
});
|
});
|
||||||
@ -168,9 +172,9 @@ describe('Router', () => {
|
|||||||
it('route with query string', () => {
|
it('route with query string', () => {
|
||||||
let router = frontexpress.Router('/route1 ');
|
let router = frontexpress.Router('/route1 ');
|
||||||
|
|
||||||
router.get('/subroute', frontexpress.Middleware());
|
router.get('/subroute', new frontexpress.Middleware());
|
||||||
|
|
||||||
let r = router.routes('/route1/subroute?a=b&c=d', 'GET');
|
let r = router.routes(application, {uri: '/route1/subroute?a=b&c=d', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1/subroute');
|
assert(r[0].uri === '/route1/subroute');
|
||||||
assert(r[0].data === undefined);
|
assert(r[0].data === undefined);
|
||||||
@ -179,9 +183,9 @@ describe('Router', () => {
|
|||||||
it('route with anchor', () => {
|
it('route with anchor', () => {
|
||||||
let router = frontexpress.Router('/route1 ');
|
let router = frontexpress.Router('/route1 ');
|
||||||
|
|
||||||
router.get('/subroute', frontexpress.Middleware());
|
router.get('/subroute', new frontexpress.Middleware());
|
||||||
|
|
||||||
let r = router.routes('/route1/subroute#a=b&c=d', 'GET');
|
let r = router.routes(application, {uri: '/route1/subroute#a=b&c=d', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1/subroute');
|
assert(r[0].uri === '/route1/subroute');
|
||||||
assert(r[0].data === undefined);
|
assert(r[0].data === undefined);
|
||||||
@ -190,9 +194,9 @@ describe('Router', () => {
|
|||||||
it('route with query string and anchor', () => {
|
it('route with query string and anchor', () => {
|
||||||
let router = frontexpress.Router('/route1 ');
|
let router = frontexpress.Router('/route1 ');
|
||||||
|
|
||||||
router.get('/subroute', frontexpress.Middleware());
|
router.get('/subroute', new frontexpress.Middleware());
|
||||||
|
|
||||||
let r = router.routes('/route1/subroute?a=b&c=d#anchor1', 'GET');
|
let r = router.routes(application, {uri: '/route1/subroute?a=b&c=d#anchor1', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1/subroute');
|
assert(r[0].uri === '/route1/subroute');
|
||||||
assert(r[0].data === undefined);
|
assert(r[0].data === undefined);
|
||||||
@ -214,7 +218,7 @@ describe('Router', () => {
|
|||||||
|
|
||||||
it('only middleware as argument', () => {
|
it('only middleware as argument', () => {
|
||||||
const router = frontexpress.Router('/route1');
|
const router = frontexpress.Router('/route1');
|
||||||
const middleware = frontexpress.Middleware();
|
const middleware = new frontexpress.Middleware();
|
||||||
|
|
||||||
const spied_methods = [];
|
const spied_methods = [];
|
||||||
for (const method of HTTP_METHODS) {
|
for (const method of HTTP_METHODS) {
|
||||||
@ -230,7 +234,7 @@ describe('Router', () => {
|
|||||||
|
|
||||||
it('with path /route1 and middleware as arguments', () => {
|
it('with path /route1 and middleware as arguments', () => {
|
||||||
const router = frontexpress.Router();
|
const router = frontexpress.Router();
|
||||||
const middleware = frontexpress.Middleware();
|
const middleware = new frontexpress.Middleware();
|
||||||
|
|
||||||
const spied_methods = [];
|
const spied_methods = [];
|
||||||
for (const method of HTTP_METHODS) {
|
for (const method of HTTP_METHODS) {
|
||||||
@ -260,11 +264,11 @@ describe('Router', () => {
|
|||||||
|
|
||||||
it('only middleware as argument', () => {
|
it('only middleware as argument', () => {
|
||||||
const router = frontexpress.Router('/');
|
const router = frontexpress.Router('/');
|
||||||
const middleware = frontexpress.Middleware();
|
const middleware = new frontexpress.Middleware();
|
||||||
|
|
||||||
router.get(middleware);
|
router.get(middleware);
|
||||||
|
|
||||||
const r = router.routes('/', 'GET');
|
const r = router.routes(application, {uri: '/', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/');
|
assert(r[0].uri === '/');
|
||||||
assert(r[0].method === 'GET');
|
assert(r[0].method === 'GET');
|
||||||
@ -273,11 +277,11 @@ describe('Router', () => {
|
|||||||
|
|
||||||
it('with path /route1 and middleware as arguments', () => {
|
it('with path /route1 and middleware as arguments', () => {
|
||||||
const router = frontexpress.Router();
|
const router = frontexpress.Router();
|
||||||
const middleware = frontexpress.Middleware();
|
const middleware = new frontexpress.Middleware();
|
||||||
|
|
||||||
router.get('/route1', middleware);
|
router.get('/route1', middleware);
|
||||||
|
|
||||||
const r = router.routes('/route1', 'GET');
|
const r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri === '/route1');
|
assert(r[0].uri === '/route1');
|
||||||
assert(r[0].method === 'GET');
|
assert(r[0].method === 'GET');
|
||||||
@ -286,16 +290,16 @@ describe('Router', () => {
|
|||||||
|
|
||||||
it('router with regexp and route with /route1', () => {
|
it('router with regexp and route with /route1', () => {
|
||||||
const router = frontexpress.Router(/^\//);
|
const router = frontexpress.Router(/^\//);
|
||||||
const middleware = frontexpress.Middleware();
|
const middleware = new frontexpress.Middleware();
|
||||||
chai.expect(() => router.get('/route1', middleware)).to.throw(TypeError);
|
chai.expect(() => router.get('/route1', middleware)).to.throw(TypeError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('router with regexp and route without uri', () => {
|
it('router with regexp and route without uri', () => {
|
||||||
const router = frontexpress.Router(/^\/part/);
|
const router = frontexpress.Router(/^\/part/);
|
||||||
const middleware = frontexpress.Middleware();
|
const middleware = new frontexpress.Middleware();
|
||||||
router.get(middleware);
|
router.get(middleware);
|
||||||
|
|
||||||
const r = router.routes('/part1', 'GET');
|
const r = router.routes(application, {uri: '/part1', method: 'GET'});
|
||||||
assert(r.length === 1);
|
assert(r.length === 1);
|
||||||
assert(r[0].uri instanceof RegExp);
|
assert(r[0].uri instanceof RegExp);
|
||||||
assert(r[0].uri.toString() === new RegExp('^\/part').toString());
|
assert(r[0].uri.toString() === new RegExp('^\/part').toString());
|
||||||
@ -303,4 +307,113 @@ describe('Router', () => {
|
|||||||
assert(r[0].middleware === middleware);
|
assert(r[0].middleware === middleware);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('check route matcher', () => {
|
||||||
|
it('/', () => {
|
||||||
|
const route = {uri: '/', method: 'GET'};
|
||||||
|
|
||||||
|
const request = {uri: '/', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/a/b/c', () => {
|
||||||
|
const route = {uri: '/a/b/c', method: 'GET'};
|
||||||
|
|
||||||
|
let request = {uri: '/a/b/c', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {});
|
||||||
|
|
||||||
|
request = {uri: '/a/b/c/', method: 'GET', params: {}};
|
||||||
|
assert.strictEqual(routeMatcher(request, route), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/^\//', () => {
|
||||||
|
const route = {uri: /^\//, method: 'GET'};
|
||||||
|
|
||||||
|
const request = {uri: '/a/b/c', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/:id', () => {
|
||||||
|
const route = {uri: '/:id', method: 'GET'};
|
||||||
|
|
||||||
|
const request = {uri: '/1000', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.strictEqual(request.params.id, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/user/:id', () => {
|
||||||
|
const route = {uri: '/user/:id', method: 'GET'};
|
||||||
|
|
||||||
|
let request = {uri: '/user/1000', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.strictEqual(request.params.id, 1000);
|
||||||
|
|
||||||
|
request = {uri: '/user/100.2122', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.strictEqual(request.params.id, 100.2122);
|
||||||
|
|
||||||
|
request = {uri: '/user', method: 'GET', params: {}};
|
||||||
|
assert.strictEqual(routeMatcher(request, route), false);
|
||||||
|
|
||||||
|
request = {uri: '/user/', method: 'GET', params: {}};
|
||||||
|
assert.strictEqual(routeMatcher(request, route), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/user/:id with id as coma separated values', () => {
|
||||||
|
const route = {uri: '/user/:id', method: 'GET'};
|
||||||
|
|
||||||
|
let request = {uri: '/user/1,2,3', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {id: [1,2,3]});
|
||||||
|
|
||||||
|
request = {uri: '/user/1.5,2.55,4.25', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {id: [1.5,2.55,4.25]});
|
||||||
|
|
||||||
|
request = {uri: '/user/a,b,c', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {id: ['a','b','c']});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/user/:id?', () => {
|
||||||
|
const route = {uri: '/user/:id?', method: 'GET'};
|
||||||
|
|
||||||
|
let request = {uri: '/user/1000', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.strictEqual(request.params.id, 1000);
|
||||||
|
|
||||||
|
request = {uri: '/user', method: 'GET', params: {}};
|
||||||
|
assert.strictEqual(routeMatcher(request, route), true);
|
||||||
|
assert.deepEqual(request.params, {id: undefined});
|
||||||
|
|
||||||
|
request = {uri: '/user/', method: 'GET', params: {}};
|
||||||
|
assert.strictEqual(routeMatcher(request, route), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/user/:firstname/:lastname', () => {
|
||||||
|
const route = {uri: '/user/:firstname/:lastname', method: 'GET'};
|
||||||
|
|
||||||
|
let request = {uri: '/user/camel/aissani', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {firstname: 'camel', lastname:'aissani'} );
|
||||||
|
|
||||||
|
request = {uri: '/user/camel', method: 'GET', params: {}};
|
||||||
|
assert.strictEqual(routeMatcher(request, route), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('/user/:firstname?/:lastname', () => {
|
||||||
|
const route = {uri: '/user/:firstname?/:lastname', method: 'GET'};
|
||||||
|
|
||||||
|
let request = {uri: '/user/camel/aissani', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {firstname: 'camel', lastname:'aissani'} );
|
||||||
|
|
||||||
|
request = {uri: '/user/aissani', method: 'GET', params: {}};
|
||||||
|
assert(routeMatcher(request, route));
|
||||||
|
assert.deepEqual(request.params, {firstname: undefined, lastname:'aissani'} );
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,17 +1,49 @@
|
|||||||
/*eslint-env mocha*/
|
/*eslint-env mocha*/
|
||||||
import {assert} from 'chai';
|
import chai, {assert} from 'chai';
|
||||||
import Settings from '../lib/settings';
|
import Settings from '../lib/settings';
|
||||||
|
|
||||||
describe('Settings', () => {
|
describe('Settings', () => {
|
||||||
const settings = new Settings();
|
const settings = new Settings();
|
||||||
|
|
||||||
describe('http GET method transformer', () => {
|
describe('http GET method transformer', () => {
|
||||||
|
it('check setting rule', () => {
|
||||||
|
const defaultHttpGetTransformer = settings.get('http GET transformer');
|
||||||
|
|
||||||
|
chai.expect(() => settings.set('http GET transformer', null)).to.throw(TypeError);
|
||||||
|
chai.expect(() => settings.set('http GET transformer', {})).to.throw(TypeError);
|
||||||
|
chai.expect(() => settings.set('http GET transformer', {foo:()=>{}})).to.throw(TypeError);
|
||||||
|
|
||||||
|
const uri = () => {};
|
||||||
|
settings.set('http GET transformer', {uri});
|
||||||
|
assert.deepEqual(settings.get('http GET transformer'), {uri});
|
||||||
|
|
||||||
|
const headers = () => {};
|
||||||
|
settings.set('http GET transformer', {headers});
|
||||||
|
assert.deepEqual(settings.get('http GET transformer'), {headers});
|
||||||
|
|
||||||
|
const data = () => {};
|
||||||
|
settings.set('http GET transformer', {data});
|
||||||
|
assert.deepEqual(settings.get('http GET transformer'), {data});
|
||||||
|
|
||||||
|
settings.set('http GET transformer', defaultHttpGetTransformer);
|
||||||
|
|
||||||
|
|
||||||
|
const defaultRouteMatcher = settings.get('route matcher');
|
||||||
|
chai.expect(() => settings.set('route matcher', null)).to.throw(TypeError);
|
||||||
|
chai.expect(() => settings.set('route matcher', {})).to.throw(TypeError);
|
||||||
|
chai.expect(() => settings.set('route matcher', 1)).to.throw(TypeError);
|
||||||
|
|
||||||
|
const routeMatcher = () => {};
|
||||||
|
settings.set('route matcher', routeMatcher);
|
||||||
|
assert.strictEqual(settings.get('route matcher'), routeMatcher);
|
||||||
|
});
|
||||||
|
|
||||||
it('simple uri', () => {
|
it('simple uri', () => {
|
||||||
const uriFn = settings.get('http GET transformer').uri;
|
const uriFn = settings.get('http GET transformer').uri;
|
||||||
const dataFn = settings.get('http GET transformer').data;
|
const dataFn = settings.get('http GET transformer').data;
|
||||||
|
|
||||||
assert(uriFn({uri: '/route', data:{a:'b', c:'d'}}) === '/route?a=b&c=d');
|
assert(uriFn({uri: '/route', data:{a:'b', c:'d'}}) === '/route?a=b&c=d');
|
||||||
assert(dataFn({uri: '/route', data:{a:'b', c:'d'}}) === undefined);
|
assert(dataFn === undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uri with query string', () => {
|
it('uri with query string', () => {
|
||||||
@ -19,7 +51,7 @@ describe('Settings', () => {
|
|||||||
const dataFn = settings.get('http GET transformer').data;
|
const dataFn = settings.get('http GET transformer').data;
|
||||||
|
|
||||||
assert(uriFn({uri: '/route?x=y&z=a', data:{a:'b', c:'d'}}) === '/route?x=y&z=a&a=b&c=d');
|
assert(uriFn({uri: '/route?x=y&z=a', data:{a:'b', c:'d'}}) === '/route?x=y&z=a&a=b&c=d');
|
||||||
assert(dataFn({uri: '/route?x=y&z=a', data:{a:'b', c:'d'}}) === undefined);
|
assert(dataFn === undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uri with query string and anchor', () => {
|
it('uri with query string and anchor', () => {
|
||||||
@ -27,7 +59,7 @@ describe('Settings', () => {
|
|||||||
const dataFn = settings.get('http GET transformer').data;
|
const dataFn = settings.get('http GET transformer').data;
|
||||||
|
|
||||||
assert(uriFn({uri: '/route?x=y&z=a#anchor1', data:{a:'b', c:'d'}}) === '/route?x=y&z=a&a=b&c=d#anchor1');
|
assert(uriFn({uri: '/route?x=y&z=a#anchor1', data:{a:'b', c:'d'}}) === '/route?x=y&z=a&a=b&c=d#anchor1');
|
||||||
assert(dataFn({uri: '/route?x=y&z=a#anchor1', data:{a:'b', c:'d'}}) === undefined);
|
assert(dataFn === undefined);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user