Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
acebd1e5c0 | ||
|
d678d066c3 | ||
|
2245de31d7 | ||
|
065b500561 | ||
|
75b130ef91 | ||
|
72598f09e1 | ||
|
d7cfd6d9f3 | ||
|
21f5dea449 | ||
|
a1e56dc6c3 | ||
|
34b7580d18 | ||
|
4ab3fa2626 | ||
|
5b45250bf5 | ||
|
787a22ed44 | ||
|
79062fe337 | ||
|
f90743d4cf |
3
.babelrc
3
.babelrc
@ -1,4 +1,3 @@
|
||||
{
|
||||
"presets": ["es2015"],
|
||||
"plugins": ["add-module-exports"]
|
||||
"presets": ["es2015"]
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
index.js
|
||||
frontexpress.js
|
||||
frontexpress.min.js
|
||||
frontexpress.min.js.map
|
||||
gzipsize.js
|
||||
test
|
||||
coverage
|
300
README.md
300
README.md
@ -1,38 +1,16 @@
|
||||

|
||||
|
||||
Frontexpress manages routes in browser like [ExpressJS](http://expressjs.com/) does on Node.
|
||||
A simple vanilla JavaScript router a la [ExpressJS](http://expressjs.com/).
|
||||
|
||||
Same language same API on all the stack.
|
||||
Code the front-end like the back-end.
|
||||
|
||||
[frontexpress demo](https://github.com/camelaissani/frontexpress-demo)
|
||||
|
||||
[](https://travis-ci.org/camelaissani/frontexpress)
|
||||
[](https://codeclimate.com/github/camelaissani/frontexpress)
|
||||
[](https://coveralls.io/github/camelaissani/frontexpress?branch=master)
|
||||

|
||||

|
||||
|
||||
Code the front-end logic with the same style than on the back-end with express
|
||||
|
||||
```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 = `<h1>Page 1 content</h1>`;
|
||||
});
|
||||
|
||||
// front-end logic on navigation path "/page2"
|
||||
app.get('/page2', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = `<h1>Page 2 content</h1>`;
|
||||
});
|
||||
|
||||
// start front-end application
|
||||
app.listen(() => {
|
||||
// on DOM ready
|
||||
});
|
||||
```
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
@ -50,45 +28,33 @@ $ bower install frontexpress
|
||||
|
||||
### From CDN
|
||||
|
||||
On [jsDelivr](http://www.jsdelivr.com/?query=frontexpress)
|
||||
On [jsDelivr](https://cdn.jsdelivr.net/npm/frontexpress@1.2.0/frontexpress.min.js)
|
||||
|
||||
## Quick Start
|
||||
## Usage
|
||||
|
||||
The quickest way to get started with frontexpress is to clone the [frontexpress-demo](https://github.com/camelaissani/frontexpress-demo) repository.
|
||||
```js
|
||||
import frontexpress from 'frontexpress';
|
||||
|
||||
## Tests
|
||||
// Front-end application
|
||||
const app = frontexpress();
|
||||
|
||||
Clone the git repository:
|
||||
// front-end logic on navigation path "/page1"
|
||||
app.get('/page1', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = res.responseText;
|
||||
});
|
||||
|
||||
```bash
|
||||
$ git clone git@github.com:camelaissani/frontexpress.git
|
||||
$ cd frontexpress
|
||||
// front-end logic on navigation path "/page2"
|
||||
app.get('/page2', (req, res) => {
|
||||
document.querySelector('.content').innerHTML = res.responseText;
|
||||
});
|
||||
|
||||
// start front-end application
|
||||
app.listen();
|
||||
```
|
||||
|
||||
Install the dependencies and run the test suite:
|
||||
### Routes
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
$ npm test
|
||||
```
|
||||
|
||||
|
||||
## Navigation path and frontexpress routing
|
||||
|
||||
### Disclaimer
|
||||
|
||||
>
|
||||
> In this first version of frontexpress, the API is not completely the mirror of the expressjs one.
|
||||
>
|
||||
> There are some missing methods. Currently, the use, get, post... methods having a middleware 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.
|
||||
>
|
||||
|
||||
### Basic routing
|
||||
|
||||
Listen navigation (GET request) on path /hello:
|
||||
Listen GET requests on path /hello:
|
||||
|
||||
```js
|
||||
app.get('/hello', (req, res) => {
|
||||
@ -96,7 +62,7 @@ app.get('/hello', (req, res) => {
|
||||
});
|
||||
```
|
||||
|
||||
Listen a POST request on path /item:
|
||||
Listen POST requests on path /item:
|
||||
|
||||
```js
|
||||
app.post('/item', (req, res) => {
|
||||
@ -104,9 +70,7 @@ app.post('/item', (req, res) => {
|
||||
});
|
||||
```
|
||||
|
||||
### Routing based on RegExp
|
||||
|
||||
Listen navigation on paths which start with /api/:
|
||||
Listen GET requests on path starting with /api/:
|
||||
|
||||
```js
|
||||
app.get(/^api\//, (req, res) => {
|
||||
@ -114,6 +78,68 @@ app.get(/^api\//, (req, res) => {
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
@ -138,7 +164,7 @@ h2!
|
||||
|
||||
h3 is ignored because ```next()``` function was not invoked.
|
||||
|
||||
### app.route()
|
||||
#### app.route()
|
||||
|
||||
You can create chainable route handlers for a route path by using ```app.route()```.
|
||||
|
||||
@ -149,7 +175,7 @@ app.route('/book')
|
||||
.put((req, res) => { console.log('Update the book') });
|
||||
```
|
||||
|
||||
### frontexpress.Router
|
||||
#### frontexpress.Router
|
||||
|
||||
Use the ```frontexpress.Router``` class to create modular, mountable route handlers.
|
||||
|
||||
@ -187,121 +213,77 @@ import birds from './birds';
|
||||
app.use('/birds', birds);
|
||||
```
|
||||
|
||||
## API
|
||||
## Plugins
|
||||
|
||||
| | 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|
|
||||
### 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*...
|
||||
|
||||
### middleware function
|
||||
Others are coming
|
||||
|
||||
After registering a middleware function, the application invokes it with these parameters:
|
||||
### 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
|
||||
(request, response, next) => {
|
||||
next();
|
||||
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(...)
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**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
|
||||
To use it:
|
||||
|
||||
```js
|
||||
{
|
||||
method,
|
||||
uri,
|
||||
headers,
|
||||
data,
|
||||
history: {
|
||||
state,
|
||||
title,
|
||||
uri
|
||||
}
|
||||
}
|
||||
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);
|
||||
```
|
||||
|
||||
**method**: `String`, HTTP methods 'GET', 'POST'...
|
||||
## More
|
||||
|
||||
**uri**: `String`, path
|
||||
[API](https://github.com/camelaissani/frontexpress/blob/master/docs/api.md)
|
||||
|
||||
**headers**: `Object`, custom HTTP headers
|
||||
## Tests
|
||||
|
||||
**data**: `Object`, data attached to the request
|
||||
Clone the git repository:
|
||||
|
||||
**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.
|
||||
|
||||
|
||||
### response object
|
||||
|
||||
```js
|
||||
{
|
||||
status,
|
||||
statusText,
|
||||
responseText,
|
||||
errorThrown,
|
||||
errors
|
||||
}
|
||||
```bash
|
||||
$ git clone git@github.com:camelaissani/frontexpress.git
|
||||
$ cd frontexpress
|
||||
```
|
||||
|
||||
**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)
|
||||
Install the dependencies and run the test suite:
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
@ -21,6 +21,7 @@
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"index.js",
|
||||
"gzipsize.js",
|
||||
"rollup.config.dev.js",
|
||||
"rollup.config.prod.js",
|
||||
"node_modules",
|
||||
|
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)
|
750
frontexpress.js
750
frontexpress.js
@ -6,9 +6,9 @@ var frontexpress = (function () {
|
||||
* @private
|
||||
*/
|
||||
|
||||
var HTTP_METHODS = ['GET', 'POST', 'PUT', 'DELETE'];
|
||||
var HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
||||
// not supported yet
|
||||
// HEAD', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH';
|
||||
// HEAD', 'CONNECT', 'OPTIONS', 'TRACE';
|
||||
|
||||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
|
||||
return typeof obj;
|
||||
@ -136,200 +136,6 @@ var toConsumableArray = function (arr) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Requester = function () {
|
||||
function Requester() {
|
||||
classCallCheck(this, Requester);
|
||||
}
|
||||
|
||||
createClass(Requester, [{
|
||||
key: 'fetch',
|
||||
|
||||
|
||||
/**
|
||||
* Make an ajax request.
|
||||
*
|
||||
* @param {Object} request
|
||||
* @param {Function} success callback
|
||||
* @param {Function} failure callback
|
||||
* @private
|
||||
*/
|
||||
|
||||
value: function fetch(request, resolve, reject) {
|
||||
var method = request.method,
|
||||
uri = request.uri,
|
||||
headers = request.headers,
|
||||
data = request.data;
|
||||
|
||||
|
||||
var success = function success(responseText) {
|
||||
resolve(request, {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
responseText: responseText
|
||||
});
|
||||
};
|
||||
|
||||
var fail = function fail(_ref) {
|
||||
var status = _ref.status,
|
||||
statusText = _ref.statusText,
|
||||
errorThrown = _ref.errorThrown;
|
||||
|
||||
reject(request, {
|
||||
status: status,
|
||||
statusText: statusText,
|
||||
errorThrown: errorThrown,
|
||||
errors: 'HTTP ' + status + ' ' + (statusText ? statusText : '')
|
||||
});
|
||||
};
|
||||
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.onreadystatechange = function () {
|
||||
if (xmlhttp.readyState === 4) {
|
||||
//XMLHttpRequest.DONE
|
||||
if (xmlhttp.status === 200) {
|
||||
success(xmlhttp.responseText);
|
||||
} else {
|
||||
fail({ status: xmlhttp.status, statusText: xmlhttp.statusText });
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
xmlhttp.open(method, uri, true);
|
||||
if (headers) {
|
||||
Object.keys(headers).forEach(function (header) {
|
||||
xmlhttp.setRequestHeader(header, headers[header]);
|
||||
});
|
||||
}
|
||||
if (data) {
|
||||
xmlhttp.send(data);
|
||||
} else {
|
||||
xmlhttp.send();
|
||||
}
|
||||
} catch (errorThrown) {
|
||||
fail({ errorThrown: errorThrown });
|
||||
}
|
||||
}
|
||||
}]);
|
||||
return Requester;
|
||||
}();
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
/**
|
||||
* Settings object.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Settings = function () {
|
||||
|
||||
/**
|
||||
* Initialize the settings.
|
||||
*
|
||||
* - setup default configuration
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
function Settings() {
|
||||
classCallCheck(this, Settings);
|
||||
|
||||
// default settings
|
||||
this.settings = {
|
||||
'http requester': new Requester(),
|
||||
|
||||
'http GET transformer': {
|
||||
uri: function uri(_ref) {
|
||||
var _uri = _ref.uri,
|
||||
headers = _ref.headers,
|
||||
data = _ref.data;
|
||||
|
||||
if (!data) {
|
||||
return _uri;
|
||||
}
|
||||
var uriWithoutAnchor = _uri,
|
||||
anchor = '';
|
||||
|
||||
var match = /^(.*)(#.*)$/.exec(_uri);
|
||||
if (match) {
|
||||
var _$exec = /^(.*)(#.*)$/.exec(_uri);
|
||||
|
||||
var _$exec2 = slicedToArray(_$exec, 3);
|
||||
|
||||
uriWithoutAnchor = _$exec2[1];
|
||||
anchor = _$exec2[2];
|
||||
}
|
||||
uriWithoutAnchor = Object.keys(data).reduce(function (gUri, d, index) {
|
||||
gUri += '' + (index === 0 && gUri.indexOf('?') === -1 ? '?' : '&') + d + '=' + data[d];
|
||||
return gUri;
|
||||
}, uriWithoutAnchor);
|
||||
return uriWithoutAnchor + anchor;
|
||||
}
|
||||
}
|
||||
// 'http POST transformer': {
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
};
|
||||
|
||||
this.rules = {
|
||||
'http requester': function httpRequester(requester) {
|
||||
if (typeof requester.fetch !== 'function') {
|
||||
throw new TypeError('setting http requester has no fetch method');
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign `setting` to `val`
|
||||
*
|
||||
* @param {String} setting
|
||||
* @param {*} [val]
|
||||
* @private
|
||||
*/
|
||||
|
||||
createClass(Settings, [{
|
||||
key: 'set',
|
||||
value: function set$$1(name, value) {
|
||||
var checkRules = this.rules[name];
|
||||
if (checkRules) {
|
||||
checkRules(value);
|
||||
}
|
||||
this.settings[name] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `setting`'s value.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @private
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'get',
|
||||
value: function get$$1(name) {
|
||||
return this.settings[name];
|
||||
}
|
||||
}]);
|
||||
return Settings;
|
||||
}();
|
||||
|
||||
/**
|
||||
* Middleware object.
|
||||
* @public
|
||||
@ -361,61 +167,66 @@ var Middleware = function () {
|
||||
* @public
|
||||
*/
|
||||
|
||||
// entered(request) { }
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
createClass(Middleware, [{
|
||||
key: 'entered',
|
||||
value: function entered(request) {}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'exited',
|
||||
value: function 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
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'updated',
|
||||
value: function 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
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'failed',
|
||||
value: function 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
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'next',
|
||||
value: function next() {
|
||||
return true;
|
||||
@ -538,26 +349,11 @@ var Router = function () {
|
||||
|
||||
}, {
|
||||
key: 'routes',
|
||||
value: function routes(uri, method) {
|
||||
value: function routes(application, request) {
|
||||
request.params = request.params || {};
|
||||
var isRouteMatch = application.get('route matcher');
|
||||
return this._routes.filter(function (route) {
|
||||
if (route.method && route.method !== method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!route.uri || !uri) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//remove query string from uri to test
|
||||
//remove anchor from uri to test
|
||||
var match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(uri);
|
||||
var baseUriToCheck = match[1] || match[2] || match[3];
|
||||
|
||||
if (route.uri instanceof RegExp) {
|
||||
return baseUriToCheck.match(route.uri);
|
||||
}
|
||||
|
||||
return route.uri === baseUriToCheck;
|
||||
return isRouteMatch(request, route);
|
||||
});
|
||||
}
|
||||
|
||||
@ -719,6 +515,284 @@ HTTP_METHODS.forEach(function (method) {
|
||||
};
|
||||
});
|
||||
|
||||
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
|
||||
var match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(request.uri);
|
||||
var 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) {
|
||||
|
||||
var decodeParmeterValue = function decodeParmeterValue(v) {
|
||||
return !isNaN(parseFloat(v)) && isFinite(v) ? Number.isInteger(v) ? Number.parseInt(v, 10) : Number.parseFloat(v) : v;
|
||||
};
|
||||
|
||||
// figure out key names
|
||||
var keys = [];
|
||||
var keysRE = /:([^\/\?]+)\??/g;
|
||||
var keysMatch = keysRE.exec(route.uri);
|
||||
while (keysMatch != null) {
|
||||
keys.push(keysMatch[1]);
|
||||
keysMatch = keysRE.exec(route.uri);
|
||||
}
|
||||
|
||||
// change parameterized path to regexp
|
||||
var regExpUri = route.uri
|
||||
// :parameter?
|
||||
.replace(/\/:[^\/]+\?/g, '(?:\/([^\/]+))?')
|
||||
// :parameter
|
||||
.replace(/:[^\/]+/g, '([^\/]+)')
|
||||
// escape all /
|
||||
.replace('/', '\\/');
|
||||
|
||||
// checks if uri match
|
||||
var routeMatch = baseUriToCheck.match(new RegExp('^' + regExpUri + '$'));
|
||||
if (!routeMatch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// update params in request with keys
|
||||
request.params = Object.assign(request.params, keys.reduce(function (acc, key, index) {
|
||||
var value = routeMatch[index + 1];
|
||||
if (value) {
|
||||
value = value.indexOf(',') !== -1 ? value.split(',').map(function (v) {
|
||||
return decodeParmeterValue(v);
|
||||
}) : value = decodeParmeterValue(value);
|
||||
}
|
||||
acc[key] = value;
|
||||
return acc;
|
||||
}, {}));
|
||||
return true;
|
||||
}
|
||||
|
||||
// if route is a simple path
|
||||
return route.uri === baseUriToCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Requester = function () {
|
||||
function Requester() {
|
||||
classCallCheck(this, Requester);
|
||||
}
|
||||
|
||||
createClass(Requester, [{
|
||||
key: 'fetch',
|
||||
|
||||
|
||||
/**
|
||||
* Make an ajax request.
|
||||
*
|
||||
* @param {Object} request
|
||||
* @param {Function} success callback
|
||||
* @param {Function} failure callback
|
||||
* @private
|
||||
*/
|
||||
|
||||
value: function fetch(request, resolve, reject) {
|
||||
var method = request.method,
|
||||
uri = request.uri,
|
||||
headers = request.headers,
|
||||
data = request.data;
|
||||
|
||||
|
||||
var success = function success(responseText) {
|
||||
resolve(request, {
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
responseText: responseText
|
||||
});
|
||||
};
|
||||
|
||||
var fail = function fail(_ref) {
|
||||
var status = _ref.status,
|
||||
statusText = _ref.statusText,
|
||||
errorThrown = _ref.errorThrown;
|
||||
|
||||
reject(request, {
|
||||
status: status,
|
||||
statusText: statusText,
|
||||
errorThrown: errorThrown,
|
||||
errors: 'HTTP ' + status + ' ' + (statusText ? statusText : '')
|
||||
});
|
||||
};
|
||||
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.onreadystatechange = function () {
|
||||
if (xmlhttp.readyState === 4) {
|
||||
//XMLHttpRequest.DONE
|
||||
if (xmlhttp.status === 200) {
|
||||
success(xmlhttp.responseText);
|
||||
} else {
|
||||
fail({ status: xmlhttp.status, statusText: xmlhttp.statusText });
|
||||
}
|
||||
}
|
||||
};
|
||||
try {
|
||||
xmlhttp.open(method, uri, true);
|
||||
if (headers) {
|
||||
Object.keys(headers).forEach(function (header) {
|
||||
xmlhttp.setRequestHeader(header, headers[header]);
|
||||
});
|
||||
}
|
||||
if (data) {
|
||||
xmlhttp.send(data);
|
||||
} else {
|
||||
xmlhttp.send();
|
||||
}
|
||||
} catch (errorThrown) {
|
||||
fail({ errorThrown: errorThrown });
|
||||
}
|
||||
}
|
||||
}]);
|
||||
return Requester;
|
||||
}();
|
||||
|
||||
var httpGetTransformer = {
|
||||
uri: function uri(_ref2) {
|
||||
var _uri = _ref2.uri,
|
||||
headers = _ref2.headers,
|
||||
data = _ref2.data;
|
||||
|
||||
if (!data) {
|
||||
return _uri;
|
||||
}
|
||||
var uriWithoutAnchor = _uri,
|
||||
anchor = '';
|
||||
|
||||
var match = /^(.*)(#.*)$/.exec(_uri);
|
||||
if (match) {
|
||||
var _$exec = /^(.*)(#.*)$/.exec(_uri);
|
||||
|
||||
var _$exec2 = slicedToArray(_$exec, 3);
|
||||
|
||||
uriWithoutAnchor = _$exec2[1];
|
||||
anchor = _$exec2[2];
|
||||
}
|
||||
uriWithoutAnchor = Object.keys(data).reduce(function (gUri, d, index) {
|
||||
gUri += '' + (index === 0 && gUri.indexOf('?') === -1 ? '?' : '&') + d + '=' + data[d];
|
||||
return gUri;
|
||||
}, uriWithoutAnchor);
|
||||
return uriWithoutAnchor + anchor;
|
||||
}
|
||||
};
|
||||
|
||||
// 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;
|
||||
// }
|
||||
// };
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
function errorIfNotFunction(toTest, message) {
|
||||
if (typeof toTest !== 'function') {
|
||||
throw new TypeError(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings object.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Settings = function () {
|
||||
|
||||
/**
|
||||
* Initialize the settings.
|
||||
*
|
||||
* - setup default configuration
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
function Settings() {
|
||||
classCallCheck(this, Settings);
|
||||
|
||||
// default settings
|
||||
this.settings = {
|
||||
'http requester': new Requester(),
|
||||
'http GET transformer': httpGetTransformer,
|
||||
// 'http POST transformer': httpPostTransformer,
|
||||
'route matcher': routeMatcher
|
||||
};
|
||||
|
||||
this.rules = {
|
||||
'http requester': function httpRequester(requester) {
|
||||
errorIfNotFunction(requester.fetch, 'setting http requester has no fetch function');
|
||||
},
|
||||
'http GET transformer': function httpGETTransformer(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': function routeMatcher$$1(_routeMatcher) {
|
||||
errorIfNotFunction(_routeMatcher, 'setting route matcher is not a function');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign `setting` to `val`
|
||||
*
|
||||
* @param {String} setting
|
||||
* @param {*} [val]
|
||||
* @private
|
||||
*/
|
||||
|
||||
createClass(Settings, [{
|
||||
key: 'set',
|
||||
value: function set$$1(name, value) {
|
||||
var checkRules = this.rules[name];
|
||||
if (checkRules) {
|
||||
checkRules(value);
|
||||
}
|
||||
this.settings[name] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return `setting`'s value.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @private
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'get',
|
||||
value: function get$$1(name) {
|
||||
return this.settings[name];
|
||||
}
|
||||
}]);
|
||||
return Settings;
|
||||
}();
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
@ -742,9 +816,8 @@ var Application = function () {
|
||||
classCallCheck(this, Application);
|
||||
|
||||
this.routers = [];
|
||||
// this.isDOMLoaded = false;
|
||||
// this.isDOMReady = false;
|
||||
this.settings = new Settings();
|
||||
this.plugins = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -763,6 +836,8 @@ var Application = function () {
|
||||
createClass(Application, [{
|
||||
key: 'set',
|
||||
value: function set$$1() {
|
||||
var _settings;
|
||||
|
||||
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
@ -773,10 +848,7 @@ var Application = function () {
|
||||
}
|
||||
|
||||
// set behaviour
|
||||
var name = args[0],
|
||||
value = args[1];
|
||||
|
||||
this.settings.set(name, value);
|
||||
(_settings = this.settings).set.apply(_settings, args);
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -802,6 +874,12 @@ var Application = function () {
|
||||
value: function listen(callback) {
|
||||
var _this = this;
|
||||
|
||||
var request = { method: 'GET', uri: window.location.pathname + window.location.search };
|
||||
var response = { status: 200, statusText: 'OK' };
|
||||
var currentRoutes = this._routes(request);
|
||||
|
||||
this._callMiddlewareMethod('entered', currentRoutes, request);
|
||||
|
||||
// manage history
|
||||
window.onpopstate = function (event) {
|
||||
if (event.state) {
|
||||
@ -809,38 +887,31 @@ var Application = function () {
|
||||
_request = _event$state.request,
|
||||
_response = _event$state.response;
|
||||
|
||||
var _currentRoutes = _this._routes(_request.uri, _request.method);
|
||||
|
||||
_this._callMiddlewareMethod('entered', _currentRoutes, _request);
|
||||
_this._callMiddlewareMethod('updated', _currentRoutes, _request, _response);
|
||||
['exited', 'entered', 'updated'].forEach(function (middlewareMethod) {
|
||||
return _this._callMiddlewareMethod(middlewareMethod, _this._routes(_request), _request, _response);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// manage page loading/refreshing
|
||||
var request = { method: 'GET', uri: window.location.pathname + window.location.search };
|
||||
var response = { status: 200, statusText: 'OK' };
|
||||
var currentRoutes = this._routes();
|
||||
window.onbeforeunload = function () {
|
||||
_this._callMiddlewareMethod('exited');
|
||||
};
|
||||
|
||||
var whenPageIsInteractiveFn = function whenPageIsInteractiveFn() {
|
||||
_this.plugins.forEach(function (pluginObject) {
|
||||
return pluginObject.plugin(_this);
|
||||
});
|
||||
_this._callMiddlewareMethod('updated', currentRoutes, request, response);
|
||||
if (callback) {
|
||||
callback(request, response);
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function () {
|
||||
_this._callMiddlewareMethod('exited');
|
||||
};
|
||||
|
||||
document.onreadystatechange = function () {
|
||||
// DOM ready state
|
||||
switch (document.readyState) {
|
||||
case 'loading':
|
||||
_this._callMiddlewareMethod('entered', currentRoutes, request);
|
||||
break;
|
||||
case 'interactive':
|
||||
whenPageIsInteractiveFn();
|
||||
break;
|
||||
if (document.readyState === 'interactive') {
|
||||
whenPageIsInteractiveFn();
|
||||
}
|
||||
};
|
||||
|
||||
@ -873,6 +944,7 @@ var Application = function () {
|
||||
/**
|
||||
* 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')});
|
||||
@ -880,8 +952,16 @@ var Application = function () {
|
||||
* // 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} middleware object or function
|
||||
* @param {Middleware|Function|plugin} middleware object, middleware function, plugin
|
||||
* @return {app} for chaining
|
||||
*
|
||||
* @public
|
||||
@ -897,19 +977,24 @@ var Application = function () {
|
||||
var _toParameters = toParameters(args),
|
||||
baseUri = _toParameters.baseUri,
|
||||
router = _toParameters.router,
|
||||
middleware = _toParameters.middleware;
|
||||
middleware = _toParameters.middleware,
|
||||
plugin = _toParameters.plugin;
|
||||
|
||||
if (router) {
|
||||
router.baseUri = baseUri;
|
||||
} else if (middleware) {
|
||||
router = new Router(baseUri);
|
||||
HTTP_METHODS.forEach(function (method) {
|
||||
router[method.toLowerCase()](middleware);
|
||||
});
|
||||
if (plugin) {
|
||||
this.plugins.push(plugin);
|
||||
} else {
|
||||
throw new TypeError('method takes at least a middleware or a router');
|
||||
if (router) {
|
||||
router.baseUri = baseUri;
|
||||
} else if (middleware) {
|
||||
router = new Router(baseUri);
|
||||
HTTP_METHODS.forEach(function (method) {
|
||||
router[method.toLowerCase()](middleware);
|
||||
});
|
||||
} else {
|
||||
throw new TypeError('method takes at least a middleware or a router');
|
||||
}
|
||||
this.routers.push(router);
|
||||
}
|
||||
this.routers.push(router);
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -923,16 +1008,13 @@ var Application = function () {
|
||||
|
||||
}, {
|
||||
key: '_routes',
|
||||
value: function _routes() {
|
||||
var uri = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.location.pathname + window.location.search;
|
||||
var method = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'GET';
|
||||
value: function _routes(request) {
|
||||
var _this2 = this;
|
||||
|
||||
var currentRoutes = [];
|
||||
this.routers.forEach(function (router) {
|
||||
currentRoutes.push.apply(currentRoutes, toConsumableArray(router.routes(uri, method)));
|
||||
});
|
||||
|
||||
return currentRoutes;
|
||||
return this.routers.reduce(function (acc, router) {
|
||||
acc.push.apply(acc, toConsumableArray(router.routes(_this2, request)));
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -992,7 +1074,7 @@ var Application = function () {
|
||||
}, {
|
||||
key: '_fetch',
|
||||
value: function _fetch(req, resolve, reject) {
|
||||
var _this2 = this;
|
||||
var _this3 = this;
|
||||
|
||||
var method = req.method,
|
||||
uri = req.uri,
|
||||
@ -1007,16 +1089,16 @@ var Application = function () {
|
||||
_headersFn = httpMethodTransformer.headers,
|
||||
_dataFn = httpMethodTransformer.data;
|
||||
|
||||
uri = _uriFn ? _uriFn({ uri: uri, headers: headers, data: data }) : uri;
|
||||
headers = _headersFn ? _headersFn({ uri: uri, headers: headers, data: data }) : headers;
|
||||
data = _dataFn ? _dataFn({ uri: uri, headers: headers, data: data }) : data;
|
||||
req.uri = _uriFn ? _uriFn({ uri: uri, headers: headers, data: data }) : uri;
|
||||
req.headers = _headersFn ? _headersFn({ uri: uri, headers: headers, data: data }) : headers;
|
||||
req.data = _dataFn ? _dataFn({ uri: uri, headers: headers, data: data }) : data;
|
||||
}
|
||||
|
||||
// calls middleware exited method
|
||||
this._callMiddlewareMethod('exited');
|
||||
|
||||
// gathers all routes impacted by the uri
|
||||
var currentRoutes = this._routes(uri, method);
|
||||
var currentRoutes = this._routes(req);
|
||||
|
||||
// calls middleware entered method
|
||||
this._callMiddlewareMethod('entered', currentRoutes, req);
|
||||
@ -1026,12 +1108,12 @@ var Application = function () {
|
||||
if (history) {
|
||||
window.history.pushState({ request: request, response: response }, history.title, history.uri);
|
||||
}
|
||||
_this2._callMiddlewareMethod('updated', currentRoutes, request, response);
|
||||
_this3._callMiddlewareMethod('updated', currentRoutes, request, response);
|
||||
if (resolve) {
|
||||
resolve(request, response);
|
||||
}
|
||||
}, function (request, response) {
|
||||
_this2._callMiddlewareMethod('failed', currentRoutes, request, response);
|
||||
_this3._callMiddlewareMethod('failed', currentRoutes, request, response);
|
||||
if (reject) {
|
||||
reject(request, response);
|
||||
}
|
||||
@ -1133,29 +1215,25 @@ HTTP_METHODS.reduce(function (reqProto, method) {
|
||||
}, Application.prototype);
|
||||
|
||||
function toParameters(args) {
|
||||
var _args, _args2, _args3, _args4;
|
||||
|
||||
var baseUri = void 0,
|
||||
middleware = void 0,
|
||||
router = void 0,
|
||||
plugin = void 0,
|
||||
which = void 0;
|
||||
if (args && args.length > 0) {
|
||||
if (args.length === 1) {
|
||||
var _args = slicedToArray(args, 1);
|
||||
|
||||
which = _args[0];
|
||||
} else {
|
||||
var _args2 = slicedToArray(args, 2);
|
||||
args.length === 1 ? (_args = args, _args2 = slicedToArray(_args, 1), which = _args2[0], _args) : (_args3 = args, _args4 = slicedToArray(_args3, 2), baseUri = _args4[0], which = _args4[1], _args3);
|
||||
|
||||
baseUri = _args2[0];
|
||||
which = _args2[1];
|
||||
}
|
||||
|
||||
if (which instanceof Router) {
|
||||
router = which;
|
||||
} else if (which instanceof Middleware || typeof which === 'function') {
|
||||
middleware = which;
|
||||
}
|
||||
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: baseUri, middleware: middleware, router: router, which: which };
|
||||
|
||||
return { baseUri: baseUri, middleware: middleware, router: router, plugin: plugin, which: which };
|
||||
}
|
||||
|
||||
/**
|
||||
|
2
frontexpress.min.js
vendored
2
frontexpress.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -26,9 +26,8 @@ export default class Application {
|
||||
|
||||
constructor() {
|
||||
this.routers = [];
|
||||
// this.isDOMLoaded = false;
|
||||
// this.isDOMReady = false;
|
||||
this.settings = new Settings();
|
||||
this.plugins = [];
|
||||
}
|
||||
|
||||
|
||||
@ -52,8 +51,7 @@ export default class Application {
|
||||
}
|
||||
|
||||
// set behaviour
|
||||
const [name, value] = args;
|
||||
this.settings.set(name, value);
|
||||
this.settings.set(...args);
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -76,43 +74,41 @@ export default class Application {
|
||||
*/
|
||||
|
||||
listen(callback) {
|
||||
const request = {method: 'GET', uri: window.location.pathname + window.location.search};
|
||||
const response = {status: 200, statusText: 'OK'};
|
||||
const currentRoutes = this._routes(request);
|
||||
|
||||
this._callMiddlewareMethod('entered', currentRoutes, request);
|
||||
|
||||
// manage history
|
||||
window.onpopstate = (event) => {
|
||||
if (event.state) {
|
||||
const {request, response} = event.state;
|
||||
const currentRoutes = this._routes(request.uri, request.method);
|
||||
|
||||
this._callMiddlewareMethod('entered', currentRoutes, request);
|
||||
this._callMiddlewareMethod('updated', currentRoutes, request, response);
|
||||
[
|
||||
'exited',
|
||||
'entered',
|
||||
'updated'
|
||||
].forEach(middlewareMethod => this._callMiddlewareMethod(middlewareMethod, this._routes(request), request, response));
|
||||
}
|
||||
};
|
||||
|
||||
// manage page loading/refreshing
|
||||
const request = {method: 'GET', uri: window.location.pathname + window.location.search};
|
||||
const response = {status: 200, statusText: 'OK'};
|
||||
const currentRoutes = this._routes();
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = () => {
|
||||
this._callMiddlewareMethod('exited');
|
||||
};
|
||||
|
||||
document.onreadystatechange = () => {
|
||||
// DOM ready state
|
||||
switch (document.readyState) {
|
||||
case 'loading':
|
||||
this._callMiddlewareMethod('entered', currentRoutes, request);
|
||||
break;
|
||||
case 'interactive':
|
||||
if (document.readyState === 'interactive') {
|
||||
whenPageIsInteractiveFn();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@ -145,6 +141,7 @@ export default class Application {
|
||||
/**
|
||||
* 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')});
|
||||
@ -152,26 +149,38 @@ export default class Application {
|
||||
* // 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} middleware object or function
|
||||
* @param {Middleware|Function|plugin} middleware object, middleware function, plugin
|
||||
* @return {app} for chaining
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
use(...args) {
|
||||
let {baseUri, router, middleware} = toParameters(args);
|
||||
if (router) {
|
||||
router.baseUri = baseUri;
|
||||
} else if (middleware) {
|
||||
router = new Router(baseUri);
|
||||
HTTP_METHODS.forEach((method) => {
|
||||
router[method.toLowerCase()](middleware);
|
||||
});
|
||||
let {baseUri, router, middleware, plugin} = toParameters(args);
|
||||
if (plugin) {
|
||||
this.plugins.push(plugin);
|
||||
} else {
|
||||
throw new TypeError('method takes at least a middleware or a router');
|
||||
if (router) {
|
||||
router.baseUri = baseUri;
|
||||
} else if (middleware) {
|
||||
router = new Router(baseUri);
|
||||
HTTP_METHODS.forEach((method) => {
|
||||
router[method.toLowerCase()](middleware);
|
||||
});
|
||||
} else {
|
||||
throw new TypeError('method takes at least a middleware or a router');
|
||||
}
|
||||
this.routers.push(router);
|
||||
}
|
||||
this.routers.push(router);
|
||||
|
||||
return this;
|
||||
}
|
||||
@ -184,13 +193,11 @@ export default class Application {
|
||||
* @private
|
||||
*/
|
||||
|
||||
_routes(uri=window.location.pathname + window.location.search, method='GET') {
|
||||
const currentRoutes = [];
|
||||
this.routers.forEach((router) => {
|
||||
currentRoutes.push(...router.routes(uri, method));
|
||||
});
|
||||
|
||||
return currentRoutes;
|
||||
_routes(request) {
|
||||
return this.routers.reduce((acc, router) => {
|
||||
acc.push(...router.routes(this, request));
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
|
||||
@ -253,16 +260,16 @@ export default class Application {
|
||||
const httpMethodTransformer = this.get(`http ${method} transformer`);
|
||||
if (httpMethodTransformer) {
|
||||
const {uri: _uriFn, headers: _headersFn, data: _dataFn } = httpMethodTransformer;
|
||||
uri = _uriFn ? _uriFn({uri, headers, data}) : uri;
|
||||
headers = _headersFn ? _headersFn({uri, headers, data}) : headers;
|
||||
data = _dataFn ? _dataFn({uri, headers, data}) : data;
|
||||
req.uri = _uriFn ? _uriFn({uri, headers, data}) : uri;
|
||||
req.headers = _headersFn ? _headersFn({uri, headers, data}) : headers;
|
||||
req.data = _dataFn ? _dataFn({uri, headers, data}) : data;
|
||||
}
|
||||
|
||||
// calls middleware exited method
|
||||
this._callMiddlewareMethod('exited');
|
||||
|
||||
// gathers all routes impacted by the uri
|
||||
const currentRoutes = this._routes(uri, method);
|
||||
const currentRoutes = this._routes(req);
|
||||
|
||||
// calls middleware entered method
|
||||
this._callMiddlewareMethod('entered', currentRoutes, req);
|
||||
@ -369,19 +376,17 @@ HTTP_METHODS.reduce((reqProto, method) => {
|
||||
|
||||
|
||||
export function toParameters(args) {
|
||||
let baseUri, middleware, router, which;
|
||||
if (args && args.length > 0) {
|
||||
if (args.length === 1) {
|
||||
[which,] = args;
|
||||
} else {
|
||||
[baseUri, which,] = args;
|
||||
}
|
||||
let baseUri, middleware, router, plugin, which;
|
||||
|
||||
if (which instanceof Router) {
|
||||
router = which;
|
||||
} else if ((which instanceof Middleware) || (typeof which === 'function')) {
|
||||
middleware = 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, which};
|
||||
|
||||
return {baseUri, middleware, router, plugin, which};
|
||||
}
|
||||
|
@ -3,6 +3,6 @@
|
||||
* @private
|
||||
*/
|
||||
|
||||
export default ['GET', 'POST', 'PUT', 'DELETE'];
|
||||
export default ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];
|
||||
// not supported yet
|
||||
// HEAD', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH';
|
||||
// HEAD', 'CONNECT', 'OPTIONS', 'TRACE';
|
||||
|
@ -27,7 +27,7 @@ export default class Middleware {
|
||||
* @public
|
||||
*/
|
||||
|
||||
// entered(request) { }
|
||||
entered(request) { }
|
||||
|
||||
|
||||
/**
|
||||
@ -40,7 +40,7 @@ export default class Middleware {
|
||||
* @public
|
||||
*/
|
||||
|
||||
// exited(request) { }
|
||||
exited(request) { }
|
||||
|
||||
|
||||
/**
|
||||
@ -55,7 +55,7 @@ export default class Middleware {
|
||||
* @public
|
||||
*/
|
||||
|
||||
// updated(request, response) { }
|
||||
updated(request, response) { }
|
||||
|
||||
|
||||
/**
|
||||
@ -67,7 +67,7 @@ export default class Middleware {
|
||||
* @param {Object} response
|
||||
* @public
|
||||
*/
|
||||
// failed(request, response) { }
|
||||
failed(request, response) { }
|
||||
|
||||
|
||||
/**
|
||||
|
@ -67,3 +67,34 @@ export default class Requester {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const httpGetTransformer = {
|
||||
uri({uri, headers, data}) {
|
||||
if (!data) {
|
||||
return uri;
|
||||
}
|
||||
let [uriWithoutAnchor, anchor] = [uri, ''];
|
||||
const match = /^(.*)(#.*)$/.exec(uri);
|
||||
if (match) {
|
||||
[,uriWithoutAnchor, anchor] = /^(.*)(#.*)$/.exec(uri);
|
||||
}
|
||||
uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {
|
||||
gUri += `${(index === 0 && gUri.indexOf('?') === -1)?'?':'&'}${d}=${data[d]}`;
|
||||
return gUri;
|
||||
}, uriWithoutAnchor);
|
||||
return uriWithoutAnchor + anchor;
|
||||
}
|
||||
};
|
||||
|
||||
// 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;
|
||||
// }
|
||||
// };
|
||||
|
@ -135,26 +135,11 @@ export default class Router {
|
||||
* @private
|
||||
*/
|
||||
|
||||
routes(uri, method) {
|
||||
routes(application, request) {
|
||||
request.params = request.params || {};
|
||||
const isRouteMatch = application.get('route matcher');
|
||||
return this._routes.filter((route) => {
|
||||
if (route.method && route.method !== method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!route.uri || !uri) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//remove query string from uri to test
|
||||
//remove anchor from uri to test
|
||||
const match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(uri);
|
||||
const baseUriToCheck = match[1] || match[2] || match[3];
|
||||
|
||||
if (route.uri instanceof RegExp) {
|
||||
return baseUriToCheck.match(route.uri);
|
||||
}
|
||||
|
||||
return route.uri === baseUriToCheck;
|
||||
return isRouteMatch(request, route);
|
||||
});
|
||||
}
|
||||
|
||||
@ -267,3 +252,71 @@ HTTP_METHODS.forEach((method) => {
|
||||
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;
|
||||
}
|
||||
|
@ -2,9 +2,15 @@
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
import {routeMatcher} from './router';
|
||||
import Requester, {httpGetTransformer} from './requester';
|
||||
|
||||
import Requester from './requester';
|
||||
|
||||
function errorIfNotFunction(toTest, message) {
|
||||
if(typeof toTest !== 'function') {
|
||||
throw new TypeError(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings object.
|
||||
@ -26,43 +32,22 @@ export default class Settings {
|
||||
// default settings
|
||||
this.settings = {
|
||||
'http requester': new Requester(),
|
||||
|
||||
'http GET transformer': {
|
||||
uri({uri, headers, data}) {
|
||||
if (!data) {
|
||||
return uri;
|
||||
}
|
||||
let [uriWithoutAnchor, anchor] = [uri, ''];
|
||||
const match = /^(.*)(#.*)$/.exec(uri);
|
||||
if (match) {
|
||||
[,uriWithoutAnchor, anchor] = /^(.*)(#.*)$/.exec(uri);
|
||||
}
|
||||
uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {
|
||||
gUri += `${(index === 0 && gUri.indexOf('?') === -1)?'?':'&'}${d}=${data[d]}`;
|
||||
return gUri;
|
||||
}, uriWithoutAnchor);
|
||||
return uriWithoutAnchor + anchor;
|
||||
}
|
||||
}
|
||||
// 'http POST transformer': {
|
||||
// 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;
|
||||
// }
|
||||
// }
|
||||
'http GET transformer': httpGetTransformer,
|
||||
// 'http POST transformer': httpPostTransformer,
|
||||
'route matcher': routeMatcher
|
||||
};
|
||||
|
||||
this.rules = {
|
||||
'http requester': (requester) => {
|
||||
if(typeof requester.fetch !== 'function') {
|
||||
throw new TypeError('setting http requester has no fetch method');
|
||||
errorIfNotFunction(requester.fetch , 'setting http requester has no fetch function');
|
||||
},
|
||||
'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');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
34
package.json
34
package.json
@ -1,15 +1,16 @@
|
||||
{
|
||||
"name": "frontexpress",
|
||||
"version": "1.0.1",
|
||||
"version": "1.2.0",
|
||||
"description": "Frontexpress manages routes in browser like ExpressJS on Node",
|
||||
"main": "dist/frontexpress.js",
|
||||
"jsnext:main": "lib/frontexpress.js",
|
||||
"scripts": {
|
||||
"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",
|
||||
"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"
|
||||
"prepublish": "rimraf dist && babel lib -d dist && npm run frontpackage"
|
||||
},
|
||||
"author": "Camel Aissani <camel.aissani@gmail.com> (https://nuageprive.fr)",
|
||||
"license": "MIT",
|
||||
@ -33,23 +34,28 @@
|
||||
"babel-core": "^6.21.0",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-istanbul": "^0.12.1",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-preset-babili": "0.0.9",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-es2015-rollup": "^3.0.0",
|
||||
"babel-register": "^6.18.0",
|
||||
"bytesize": "^0.2.0",
|
||||
"chai": "^3.5.0",
|
||||
"eslint": "^3.12.2",
|
||||
"eslint-loader": "^1.6.1",
|
||||
"expose-loader": "^0.7.1",
|
||||
"chai": "^4.*",
|
||||
"eslint": "^3.*",
|
||||
"istanbul": "^0.4.5",
|
||||
"mocha": "^3.2.0",
|
||||
"rimraf": "^2.5.4",
|
||||
"rollup": "^0.38.3",
|
||||
"rollup": "^0.*",
|
||||
"rollup-plugin-babel": "^2.7.1",
|
||||
"rollup-plugin-uglify": "^1.0.1",
|
||||
"sinon": "^1.17.6",
|
||||
"uglify-js": "github:mishoo/UglifyJS2#harmony"
|
||||
}
|
||||
"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"
|
||||
]
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import uglify from 'rollup-plugin-uglify';
|
||||
import { minify } from 'uglify-js';
|
||||
import uglify from 'rollup-plugin-uglify-es';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
|
||||
export default {
|
||||
@ -13,13 +12,6 @@ export default {
|
||||
babelrc: false,
|
||||
presets: ['es2015-rollup']
|
||||
}),
|
||||
uglify({
|
||||
compress: {
|
||||
warnings: false,
|
||||
},
|
||||
output: {
|
||||
comments: false
|
||||
}
|
||||
}, minify)
|
||||
uglify()
|
||||
]
|
||||
};
|
||||
|
@ -989,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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
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());
|
||||
});
|
||||
});
|
@ -4,6 +4,9 @@ import sinon from 'sinon';
|
||||
import frontexpress from '../lib/frontexpress';
|
||||
import HTTP_METHODS from '../lib/methods';
|
||||
|
||||
const application = frontexpress();
|
||||
const routeMatcher = application.get('route matcher');
|
||||
|
||||
describe('Router', () => {
|
||||
|
||||
describe('generated methods', () => {
|
||||
@ -12,6 +15,7 @@ describe('Router', () => {
|
||||
assert(typeof router.all === 'function');
|
||||
assert(typeof router.get === 'function');
|
||||
assert(typeof router.put === 'function');
|
||||
assert(typeof router.patch === 'function');
|
||||
assert(typeof router.post === 'function');
|
||||
assert(typeof router.delete === 'function');
|
||||
});
|
||||
@ -46,7 +50,7 @@ describe('Router', () => {
|
||||
|
||||
router.get(middleware);
|
||||
|
||||
const r1 = router.routes('/', 'GET');
|
||||
const r1 = router.routes(application, {uri: '/', method: 'GET'});
|
||||
assert(r1.length === 1);
|
||||
assert(r1[0].uri === undefined);
|
||||
assert(r1[0].method === 'GET');
|
||||
@ -64,37 +68,37 @@ describe('Router', () => {
|
||||
.post('/route2', middleware2)
|
||||
.all('/route3', middleware3);
|
||||
|
||||
const r1 = router.routes('/route1', 'GET');
|
||||
const r1 = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||
assert(r1.length === 1);
|
||||
assert(r1[0].uri === '/route1');
|
||||
assert(r1[0].method === 'GET');
|
||||
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[0].uri === '/route2');
|
||||
assert(r2[0].method === 'POST');
|
||||
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[0].uri === '/route3');
|
||||
assert(r3[0].method === 'GET');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.routes('/route3', 'POST');
|
||||
r3 = router.routes(application, {uri: '/route3', method: 'POST'});
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'POST');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.routes('/route3', 'PUT');
|
||||
r3 = router.routes(application, {uri: '/route3', method: 'PUT'});
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'PUT');
|
||||
assert(r3[0].middleware === middleware3);
|
||||
|
||||
r3 = router.routes('/route3', 'DELETE');
|
||||
r3 = router.routes(application, {uri: '/route3', method: 'DELETE'});
|
||||
assert(r3.length === 1);
|
||||
assert(r3[0].uri === '/route3');
|
||||
assert(r3[0].method === 'DELETE');
|
||||
@ -107,7 +111,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri instanceof RegExp);
|
||||
assert(r[0].uri.toString() === new RegExp('^\/route1').toString());
|
||||
@ -120,7 +124,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri === '/route1/subroute');
|
||||
});
|
||||
@ -130,7 +134,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri === '/route1');
|
||||
});
|
||||
@ -140,7 +144,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri === '/route1/subroute');
|
||||
});
|
||||
@ -150,7 +154,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri === '/route1/subroute');
|
||||
|
||||
@ -160,7 +164,7 @@ describe('Router', () => {
|
||||
|
||||
router.get(new frontexpress.Middleware());
|
||||
|
||||
r = router.routes('/route1', 'GET');
|
||||
r = router.routes(application, {uri: '/route1', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/route1');
|
||||
});
|
||||
@ -170,7 +174,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri === '/route1/subroute');
|
||||
assert(r[0].data === undefined);
|
||||
@ -181,7 +185,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri === '/route1/subroute');
|
||||
assert(r[0].data === undefined);
|
||||
@ -192,7 +196,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri === '/route1/subroute');
|
||||
assert(r[0].data === undefined);
|
||||
@ -264,7 +268,7 @@ describe('Router', () => {
|
||||
|
||||
router.get(middleware);
|
||||
|
||||
const r = router.routes('/', 'GET');
|
||||
const r = router.routes(application, {uri: '/', method: 'GET'});
|
||||
assert(r.length === 1);
|
||||
assert(r[0].uri === '/');
|
||||
assert(r[0].method === 'GET');
|
||||
@ -277,7 +281,7 @@ describe('Router', () => {
|
||||
|
||||
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[0].uri === '/route1');
|
||||
assert(r[0].method === 'GET');
|
||||
@ -295,7 +299,7 @@ describe('Router', () => {
|
||||
const middleware = new frontexpress.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[0].uri instanceof RegExp);
|
||||
assert(r[0].uri.toString() === new RegExp('^\/part').toString());
|
||||
@ -303,4 +307,113 @@ describe('Router', () => {
|
||||
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,11 +1,43 @@
|
||||
/*eslint-env mocha*/
|
||||
import {assert} from 'chai';
|
||||
import chai, {assert} from 'chai';
|
||||
import Settings from '../lib/settings';
|
||||
|
||||
describe('Settings', () => {
|
||||
const settings = new Settings();
|
||||
|
||||
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', () => {
|
||||
const uriFn = settings.get('http GET transformer').uri;
|
||||
const dataFn = settings.get('http GET transformer').data;
|
||||
|
Loading…
x
Reference in New Issue
Block a user