- refactor logic of processing artifacts into their own classes
- introduce Container as the main class for bootstrapping and build a
registry of handlers during boot to organize them by a hierarchy
denoted by path
- adopt middleware like registration and invocation
- container.use(path, handler)
- container.run(context, done)
- allow more phases during boot
- boot is now asynchronous
Let environment variables override configuration set by config.json
and/or app.set()
Behavior changes
- datasources.json now support dynamic configuration through
env-vars and config.json
- component-config.json will first consider env-var
for resolving dynamic conf, then fallback to config.json
- middleware.json will first consider env-var for resolving
dynamic conf, then fallback to config.json
- for all the dynamic confg, unresolved conf will return as `undefined`
Example:
Consider the following server/datasources.json
```
{
"mysql" : {
"name" : "mysql_db",
"host" : "${MYSQL_DB_HOST}",
...
}
}
```
Now you can provide the parameter through
an environment variable:
```
$ MYSQL_DB_HOST=127.0.0.1 node .
```
or you can set the value in server/config.json
```
{
"MYSQL_DB_HOST": "127.0.0.1"
}
```
Fix executor to always emit the "booted" event and call the callback
in the next tick of the event loop, regardless of whether there are any
async boot scripts.
Before this change, adding a listener for "booted" event was cumbersome:
boot(app);
if (app.booting)
app.on('booted', handler);
else
handler();
With the fix in place, one can simply write the following:
boot(app);
app.on('booted', handler);
App variables can now be specified in component-config.json using
the ${var} format. The value of var is looked up using app.get().
This allows loopback explorer and other loopback components which
require app variables, to be loaded declaratively using
component-config.json.
App variables can now be specified in middleware.json using the ${var}
format. The value of var is looked up using app.get().
This allows loopback.rest and other loopback middleware which require
app variables, to be loaded declaratively using middleware.json.
Port can't be number checked to support iisnode. Using a parseInt()
or number isNumber function won't work if we want to support iisnode
which uses named pipes for ports (ex. \\.\pipe\mypipe)
Use the global `isFinite` function to find the first valid port
number. This is the same behaviour as `_.isFinite` in lodash@2.x.
As a result, only values that are a valid number are accepted, e.g.
"0" is accepted but "0xy" is not.
- Ensure `app.booting` is initially `undefined`
- Ensure `app.booting` is set to true during boot execution
- Ensure `app.booting` is set to false upon boot completion
This prevents an occurence where an error is completely swallowed if a script
required by loopback-boot has a bad require() call. The script is never ran
but execution continues.
Load configuration of components from `component-config`
and configure all components as specified.
Sample JSON:
{
"loopback-component-foobar": {
"option1": "value1",
"option2": "value2"
}
}
The component is expected to export the following function:
module.exports = function(app, options) { /* ... */ };
When the middleware name (path) is in the format {module}#{filename},
loopback-boot resolves the path by trying multiple locations and
using the first one that exists:
- {module} and check the {filename} property of the exports
-> e.g. loopback.rest
- {module}/server/middleware/{filename}
-> e.g. loopback/server/middleware/rest
- {module}/middleware/{filename}
-> e.g. loopback/middleware/rest
Values in any other format will bypass this resolution algorithm and
they will be used in the original form:
- a full path in a module: loopback/server/middleware/rest
- a relative path: ./middleware/custom, ./custom, ../logger
- an absolute path: /usr/local/lib/node_modules/compression
Sample JSON:
{
"routes:before": {
"morgan": {
"params": ["dev"]
}
},
"routes": {
"loopback/server/middleware/rest": {
}
},
"subapps": {
"./adminer": {
},
}
}
The JSON file can be customized using the usual conventions:
- middleware.local.{js|json}
- middleware.{env}.{js|json}
It is also possible to mount the same middleware in the same phase
multiple times with different configuration.
Example config:
{
"auth": {
"oauth2": [
{
"params": "first"
},
{
"params": "second"
}
]
},
});
LoopBack built-in models are special: they follow the loopback-boot
structure and provide `common/models/{name}.json` files, but they are
also automatically loaded (created) by loopback.
This change modifies `executor` to recognize built-in models and do not
redefine them.
Simplify the contract for functions exported by `models/*.js` files
by removing the second argument `Base`. The base class can be accessed
using `ModelCtor.base`.
An updated example of a model js file:
```js
module.exports = function(Customer) {
Customer.setup = function() {
Customer.base.setup.apply(this, arguments);
// etc.
};
};
```
The new loopback project layout adds a concept of components like
'rest server' and 'isomorphic client', each component having its own set
of boot files. The name `app.json` is confusing, since it is configuring
a component, not the app (which is the whole project).
In the first phase, all models are defined.
In the second phase, models are configured, attached to data-sources
and exposed on the app object.
This way when the `attached` Model event is emitted, all models are
already defined and thus a listener can get reference of any other
model used in the app.
Rework the way how models are configured, the goal is to allow
loopback-boot to automatically determine the correct order
of the model definitions to ensure base models are defined
before they are extended.
1. The model .js file no longer creates the model, it exports
a config function instead:
```js
module.exports = function(Car, Base) {
// Car is the model constructor
// Base is the parent model (e.g. loopback.PersistedModel)
Car.prototype.honk = function(duration, cb) {
// make some noise for `duration` seconds
cb();
};
};
```
2. The model is created by loopback-boot from model .json file.
The .js file must have the same base file name.
3. The `boot()` function has a new parameter `modelSources` to
specify the list of directories where to look for model definitions.
The parameter defaults to `['./models']`.
As a side effect, only models configured in `models.json` and their
base clases are defined. This should keep the size of the browserified
bundle small, because unused models are not included.
Breaking change.
The bootstrapper no longer calls `loopback.autoAttach`. Applications
have to explicitly configure datasources for their models
via `models.json`.
Breaking change.
In the new 2.x project layout, definition of loopback Models is out of
scope of the boot process. The bootstrapper only configures existing
models - attaches them to a dataSource and the app object.
Split bootLoopBackApp into two steps:
- compile
- execute
Most of the changes are just shuffling the existing code around.
What has changed:
- `loopback.autoAttach()` is called after `models/*` are required.
The calls were made in the opposite order before this commit.