Compare commits

...

389 Commits

Author SHA1 Message Date
Diana Lau c8241b2b35
Merge pull request #308 from achrinzafork/chore/update-lts
chore: update LTS status to End-of-Life
2021-02-09 10:48:13 -05:00
Rifa Achrinza afe57b5b04 chore: update LTS status to End-of-Life
see https://github.com/strongloop/loopback-next/issues/6957
2021-01-22 11:13:00 +08:00
Miroslav Bajtoš 56dcfaab6c
Merge pull request #305 from strongloop/feat/maintenance-lts
Update LTS status in README
2020-03-05 17:28:05 +01:00
Miroslav Bajtoš 51ec554632
Update LTS status in README 2020-03-05 13:41:27 +01:00
Agnes Lin f4313949c3
Merge pull request #302 from strongloop/copyright
chore: update copyrights year
2020-02-10 08:32:31 -05:00
Diana Lau 98883a877f chore: update copyrights year 2020-02-08 17:01:01 -05:00
Diana Lau 7a6021dc77
Merge pull request #301 from strongloop/fixbot
fix stale bot
2020-02-06 08:58:27 -05:00
Diana Lau f9d6d147bc fix stale bot 2020-02-03 14:18:55 -05:00
Nora 298aec304a
Merge pull request #300 from strongloop/chore/improve-issue-templates
chore: improve issue and PR templates
2019-11-21 10:31:47 -05:00
Nora 0043b9e27d
Merge pull request #299 from strongloop/fix-eslint
chore: fix eslint violations
2019-11-21 10:31:38 -05:00
Nora 3cbe0028a4 chore: improve issue and PR templates 2019-11-17 14:22:06 -05:00
Nora e9b108f9c6 chore: fix eslint violations 2019-11-17 14:19:25 -05:00
Miroslav Bajtoš 0bbe8335a4
Merge pull request #297 from strongloop/fix/ci
test: fix browserify-based tests
2019-10-07 10:38:13 +02:00
Miroslav Bajtoš c11ba18f73
test: fix browserify-based tests
Add `packageFilter` to handle buggy `async` browserify config.

Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
2019-10-03 17:06:12 +02:00
Miroslav Bajtoš 1fec4ea375
Merge pull request #298 from strongloop/drop-node6
Drop support for Node.js 6.x
2019-10-03 17:03:36 +02:00
Miroslav Bajtoš 974c63ccd5
Drop support for Node.js 6.x
It has reached end-of-life long time.

Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
2019-10-03 16:57:02 +02:00
Diana Lau f6b2f24b29
Merge pull request #296 from strongloop/stalebot
chore: add stalebot
2019-09-19 10:00:00 -04:00
Diana Lau b4d1f96494 chore: add stalebot 2019-09-19 09:36:49 -04:00
Diana Lau 6d8d9c92ce
Merge pull request #293 from strongloop/fix/publish-config
Tag 3.x versions as `latest` in npm registry
2019-06-25 09:54:04 -04:00
Miroslav Bajtoš 14050c199f
Tag 3.x versions as `latest` in npm registry
Signed-off-by: Miroslav Bajtoš <mbajtoss@gmail.com>
2019-06-25 08:38:07 +02:00
Diana Lau 59e46b3d91 3.3.1
* chore: update LTS status (Diana Lau)
 * chore: update copyrights years (Agnes Lin)
2019-06-24 11:22:44 -04:00
Diana Lau 8bb4368ca8
Merge pull request #292 from strongloop/lts
chore: update LTS status
2019-06-18 15:33:11 -04:00
Diana Lau a1d2954626 chore: update LTS status 2019-06-12 21:54:11 -04:00
Agnes Lin 837b8185f2
Merge pull request #290 from strongloop/copyrights
chore: update copyrights years
2019-05-07 10:29:23 -04:00
Agnes Lin ef39abf6eb chore: update copyrights years 2019-05-07 10:16:03 -04:00
Raymond Feng 8587dd6a19 3.3.0
* chore: upgrade deps to avoid npm audit warnings (Raymond Feng)
2019-03-28 14:28:27 -07:00
Raymond Feng 561cbd5bca
Merge pull request #289 from strongloop/upgrade-deps
chore: upgrade deps to avoid npm audit warnings
2019-03-28 14:27:11 -07:00
Raymond Feng 02da15cd3e chore: upgrade deps to avoid npm audit warnings 2019-03-26 11:14:56 -07:00
Miroslav Bajtoš 17ba74ed1c
3.2.1
* fix: set `app.booting` flag immediately (Miroslav Bajtoš)
 * fix: update lodash (jannyHou)
2019-03-22 16:34:33 +01:00
Miroslav Bajtoš e73b90ed97
Merge pull request #286 from strongloop/fix/booting-flag
fix: set `app.booting` flag immediately
2019-03-22 16:34:04 +01:00
Miroslav Bajtoš c10e8225bb
fix: set `app.booting` flag immediately
Before this change, `app.booting` was set by Application plugin as part
of regular phase invocation, which executes individual plugins in
subsequent turns of the event loop. As a result, `app.booting` was
initially `undefined` despite the fact that the booting process was
already in progress.

This patch moves manipulation of `app.booting` flag directly to
`Bootstrapper#run` method, to ensure it's set early enough and always
properly cleared at the end.
2019-03-22 13:57:25 +01:00
Janny 825411e6b4
Merge pull request #285 from strongloop/update/dependency-lodash
update lodash
2019-03-01 14:08:27 -05:00
jannyHou 9b77d8159a fix: update lodash 2019-03-01 12:32:47 -05:00
Miroslav Bajtoš fd3a381289
3.2.0
* README: update LTS status (Miroslav Bajtoš)
 * Add support for es6 modules for boot scripts (Walker)
2018-10-18 09:01:15 +02:00
Miroslav Bajtoš 3e7031c82e
Merge pull request #284 from strongloop/update-lts
README: update LTS status
2018-10-18 09:00:25 +02:00
Miroslav Bajtoš c278fe6705
README: update LTS status 2018-10-16 13:15:49 +02:00
Miroslav Bajtoš 0ace54cd9e
Merge pull request #280 from klarkc/master
Add support for es6 modules in boot scripts
2018-10-11 16:23:54 +02:00
Walker bfa874de63
Add support for es6 modules for boot scripts 2018-10-11 16:17:29 +02:00
Diana Lau b7a14e30e1 3.1.1
* update: dependency (jannyHou)
 * chore: update dependencies (Diana Lau)
 * [WebFM] cs/pl/ru translation (candytangnb)
 * chore: update license (Diana Lau)
 * CODEOWNERS: move @lehni to Alumni section (Miroslav Bajtoš)
 * Add support for ES6 style async boot scripts (Jürg Lehni)
2018-07-26 17:33:46 -04:00
Janny 2675771ec1
Merge pull request #283 from strongloop/update-dev
Update dev
2018-07-26 14:54:35 -04:00
jannyHou 14eeb4487d update: dependency 2018-07-26 14:16:16 -04:00
Diana Lau e89e353ab0
Merge pull request #282 from strongloop/update-dep
chore: update dependencies
2018-07-26 11:09:04 -04:00
Diana Lau d598c3738a chore: update dependencies 2018-07-25 21:59:39 -04:00
Taranveer Virk c4ab39c4e0
Merge pull request #281 from candytangnb/webfm-0629-000536-cs,pl,ru-translation
[WebFM] cs/pl/ru translation Check-in by YI TANG (tangyinb@cn.ibm.com)
2018-07-06 14:51:07 -04:00
candytangnb ded5bccb60 [WebFM] cs/pl/ru translation
cs/pl/ru translation check-in by YI TANG (tangyinb@cn.ibm.com) using
WebFM tool.
2018-06-29 00:05:36 -04:00
Miroslav Bajtoš 473e685a69
Merge pull request #253 from lehni/feature/async-boot-scripts-master
Add support for ES6 style async boot scripts
2017-12-11 10:35:22 +01:00
Diana Lau cdba5f0748
Merge pull request #274 from strongloop/license
chore: update license
2017-11-13 14:52:16 -05:00
Diana Lau 47ba28898a chore: update license 2017-11-10 18:02:24 -05:00
Miroslav Bajtoš a035aaa3ae Merge pull request #271 from strongloop/good-bye-lehni
CODEOWNERS: move @lehni to Alumni section
2017-10-19 10:53:05 +02:00
Miroslav Bajtoš c1cda7aefc
CODEOWNERS: move @lehni to Alumni section 2017-10-19 10:52:32 +02:00
Diana Lau d7efabaebd 3.1.0
* update strong-globalize to 3.1.0 (shimks)
 * CODEOWNERS: add zbarbuto (Miroslav Bajtoš)
 * Ignore source maps in boot (Zak Barbuto)
 * CODEOWNERS: add lehni (Miroslav Bajtoš)
 * Create Issue and PR Templates (#261) (Sakib Hasan)
 * Add CODEOWNER file (Diana Lau)
2017-10-13 13:32:25 -04:00
Diana Lau a9ae78ea54 Merge pull request #268 from strongloop/update-strong-globalize
update strong-globalize to 3.1.0
2017-10-13 12:35:46 -04:00
shimks 3277738937 update strong-globalize to 3.1.0 2017-10-12 15:01:48 -04:00
Miroslav Bajtoš 123f3da523 Merge pull request #266 from strongloop/welcome-zbarbuto
CODEOWNERS: add zbarbuto
2017-09-27 11:12:14 +02:00
Miroslav Bajtoš e64adb92de CODEOWNERS: add zbarbuto 2017-09-25 09:56:08 +02:00
Miroslav Bajtoš 6e7809d3ef Merge pull request #264 from NextFaze/fix/ignore-maps-3
Ignore source maps in boot
2017-09-21 12:59:47 +02:00
Zak Barbuto a78a05f9b4 Ignore source maps in boot 2017-09-21 16:35:14 +09:30
Miroslav Bajtoš 42a7152474 Merge pull request #262 from strongloop/welcome-lehni
CODEOWNERS: add lehni
2017-08-31 17:02:24 +02:00
Miroslav Bajtoš 038bfeae43
CODEOWNERS: add lehni 2017-08-31 11:26:07 +02:00
Jürg Lehni 6e36f02005 Add support for ES6 style async boot scripts 2017-08-18 12:43:21 +02:00
Sakib Hasan a054fb0064 Create Issue and PR Templates (#261)
* create issue template

* create pr template
2017-08-16 15:02:49 -04:00
Diana Lau 10ec2c5ed6 Merge pull request #254 from strongloop/add-codeowner
Add CODEOWNERS file
2017-07-26 20:42:40 -04:00
Diana Lau 3f42e66dea Add CODEOWNER file 2017-07-26 19:40:35 -04:00
Diana Lau 2bf14c8cb4 3.0.1
* Update Italian translated strings Q2 2017 (Allen Boone)
 * Update translated strings Q2 2017 (Allen Boone)
 * Replicate new issue_template from loopback (Siddhi Pai)
 * Replicate issue_template from loopback repo (Siddhi Pai)
2017-06-22 09:47:10 -04:00
Miroslav Bajtoš 6e6ecb3ecc Merge pull request #238 from strongloop/replicate-issue-template
Replicate issue_template from loopback repo
2017-06-07 13:58:03 +02:00
Candy 0d216ac360 Merge pull request #249 from kallenboone/master
Update Italian translated strings Q2 2017
2017-06-02 16:36:07 -04:00
Allen Boone 96d59ce610 Update Italian translated strings Q2 2017 2017-06-02 16:21:20 -04:00
Candy 756e3aedda Merge pull request #248 from kallenboone/master
Update translated strings Q2 2017
2017-05-23 14:52:24 -04:00
Allen Boone 3ec9d3ab1c Update translated strings Q2 2017 2017-05-23 14:27:47 -04:00
Raymond Feng 92d6a1f91c 3.0.0
* Upgrade deps and fix style issues (Raymond Feng)
 * Provide scriptExtensions option (Supasate Choochaisri)
 * Update paid support URL (Siddhi Pai)
 * Refactor for  modular and pluggable design (Raymond Feng)
 * Add Node v7 to Travis CI platforms (Miroslav Bajtoš)
 * Drop support for Node v0.10 and v0.12 (Miroslav Bajtoš)
 * readme: update URL to new doc site (David Cheung)
 * Update ja translation file (Candy)
 * Update header-browser.md (Sequoia McDowell)
 * Update translation files - round#2 (Candy)
 * Normalize line endings to support both LF and CRLF (Miroslav Bajtoš)
 * Remove "defaultForType" from datasource config (Miroslav Bajtoš)
 * Update deps to loopback 3.0.0 RC (Miroslav Bajtoš)
 * globalization: add translated strings (gunjpan)
 * Start development of 3.0 (Miroslav Bajtoš)
2017-05-22 08:44:21 -07:00
Raymond Feng 63a11502a6 Merge pull request #247 from strongloop/feature/upgrade-deps
Upgrade deps and fix style issues
2017-05-22 10:41:07 -05:00
Raymond Feng 3bb519d5e9 Upgrade deps and fix style issues 2017-05-08 14:48:48 -07:00
Raymond Feng 480380224d Merge pull request #240 from supasate/provide-script-extensions-option
Provide scriptExtensions option for 3.x
2017-04-03 10:02:51 -07:00
Supasate Choochaisri d68ffc6f6f Provide scriptExtensions option 2017-04-01 02:14:47 +07:00
Siddhi Pai aa321cf072 Replicate new issue_template from loopback 2017-02-15 15:27:59 -08:00
Siddhi Pai f39fe30e47 Replicate issue_template from loopback repo 2017-02-13 10:25:44 -08:00
Simon Ho 79d9ddb835 Merge pull request #234 from strongloop/update-support-URL
Replicate .github from loopback repo
2016-12-07 00:30:46 -08:00
Siddhi Pai ba688e0026 Update paid support URL 2016-12-06 03:06:40 -08:00
David Cheung f7c9cbc2c6 Merge pull request #181 from strongloop/feature/extensibility
[SEMVER-MAJOR] Refactor for modular and pluggable design
2016-11-22 14:19:29 -05:00
Raymond Feng ac1571ccf1 Refactor for modular and pluggable design
- 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
2016-11-22 13:38:28 -05:00
Miroslav Bajtoš 314dff9f5f Merge pull request #231 from strongloop/drop-support-node-0x
Drop support for Node v0.10 and v0.12
2016-11-15 15:42:39 +01:00
Miroslav Bajtoš ecc2d43957 Add Node v7 to Travis CI platforms 2016-11-15 15:07:33 +01:00
Miroslav Bajtoš fbea19a002 Drop support for Node v0.10 and v0.12 2016-11-15 15:07:22 +01:00
David Cheung e96b08087d Merge pull request #227 from strongloop/update-new-docs-url
readme: update URL to new doc site
2016-11-08 13:58:01 -05:00
David Cheung a17c6c50e3 readme: update URL to new doc site 2016-10-28 16:28:11 -04:00
Miroslav Bajtoš 6491cc8e71 Merge pull request #220 from Sequoia/patch-1
Update header-browser.md
2016-10-11 14:25:15 +02:00
David Cheung 94aef17122 Merge pull request #221 from strongloop/add_translation3
Update ja translation file
2016-10-06 15:27:34 -04:00
Candy 295db6d873 Update ja translation file 2016-10-06 11:25:39 -04:00
Sequoia McDowell 63cc0ecf7b Update header-browser.md
closes #53
2016-10-05 17:25:52 -04:00
Amirali Jafarian 0d985bae0b Merge pull request #219 from strongloop/add_translation2
Update translation files - round#2
2016-09-28 17:13:36 -04:00
Candy 5da1420027 Update translation files - round#2 2016-09-28 13:56:16 -04:00
Miroslav Bajtoš 57e5e64b4d Merge pull request #214 from strongloop/update-lb-3-rc-1
Update deps to loopback 3.0.0 RC
2016-09-23 12:41:58 +02:00
Miroslav Bajtoš 49ed10caaf Normalize line endings to support both LF and CRLF
This should fix build failures on Windows caused by line-ending
mismatch.
2016-09-23 11:16:26 +02:00
Miroslav Bajtoš 748a728a4f Remove "defaultForType" from datasource config
The option "defaultForType" is no longer supported by LoopBack
and causes an unhandled error.
2016-09-23 10:57:53 +02:00
Miroslav Bajtoš 58ef16993b Update deps to loopback 3.0.0 RC 2016-09-22 13:11:01 +02:00
David Cheung a6076ae8be Merge pull request #213 from strongloop/add-translatedFiles
Add translated files.
2016-09-21 15:19:30 -04:00
gunjpan 422fa6c11d globalization: add translated strings 2016-09-21 11:38:52 -04:00
David Cheung 28d58ede8e Merge pull request #211 from strongloop/start/3.0
Start development of 3.0
2016-09-16 12:02:59 -04:00
Miroslav Bajtoš 889a9fe275 Start development of 3.0 2016-09-16 10:17:29 +02:00
Miroslav Bajtoš d2c55e79c5 2.22.0
* Replace fs.existsSync calls with fs.statSync (Joshua Estrin Skrzypek)
 * Change test cases port to be dynamic (David Cheung)
 * Globalization for Loopback-boot (David Cheung)
2016-09-05 14:55:34 +02:00
Miroslav Bajtoš 53112957d9 Merge pull request #204 from jskrzypek/replace-exists-sync
Replace fs.existsSync calls with wrapped fs.statSync

Close #204
2016-09-05 14:45:20 +02:00
Joshua Estrin Skrzypek 09c3f8365b Replace fs.existsSync calls with fs.statSync 2016-09-05 14:29:51 +02:00
David Cheung e68502a4f5 Merge pull request #205 from strongloop/indepdent-port
Change test cases port to be dynamic
2016-08-16 16:41:52 -04:00
David Cheung dcc575f90b Change test cases port to be dynamic
To avoid port collision when running in Jenkins
2016-08-16 14:37:28 -04:00
David Cheung 13a049412a Merge pull request #200 from strongloop/globalize-2.x
Globalize Loopback-boot
2016-08-05 16:24:57 -04:00
David Cheung 1121721afe Globalization for Loopback-boot 2016-08-05 14:31:48 -04:00
Miroslav Bajtoš 8fb9f751cd 2.21.0
* Configurable dir for components and middleware (Doped Dude)
 * test: fix security warning (Miroslav Bajtoš)
2016-07-27 16:14:22 +02:00
Miroslav Bajtoš 1382e8af2f Merge pull request #171 from dopeddude/component-and-middleware-rootpath-configuration
Configurable directory for components and middleware

Close #171
2016-07-27 16:12:41 +02:00
Doped Dude 129938bacd Configurable dir for components and middleware
Add two new options `middlewareRootDir` and `componentRootDir`
allowing users to load middleware and/or components from a custom
place
2016-07-27 16:09:46 +02:00
Miroslav Bajtoš eb49c7a386 Merge pull request #195 from strongloop/fix/crypto-warning-in-tests
test: fix security warning
2016-07-19 15:40:06 +02:00
Miroslav Bajtoš 9b23e71501 2.20.0
* Update URLs in CONTRIBUTING.md (#198) (Ryan Graham)
 * travis: drop io.js, add Node v4 and v6 (Miroslav Bajtoš)
 * Stop caching config files (Miroslav Bajtoš)
2016-07-14 13:54:35 +02:00
Ryan Graham ce7fe3fb07 Update URLs in CONTRIBUTING.md (#198) 2016-07-13 17:42:46 -07:00
Miroslav Bajtoš c2484a6265 Merge pull request #194 from strongloop/fix/require-cache
Stop caching config files
2016-07-13 16:37:49 +02:00
Miroslav Bajtoš 296c38568c Merge pull request #196 from strongloop/fix/travis
travis: drop io.js, add Node v4 and v6
2016-07-01 16:22:10 +02:00
Miroslav Bajtoš 24571328a8 travis: drop io.js, add Node v4 and v6 2016-07-01 16:03:42 +02:00
Miroslav Bajtoš 5f5e86e47f test: fix security warning
Add a simple implementation of getRandomValues to the browser context
to get rid of the following warning:

    [SECURITY] node-uuid: crypto not usable, falling back to insecure
    Math.random()
2016-07-01 16:01:58 +02:00
Miroslav Bajtoš 7b226b212e Stop caching config files 2016-07-01 15:38:25 +02:00
Miroslav Bajtoš 58d9322190 2.19.0
* update copyright notices and license (Ryan Graham)
 * Add flag var lazyConnect to ds config (juehou)
2016-06-20 17:11:43 +02:00
Ryan Graham 5eaa909006
update copyright notices and license 2016-05-05 21:52:36 -07:00
Janny defe5f4bb8 Merge pull request #185 from strongloop/feature/lazy-connect
Add flag var lazyConnect to ds config
2016-05-05 18:18:22 -04:00
juehou d334425ada Add flag var lazyConnect to ds config 2016-05-05 17:33:16 -04:00
Miroslav Bajtoš 51a699af01 2.18.1
* parse config: should ignore null values (Loïc Mahieu)
2016-04-13 16:16:14 +02:00
Miroslav Bajtoš 47974fd1d2 Merge pull request #183 from LoicMahieu/fix/182
parse config: should ignore null values
2016-04-13 16:15:52 +02:00
Loïc Mahieu 1d2649eee7 parse config: should ignore null values
Fix #182
2016-04-13 11:26:24 +02:00
Miroslav Bajtoš cc551b0c0f 2.18.0
* Dynamic datasources.json from ENV and config.json (David Cheung)
 * Use eslint with loopback config (Miroslav Bajtoš)
2016-04-07 10:28:22 +02:00
Miroslav Bajtoš 707214cac4 Merge pull request #177 from strongloop/dynamic-config
Dynamic datasources.json from ENV and config.json
2016-04-07 10:26:41 +02:00
David Cheung 4a815deb27 Dynamic datasources.json from ENV and config.json
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"
}
```
2016-04-06 10:30:23 -04:00
Miroslav Bajtoš b3e5f23865 Merge pull request #178 from strongloop/feature/eslint
Use eslint with loopback config
2016-04-05 15:55:58 +02:00
Miroslav Bajtoš 902005ed8e Use eslint with loopback config
Drop jshint and jscs in favour of eslint.
2016-04-05 15:42:58 +02:00
Miroslav Bajtoš 058e0e2f56 2.17.0
* executor: move "booted" and cb() to the next tick (Miroslav Bajtoš)
 * Fix lodash 4.0.0 breaking changes (Jérémie Drouet)
 * When config is overriden with null don't merge (Farid Neshat)
2016-02-23 12:08:30 +01:00
Miroslav Bajtoš 90b9211fff Merge pull request #174 from strongloop/fix/booted-event-asynchrony
executor: move "booted" and cb() to the next tick
2016-02-23 12:07:42 +01:00
Miroslav Bajtoš b0e5a0bc63 executor: move "booted" and cb() to the next tick
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);
2016-02-22 11:01:06 +01:00
Miroslav Bajtoš e9afcaac70 Merge pull request #168 from alFReD-NSH/null-config
When config is overriden with null don't merge
2016-02-01 13:42:06 +01:00
Simon Ho 2e3b37a4f8 Merge pull request #169 from jdrouet/lodash-breaking-change
Fix lodash 4.0.0 breaking changes
2016-01-18 13:28:28 -08:00
Jérémie Drouet c475e4af84 Fix lodash 4.0.0 breaking changes 2016-01-18 13:29:46 +01:00
Farid Neshat d850560a18 When config is overriden with null don't merge
Fixes #146 and also fixes strongloop/loopback-component-explorer#51
2016-01-08 22:54:17 +08:00
Miroslav Bajtoš a5b888a719 2.16.0
* executor: allow loopback versions >= 3.0.0-alpha (Miroslav Bajtoš)
2015-12-22 10:03:54 +01:00
Miroslav Bajtoš ab6a8ae776 Merge pull request #165 from strongloop/support-loopback-3.0
executor: allow loopback versions >= 3.0.0-alpha
2015-12-22 10:03:39 +01:00
Miroslav Bajtoš 3836078985 executor: allow loopback versions >= 3.0.0-alpha 2015-12-22 09:55:37 +01:00
Miroslav Bajtoš adeece3b30 2.15.0
* Bluemix prefers HOST/PORT over VCAP_APP_XXXX (Sai Vennam)
 * Set app env if it is supplied in options object (Amir Jafarian)
2015-12-04 17:06:10 +01:00
Miroslav Bajtoš d8b298b53d Merge pull request #158 from svennam92/patch-1
Bluemix favors PORT over VCAP_APP_PORT
2015-12-04 17:05:31 +01:00
Sai Vennam de6ebf65ee Bluemix prefers HOST/PORT over VCAP_APP_XXXX 2015-12-03 13:29:58 -05:00
Miroslav Bajtoš 4e3e1a7394 Merge pull request #164 from strongloop/set-app-env
Set app env if it is supplied in options object
2015-12-02 16:38:54 +01:00
Amir Jafarian 2574c9dbb6 Set app env if it is supplied in options object
Set the app env if it is supplied in options object which solves issue
number 28 under loopback-boot repo
2015-11-30 10:30:09 -05:00
Miroslav Bajtoš 088ca864c2 2.14.2
* executor: preserve RegExps in middleware paths (Miroslav Bajtoš)
2015-11-24 18:53:48 +01:00
Miroslav Bajtoš 5f0175afbf Merge pull request #159 from strongloop/fix/interpolate-regexp
executor: preserve RegExps in middleware paths
2015-11-24 18:53:17 +01:00
Miroslav Bajtoš a023e1f142 2.14.1
* Warn user if missing a config file (Amir Jafarian)
 * Refer to licenses with a link (Sam Roberts)
2015-11-24 14:15:56 +01:00
Miroslav Bajtoš c17efda0eb Merge pull request #162 from Amir-61/missingConfigWarning
Warn about missing main json config file
2015-11-24 14:07:53 +01:00
Amir Jafarian 3f55acf9d5 Warn user if missing a config file
Report a warning when the main json file is missing and there is a
local or {env} file present
2015-11-23 13:34:19 -05:00
Miroslav Bajtoš 1d27cf0e05 executor: preserve RegExps in middleware paths
Fix interpolateVariables() to skip objects with a non-default
constructor, for example RegExp or Date.
2015-11-04 15:47:55 +01:00
Sam Roberts 24fbfbebf1 Refer to licenses with a link 2015-11-03 12:27:49 -08:00
Miroslav Bajtoš 638368844d 2.14.0
* Support bluemix env variables for host and port (Miroslav Bajtoš)
2015-10-14 11:20:30 +02:00
Miroslav Bajtoš 5238bc1027 Merge pull request #156 from strongloop/feature/bluemix-env
Support bluemix environment variables for host and port
2015-10-14 11:18:45 +02:00
Miroslav Bajtoš c8fdbd5110 Support bluemix env variables for host and port
Detect Bluemix/CouldFoundry environment and automatically apply
VCAP_APP_PORT and VCAP_APP_HOST settings.
2015-10-09 13:21:38 -07:00
Miroslav Bajtoš 63589c03ab 2.13.0
* add env folder for boot (yorkie)
 * Use strongloop conventions for licensing (Sam Roberts)
2015-10-01 19:19:10 +02:00
Miroslav Bajtoš fbafa2b608 Merge pull request #154 from weflex/add/env-boot
add env postifix to boot
2015-10-01 18:11:24 +02:00
yorkie d249b15ce0 add env folder for boot 2015-09-29 12:31:46 +08:00
Sam Roberts d1b00ce5d8 Use strongloop conventions for licensing 2015-09-21 16:42:54 -07:00
Miroslav Bajtoš 6ef37ef651 2.12.2
* test: fix strict mode failure (Ryan Graham)
2015-09-09 13:39:21 +02:00
Miroslav Bajtoš 819e1ea9c8 Merge pull request #155 from strongloop/fix-build
test: fix strict mode failure
2015-09-09 13:27:42 +02:00
Ryan Graham a5bc1056aa test: fix strict mode failure
Since the value is undefined at the this point, just remove it so that
linters and strict mode don't cause the entire test suite to abort.
2015-09-08 17:30:30 -07:00
Simon Ho 55c87796cf 2.12.1
* Add config variable checks (Simon Ho)
2015-08-31 23:22:50 -07:00
Simon Ho 7cbb8f6b40 Merge pull request #152 from strongloop/bug/add-config-var-checks
Add config variable checks
2015-08-31 20:15:57 -07:00
Simon Ho 43042905af Add config variable checks
- Check for config variables defined as functions
- Check for config variables that are undefined
2015-08-31 17:53:27 -07:00
Miroslav Bajtoš f0aa0f8fdb 2.12.0
* Resolve ${var} values in component-config.json (Hage Yaapa)
 * Resolve ${var} values in middleware.json (Hack Sparrow)
 * Upgrade Travis to container-based infrastructure (Miroslav Bajtoš)
2015-08-28 19:06:22 +02:00
Miroslav Bajtoš 26a50137c7 Merge pull request #151 from hacksparrow/feature/declarative-loopback-explorer
Resolve ${var} values in component-config.json
2015-08-28 09:48:41 +02:00
Hage Yaapa 1b491dee85 Resolve ${var} values in component-config.json
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.
2015-08-28 11:49:46 +05:30
Miroslav Bajtoš 0b0f0ed202 Merge pull request #149 from hacksparrow/feature/declarative-loopback-rest
Resolve ${var} values in middleware.json
2015-08-25 15:32:42 +02:00
Hack Sparrow 8b475a97b2 Resolve ${var} values in middleware.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.
2015-08-25 18:42:47 +05:30
Miroslav Bajtoš 75c945effa Merge pull request #150 from strongloop/fix/travis-config
Upgrade Travis to container-based infrastructure
2015-08-24 15:43:31 +02:00
Miroslav Bajtoš f6dfbd2625 Upgrade Travis to container-based infrastructure
See http://docs.travis-ci.com/user/migrating-from-legacy/ for details.
2015-08-24 15:35:56 +02:00
Raymond Feng 674a8da693 2.11.0
* Allow middleware array merge by a key in item objects (Raymond Feng)
2015-08-11 08:33:58 -07:00
Raymond Feng 8864e2b394 Merge pull request #148 from strongloop/feature/more-middleware-merge
Allow middleware array merge by a key in item objects
2015-08-11 08:16:44 -07:00
Raymond Feng b081d828b2 Allow middleware array merge by a key in item objects 2015-08-10 17:17:09 -07:00
Raymond Feng be3997de15 2.10.0
* Enhance middleware config merge (Raymond Feng)
2015-08-10 08:02:30 -07:00
Raymond Feng 32276c79d4 Merge pull request #147 from strongloop/feature/enhance-middleware-merge
Enhance middleware config merge
2015-08-10 07:55:31 -07:00
Raymond Feng 7362cc489e Enhance middleware config merge 2015-08-07 15:53:28 -07:00
Raymond Feng 952c4101ef 2.9.0
* Fix the build failure (Raymond Feng)

 * Fix the model-config/datasource merge (Raymond Feng)

 * Resolved style issue (Dennis Ashby)

 * Added code to allow model-config to respect model-config.local.js and model-config.env.js as do other config files. (Dennis Ashby)

 * Add jsdoc for `options.mixinSources` (Miroslav Bajtoš)

 * allow middleware to be optional (Hage Yaapa)
2015-08-03 07:47:10 -07:00
Raymond Feng e052da83db Merge pull request #143 from strongloop/dashby3000-master
Fix & Merge https://github.com/strongloop/loopback-boot/pull/141
2015-08-03 07:45:48 -07:00
Raymond Feng 450aa6dd21 Merge branch 'master' of github.com:strongloop/loopback-boot into dashby3000-master 2015-07-31 13:45:23 -07:00
Raymond Feng 329fc59113 Fix the build failure 2015-07-31 10:02:16 -07:00
Raymond Feng 02c826337c Fix the model-config/datasource merge 2015-07-31 10:01:53 -07:00
Dennis Ashby ab723b5490 Resolved style issue 2015-07-31 06:29:03 -07:00
Dennis Ashby 41af081c55 Added code to allow model-config to respect model-config.local.js and model-config.env.js as do other config files. 2015-07-25 09:56:28 -07:00
Miroslav Bajtoš 4f6e768426 Merge pull request #139 from strongloop/fix/jsdoc-mixinSources
Add jsdoc for `options.mixinSources`
2015-07-03 09:42:19 +02:00
Miroslav Bajtoš 6980a250ef Add jsdoc for `options.mixinSources` 2015-06-30 18:07:22 +02:00
Miroslav Bajtoš ac5265f726 Merge pull request #134 from hacksparrow/master
allow middleware to be optional

Close #134
2015-06-25 19:33:03 +02:00
Hage Yaapa 66cc099d1d allow middleware to be optional 2015-06-25 19:32:40 +02:00
Miroslav Bajtoš 0aac03ebb5 2.8.2
* Excl. mod. main path from middleware instructions (Miroslav Bajtoš)
2015-06-24 19:00:42 +02:00
Miroslav Bajtoš 5c8334c7c7 Merge pull request #136 from strongloop/fix/omit-middleware-main-module-file
Excl. mod. main path from middleware instructions
2015-06-24 19:00:15 +02:00
Miroslav Bajtoš a63ae8e44b Excl. mod. main path from middleware instructions
When loading middleware configured in "middleware.json", loopback-boot
used to require() the full path to middleware's module main file,
for example "node_modules/strong-express-metrics/index.js".

This is breaking strong-agent probes, as it's difficult to detect
when the intercepted module (e.g. "strong-express-metrics") was loaded.

This commit modifies the compiler to omit the tail (the path to the
module main source file) when requiring middleware.
2015-06-24 17:21:22 +02:00
Ritchie Martori 6c33a60bbc 2.8.1
* Add more debug info for config loading (Ritchie Martori)

 * use a new variable for better debug output (Bryan Clark)
2015-06-10 09:56:00 -07:00
Ritchie Martori a7d6cf695c Merge pull request #133 from strongloop/feature/config-loader-debug
Add more debug info for config loading
2015-06-10 09:52:07 -07:00
Ritchie Martori 50d99c7e6b Add more debug info for config loading 2015-06-03 11:12:10 -07:00
Raymond Feng bd4c2fc968 Merge pull request #132 from clarkbw/better-debug
use a new variable for better debug output
2015-05-29 10:14:50 -07:00
Bryan Clark 2e63631b31 use a new variable for better debug output
Because tryResolveAppPath returns undefined when it can’t resolve a
path the debug output for directories that cannot be resolved is fairly
useless.

You get output like this:
  loopback👢compiler Skipping unknown module source dir undefined
+0ms

When you want output like this:
loopback👢compiler Skipping unknown module source dir "./models"
+0ms
2015-05-29 09:03:08 -07:00
Miroslav Bajtoš a06aaba1cb 2.8.0
* Support iisnode using named pipes as PORT value (Jonathan Sheely)

 * support 'mixinsources' option (Pradnya Baviskar)

 * compiler: Simplify verifyModelDefinitions() (Miroslav Bajtoš)

 * Fix coding style issues, add API docs (Miroslav Bajtoš)

 * Extend options arg to support custom model definitions (Shlomi Assaf)

 * add support for mixins  - [mixinDirs]: List of directories to look for files  containing model mixin definition. (Pradnya Baviskar)
2015-05-29 10:37:02 +02:00
Miroslav Bajtoš 083a1265a4 Merge pull request #130 from jsheely/master
Support iisnode using named pipes as PORT value

Close #130
2015-05-29 10:35:18 +02:00
Jonathan Sheely 44f733f59f Support iisnode using named pipes as PORT value
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)
2015-05-29 10:34:05 +02:00
Miroslav Bajtoš 140180f667 Merge pull request #131 from PradnyaBaviskar/lb-issue-79-mixinsources
support 'mixinsources' option
2015-05-07 16:04:14 +02:00
Pradnya Baviskar 53f51820f5 support 'mixinsources' option
- By default looks for mixinsources in  directory

- Loads only mixins used through models
2015-05-07 14:35:55 +05:30
Miroslav Bajtoš 2815a42d81 Merge pull request #100 from shlomiassaf:simpleModelDef
Extend options arg to support custom model definitions

Close #100
Close #49
2015-05-05 11:29:30 +02:00
Miroslav Bajtoš 43df90a4c8 compiler: Simplify verifyModelDefinitions()
Refactor `verifyModelDefinitions()` to use the new function
`fixFileExtension` instead of removed `filterValidSourceFiles()`.
2015-05-05 11:28:58 +02:00
Miroslav Bajtoš 0563840006 Fix coding style issues, add API docs 2015-05-05 11:28:58 +02:00
Shlomi Assaf b6c60a297d Extend options arg to support custom model definitions
Extend the options argument of boot/compile to allow the developer
to supply custom model definitions (to replace the result of
findModelDefinitions). Together with the existing options.models,
it allows to specify all model-related data and still use the benefits
which loopback-boot provides (most notably loading models in order
defined by inheritance, i.e. base models are loaded before models that
are extending them).
2015-05-05 11:24:02 +02:00
Miroslav Bajtoš 0e19f0a1b2 Merge pull request #120 from PradnyaBaviskar/lb-boot-issue-79
Add support for mixinDirs
2015-04-24 09:29:39 +02:00
Pradnya Baviskar 9bb988713e add support for mixins
- [mixinDirs]: List of directories to look for files
 containing model mixin definition.
2015-04-24 12:42:56 +05:30
Miroslav Bajtoš aed73a9e51 2.7.1
* executor: fix port lookup (Miroslav Bajtoš)

 * Clean up compiler.tryResolveAppPath (Miroslav Bajtoš)

 * Configure Travis CI builds (Miroslav Bajtoš)
2015-04-23 09:32:10 +02:00
Miroslav Bajtoš c3bbc13fd8 Merge pull request #126 from strongloop/fix/number-isFinite
executor: fix port lookup
2015-04-23 09:26:29 +02:00
Miroslav Bajtoš e5fd8c7975 executor: fix port lookup
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.
2015-04-23 08:51:24 +02:00
Miroslav Bajtoš a2000829bc Merge pull request #127 from strongloop/refactor/tryResolveAppPath
Clean up compiler.tryResolveAppPath
2015-04-22 11:40:12 +02:00
Miroslav Bajtoš acb3d1815f Clean up compiler.tryResolveAppPath
Enable the strict mode by default.
2015-04-22 09:52:06 +02:00
Miroslav Bajtoš e3763ac28e Merge pull request #121 from strongloop/enable-travis
Configure Travis CI builds
2015-04-17 09:14:11 +02:00
Miroslav Bajtoš f9d74f2c5e Configure Travis CI builds 2015-04-17 09:12:45 +02:00
Miroslav Bajtoš 6c0c8b9f8b 2.7.0
* Upgrade lodash and drop underscore.string (Bryan Clark)

 * add console.error message to a bad require in a boot script (Bryan Clark)

 * Support per-application registry of models (Miroslav Bajtoš)

 * Use filename as default value for Model name (Pradnya Baviskar)

 * compiler: code cleanup (Miroslav Bajtoš)

 * Improve the resolution of relative paths  - resolve module relative path for component  - prioritize coffeescript over json (Pradnya Baviskar)

 * Resolve module paths as relative to appRootDir - for middleware (Pradnya Baviskar)

 * Support for multiple apps in browserified bundle. (Krishna Raman)

 * Resolve missing file extension for module relative paths (Pradnya Baviskar)

 * Resolve module paths as relative to appRootDir (Pradnya Baviskar)

 * Resolve relative paths in  using appRootDir (Pradnya Baviskar)

 * Add feature to disable component (Pradnya Baviskar)

 * Fix test for different line endings on Windows (Pradnya Baviskar)

 * Refactor unit test assertions to be more specific (Simon Ho)

 * Add unit test to verify `app.booting flag status (Simon Ho)
2015-04-15 17:43:43 +02:00
Miroslav Bajtoš d9c0f12a66 Merge pull request #119 from clarkbw/lodash
upgrade lodash and drop underscore.string

Close #119
2015-04-15 17:40:32 +02:00
Bryan Clark 044a4df07a Upgrade lodash and drop underscore.string
Update to the latest version of "lodash" to use the string functions
included and drop the usage of "underscore.string".

Existing tests run checks for `flying-car` definitions to be named
`FlyingCar` so there doesn’t seem to be a need to add more checks.

`isFinite` changed from the older version of lodash to no longer coerce
strings into numbers, this commit adds a call of `parseInt` before
checking whether the value is a finite number.

`parseInt` on an undefined returns NaN which isFinite does not identify
as a number.

While changing this part, the code was reworked to use `Number.isFinite`
and thus the executor no longer depends on lodash. This should reduce
the size of the browser bundle.
2015-04-15 17:40:14 +02:00
Miroslav Bajtoš 80831cdaaf Merge pull request #115 from clarkbw/bad-require
add console.error message to a bad require in a boot script
2015-04-14 19:02:06 +02:00
Bryan Clark 1d8bd15a77 add console.error message to a bad require in a boot script
This change produces the following error message in the tests:
`Failed loading boot script badScript.js`
`Cannot find module 'doesnt-exist’`
2015-04-14 09:09:18 -07:00
Miroslav Bajtoš 2e9f001100 Merge pull request #112 from strongloop/feature/app-registries
Support per-application registry of models
2015-04-14 08:22:02 +02:00
Miroslav Bajtoš acf1868ba4 Support per-application registry of models 2015-04-09 17:56:56 +02:00
Miroslav Bajtoš 1b9f137062 Merge pull request #118 from PradnyaBaviskar/lb-boot-issue-42
Use filename as default value for Model name
2015-04-08 10:50:03 +02:00
Pradnya Baviskar 39d820a657 Use filename as default value for Model name 2015-04-08 14:04:57 +05:30
Miroslav Bajtoš ade8605618 Merge pull request #117 from strongloop/fix/compiler-code-cleanup
compiler: code cleanup
2015-04-07 12:38:24 +02:00
Miroslav Bajtoš 6526ffeb3d compiler: code cleanup
Refactor and simplify the internal method tryResolveAppPath.

Fix a bug in the resolver of middleware paths which was discovered
by the refactoring.
2015-04-07 10:42:49 +02:00
Miroslav Bajtoš 626200a098 Merge pull request #114 from PradnyaBaviskar/lb-boot-issue-73-5
Resolve module paths as relative to appRootDir - for component
2015-04-07 10:13:09 +02:00
Pradnya Baviskar 07d276977a Improve the resolution of relative paths
- resolve module relative path for component
 - prioritize coffeescript over json
2015-04-07 13:38:57 +05:30
Miroslav Bajtoš d7c67c803a Merge pull request #113 from PradnyaBaviskar/lb-boot-issue-73-4
Resolve module paths as relative to appRootDir - for middleware
2015-03-20 16:32:24 +01:00
Pradnya Baviskar 3a7b9739d9 Resolve module paths as relative to appRootDir - for middleware 2015-03-20 18:32:45 +05:30
Krishna Raman 416738b679 Merge pull request #106 from strongloop/feature/multi-app-browserify-support
Support for multiple apps in browserified bundle.
2015-03-19 10:13:19 -07:00
Krishna Raman 311b892a0f Support for multiple apps in browserified bundle. 2015-03-19 09:57:32 -07:00
Miroslav Bajtoš 61798455f8 Merge pull request #109 from PradnyaBaviskar/lb-boot-issue-73-2
Resolve missing file extension for module relative paths
2015-03-18 07:32:33 +01:00
Pradnya Baviskar 187a105333 Resolve missing file extension for module relative paths 2015-03-17 11:42:04 +05:30
Miroslav Bajtoš c9f377a4c9 Merge pull request #107 from PradnyaBaviskar/lb-boot-issue-73
Resolve module paths as relative to appRootDir
2015-03-16 10:21:51 +01:00
Pradnya Baviskar 4913b3ffb9 Resolve module paths as relative to appRootDir 2015-03-16 14:43:37 +05:30
Miroslav Bajtoš cb7ca7d9ad Merge pull request #104 from PradnyaBaviskar/lb-explorer-issue-65
Resolve relative paths in bootScripts using appRootDir
2015-03-11 08:43:39 +01:00
Pradnya Baviskar 6e6eaccadd Resolve relative paths in using appRootDir 2015-03-11 11:07:28 +05:30
Miroslav Bajtoš df81d5487c Merge pull request #103 from PradnyaBaviskar/lb-explorer-issue-51
Add feature to disable component
2015-03-10 11:18:46 +01:00
Pradnya Baviskar 2c4b6f06a4 Add feature to disable component 2015-03-10 15:03:20 +05:30
Miroslav Bajtoš bfcd3ff15e Merge pull request #102 from PradnyaBaviskar/fix-test-for-windows
Fix test for different line endings on Windows
2015-03-05 08:04:36 +01:00
Pradnya Baviskar 872889423a Fix test for different line endings on Windows 2015-03-05 09:53:28 +05:30
Miroslav Bajtoš 464c389af9 Merge pull request #99 from strongloop/fix/booting-flag-unit-test
Refactor unit test assertions to be more specific
2015-02-24 15:01:37 +01:00
Simon Ho 276391811a Refactor unit test assertions to be more specific
- Change `process.bootingFlagSet` assertion from `ok()` to `true()`
- Change `app.booting` assertion from `not.be.ok()` to `false()`
2015-02-23 13:10:12 -08:00
Miroslav Bajtoš d041d80933 Merge pull request #97 from strongloop/feature/unit-test-booting-flag
Add unit test to check `app.booting` flag
2015-02-23 18:01:52 +01:00
Simon Ho e89a60c45c Add unit test to verify `app.booting flag status
- 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
2015-02-20 13:51:53 -08:00
Miroslav Bajtoš ce58b7d65e 2.6.5
* Save instructions.json in root dir Saving in node_modules dir causes complaints and missing files fixes https://github.com/strongloop/loopback-boot/issues/94 (Berkeley Martinez)
2015-02-20 12:40:00 +01:00
Miroslav Bajtoš 9b407b1ada Merge pull request #95 from r3dm/fix/save-instruct-in-root
Save instructions.json in root dir
2015-02-20 12:39:31 +01:00
Berkeley Martinez d8bf8687a8 Save instructions.json in root dir
Saving in node_modules dir causes complaints and missing files
fixes https://github.com/strongloop/loopback-boot/issues/94
2015-02-19 13:38:13 -08:00
Miroslav Bajtoš af1d0dcccb Merge tag 'v2.6.4'
2.6.4

 * executor: pass correct `this` to middleware (Clark Wang)

 * Fix broken links (Rand McKinney)
2015-02-02 18:57:44 +01:00
Miroslav Bajtoš a03e881f0a Merge branch 'release/2.6.4' into production 2015-02-02 18:57:38 +01:00
Miroslav Bajtoš 15eb54ac85 v2.6.4 2015-02-02 18:57:33 +01:00
Miroslav Bajtoš 9dde8b0adf Merge pull request #92 from clarkorz/fix/passport
Fix "can't config passport#initialize in middleware.json"

Close #92
2015-02-02 18:56:18 +01:00
Clark Wang 6424534831 executor: pass correct `this` to middleware
Fix the bug "can't configure passport#initialize in middleware.json"
2015-02-02 18:54:22 +01:00
Rand McKinney 215abf9d30 Fix broken links 2015-01-29 12:17:24 -08:00
Miroslav Bajtoš 77b83ce3fd Merge tag 'v2.6.3'
2.6.3

 * Don't swallow error when a sub-dependency doesn't resolve. (Samuel Reed)

 * Fix "incompatible loopback version" check & msg (Miroslav Bajtoš)

 * Add "booting" flag and emit "booted" event (Simon Ho)

 * Configure components via `component-config.json` (Miroslav Bajtoš)

 * Fix bad CLA URL in CONTRIBUTING.md (Ryan Graham)

 * Dedupe boot scripts (Eric Satterwhite)

 * Replace underscore with lodash (Ryan Graham)

 * compiler: resolve paths in middleware params (Miroslav Bajtoš)

 * Implement shorthand notation for middleware paths (Raymond Feng)

 * Load middleware and phases from `middleware.json` (Miroslav Bajtoš)

 * Add jscs style check, fix violations found (Miroslav Bajtoš)

 * Clean up .jshintrc (Miroslav Bajtoš)

 * Use `chai` instead of `must` (Miroslav Bajtoš)

 * Bump version (Raymond Feng)

 * Fix the test for built-in models on Windows (Raymond Feng)

 * Fix jsdoc (Raymond Feng)

 * compiler: fix coding style violations (Miroslav Bajtoš)

 * support coffee-script models and client code (bitmage)

 * compiler: support module-relative model sources (Miroslav Bajtoš)

 * Skip definitions of built-in loopback models (Miroslav Bajtoš)

 * package: update dependency versions (Miroslav Bajtoš)

 * Use loopback 2.x in unit tests. (Miroslav Bajtoš)

 * Add support for async boot scripts (Raymond Feng)

 * Clean up jsdoc comments. (Miroslav Bajtoš)

 * Custom rootDir for app config (johnsoftek)

 * compiler: improve merging of Arrays and Objects (Miroslav Bajtoš)

 * config-loader: deeply merge Array and Object vals (Shelby Sanders)

 * gitignore: add Idea's *.iml files (Miroslav Bajtoš)

 * package: Add `jshint` to `devDependencies` (Miroslav Bajtoš)

 * Update contribution guidelines (Ryan Graham)

 * test: ensure sandbox dir is present (Miroslav Bajtoš)

 * test: add `global.navigator` for browser tests (Miroslav Bajtoš)

 * test: increase timeout for browserify (Miroslav Bajtoš)

 * index: fix jshint error (Miroslav Bajtoš)

 * documentation fix (Alex)

 * Fix typo (Fabien Franzen)

 * Implemented modelSources, bootDirs and bootScripts options (Fabien Franzen)

 * executor: remove `Base` arg from model function (Miroslav Bajtoš)

 * v2.0.0-beta3 (Miroslav Bajtoš)

 * compiler: return a clone of instructions (Miroslav Bajtoš)

 * test: export Int32Array and DataView for browser (Miroslav Bajtoš)

 * v2.0.0-beta2 (Miroslav Bajtoš)

 * Rename `models.json` to `model-config.json` (Miroslav Bajtoš)

 * Remove non-API docs. (Rand McKinney)

 * 2.0.0-beta1 (Miroslav Bajtoš)

 * test: fix jshint warnings (Miroslav Bajtoš)

 * compiler: fix references to loopback (Miroslav Bajtoš)

 * Rename `app.json` to `config.json` (Miroslav Bajtoš)

 * compiler: Sort models topologically (Miroslav Bajtoš)

 * executor: Split model boot into two phases (Miroslav Bajtoš)

 * compiler: Move model-sources cfg to models.json (Miroslav Bajtoš)

 * package: Bump up the version to 2.0.0-dev (Miroslav Bajtoš)

 * Rework model configuration (Miroslav Bajtoš)

 * Remove auto-attach. (Miroslav Bajtoš)

 * Change models.json to configure existing models (Miroslav Bajtoš)
2015-01-13 13:35:36 +01:00
Miroslav Bajtoš 4f9f112d80 Merge branch 'release/2.6.3' into production 2015-01-13 13:35:34 +01:00
Miroslav Bajtoš 548dba0c37 v2.6.3 2015-01-13 13:35:32 +01:00
Miroslav Bajtoš a935669d9c Merge tag 'v2.6.2'
2.6.2

 * Don't swallow error when a sub-dependency doesn't resolve. (Samuel Reed)
2015-01-13 10:34:46 +01:00
Miroslav Bajtoš bb98472965 Merge branch 'release/2.6.2' into production 2015-01-13 10:34:44 +01:00
Miroslav Bajtoš d2c70bad3c v2.6.2 2015-01-13 10:34:42 +01:00
Miroslav Bajtoš 2a1f07aad9 Merge pull request #88 from STRML/master
Don't swallow error when a sub-dependency doesn't resolve.
2015-01-13 10:34:29 +01:00
Samuel Reed 30a7b6d9b8 Don't swallow error when a sub-dependency doesn't resolve.
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.
2015-01-13 10:23:54 +01:00
Miroslav Bajtoš fbf1e95e29 Merge tag 'v2.6.1'
2.6.1

 * Fix "incompatible loopback version" check & msg (Miroslav Bajtoš)
2015-01-12 18:01:01 +01:00
Miroslav Bajtoš ab33132ef7 Merge branch 'release/2.6.1' into production 2015-01-12 18:00:59 +01:00
Miroslav Bajtoš 8bc526377f v2.6.1 2015-01-12 18:00:57 +01:00
Miroslav Bajtoš 146a3183eb Merge pull request #89 from strongloop/fix/version-error-message
Fix "incompatible loopback version" check & msg
2015-01-12 17:55:11 +01:00
Miroslav Bajtoš 4f8514a454 Fix "incompatible loopback version" check & msg
Use `util.format` to build the error message, `Error` constructors
does not support placeholders like `%s`.

Detect pre-release versions and handle them in the same way as regular
releases.
2015-01-12 16:00:06 +01:00
Miroslav Bajtoš 30ff50c581 Merge tag 'v2.6.0'
2.6.0

 * Add "booting" flag and emit "booted" event (Simon Ho)

 * Configure components via `component-config.json` (Miroslav Bajtoš)

 * Fix bad CLA URL in CONTRIBUTING.md (Ryan Graham)
2015-01-08 08:31:30 +01:00
Miroslav Bajtoš 33b3729b62 Merge branch 'release/2.6.0' into production 2015-01-08 08:31:28 +01:00
Miroslav Bajtoš 5fd79f0339 v2.6.0 2015-01-08 08:31:26 +01:00
Miroslav Bajtoš c47bde9281 Merge pull request #77 from strongloop/feature/add-boot-completion-check
Add "booting" flag and emit "booted" event
2015-01-07 16:41:29 +01:00
Miroslav Bajtoš 8aa7156d3b Merge pull request #86 from strongloop/feature/load-component
Configure components via `component-config.json`
2015-01-07 08:28:29 +01:00
Simon Ho 1f7d8e56e8 Add "booting" flag and emit "booted" event 2015-01-06 21:27:32 -08:00
Miroslav Bajtoš f8247be23c Configure components via `component-config.json`
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) { /* ... */ };
2015-01-06 14:00:26 +01:00
Ryan Graham 1ef2616979 Fix bad CLA URL in CONTRIBUTING.md 2014-12-29 08:27:45 -08:00
Miroslav Bajtoš c4473951a0 Merge tag 'v2.5.2'
2.5.2

 * Dedupe boot scripts (Eric Satterwhite)
2014-12-19 15:21:44 +01:00
Miroslav Bajtoš 6a442f8307 Merge branch 'release/2.5.2' into production 2014-12-19 15:21:42 +01:00
Miroslav Bajtoš 4031f6b985 v2.5.2 2014-12-19 15:21:40 +01:00
Miroslav Bajtoš 8a3e529ea3 Merge pull request #72 from esatterwhite/lb-64-multiple-invocation-same-boot-script
Ensure to dedupe boot scripts array before running.

Closes #72
Fixes #64
2014-12-19 15:18:58 +01:00
Eric Satterwhite 301031b78f Dedupe boot scripts
Remove duplicated entries in the array of boot scripts to run.
Because the bootDirs defaults to process.cwd(), if the user manually
includes scripts in the same directory, without specifying a bootDir,
those scripts will be included multiple times.
2014-12-19 15:18:37 +01:00
Miroslav Bajtoš beb4f8c34c Merge tag 'v2.5.1'
2.5.1

 * Replace underscore with lodash (Ryan Graham)
2014-12-08 08:09:36 +01:00
Miroslav Bajtoš 4f65067348 Merge branch 'release/2.5.1' into production 2014-12-08 08:09:35 +01:00
Miroslav Bajtoš a90964ab30 v2.5.1 2014-12-08 08:09:33 +01:00
Miroslav Bajtoš 4b9e13bfc6 Merge pull request #76 from strongloop/feature/remove-underscore
Replace underscore with lodash
2014-12-03 19:15:00 +01:00
Ryan Graham e1f7c592a1 Replace underscore with lodash
Replace 2 uses of underscore and 1 use of a lodash submodule with full
lodash module.
2014-12-02 22:10:52 -08:00
Miroslav Bajtoš 8da44e69dd Merge tag 'v2.5.0'
2.5.0

 * compiler: resolve paths in middleware params (Miroslav Bajtoš)
2014-12-02 18:14:04 +01:00
Miroslav Bajtoš 77ded6edfe Merge branch 'release/2.5.0' into production 2014-12-02 18:14:00 +01:00
Miroslav Bajtoš e81ff119a3 v2.5.0 2014-12-02 18:13:58 +01:00
Miroslav Bajtoš 3dfa3bcb13 Merge pull request #74 from strongloop/feature/relative-paths-in-middleware-params
compiler: resolve paths in middleware params
2014-12-01 20:18:21 +01:00
Miroslav Bajtoš 8cc2518cb0 compiler: resolve paths in middleware params
Introduce a convention for specifying relative paths in middleware
params: values prefixed with `$!` and starting with `./` or `../`
are resolved relatively to `middleware.json`.

Example:

    {
      "files": {
        "loopback#static": {
          "params": "$!../client"
        },
        "loopback#static": {
          "params": "$!./public"
        }
      }
    }
2014-11-28 12:17:27 +01:00
Miroslav Bajtoš 81303b3093 Merge tag 'v2.4.0'
2.4.0

 * Implement shorthand notation for middleware paths (Raymond Feng)

 * Load middleware and phases from `middleware.json` (Miroslav Bajtoš)

 * Add jscs style check, fix violations found (Miroslav Bajtoš)

 * Clean up .jshintrc (Miroslav Bajtoš)

 * Use `chai` instead of `must` (Miroslav Bajtoš)
2014-11-27 19:23:49 +01:00
Miroslav Bajtoš d3145f5745 Merge branch 'release/2.4.0' into production 2014-11-27 19:23:47 +01:00
Miroslav Bajtoš 097fced310 v2.4.0 2014-11-27 19:23:45 +01:00
Miroslav Bajtoš 6de571f442 Merge pull request #70 from strongloop/feature/short-middleware-paths
#68 - Implement shorthand notation for middleware paths
2014-11-25 11:51:00 +01:00
Raymond Feng 2f72006c88 Implement shorthand notation for middleware paths
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
2014-11-25 11:43:00 +01:00
Miroslav Bajtoš d25c64523d Merge pull request #66 from strongloop/feature/middleware-mounting
Load middleware and phases from `middleware.json`
2014-11-19 09:58:38 +01:00
Miroslav Bajtoš 1114bc9227 Load middleware and phases from `middleware.json`
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"
          }
        ]
      },
    });
2014-11-19 09:45:04 +01:00
Miroslav Bajtoš 08fcc5faa7 Merge pull request #67 from strongloop/feature/infrastructure-cleanup
Infrastructure cleanup
2014-11-13 16:09:10 +01:00
Miroslav Bajtoš 5b5071864b Add jscs style check, fix violations found 2014-11-13 15:54:59 +01:00
Miroslav Bajtoš 83723379a2 Clean up .jshintrc
jshint does not support `trailing` as of v1.5.0.

Remove `maxlen` too, jscs provides better implementation.

Fix definition of global mocha variables - mark them as immutable.
2014-11-13 15:33:23 +01:00
Miroslav Bajtoš e2aff71bf9 Use `chai` instead of `must`
As of 1.10, chai supports nonary assertions, there are no more reasons
for using a different variant.
2014-11-13 15:31:35 +01:00
Raymond Feng 6040f66adc Merge tag 'v2.3.1'
2.3.1
2014-11-10 08:58:42 -08:00
Raymond Feng 9cd5d3d125 Merge branch 'release/2.3.1' into production 2014-11-10 08:58:32 -08:00
Raymond Feng f4c7b1ba38 Bump version 2014-11-10 08:57:46 -08:00
Raymond Feng 27bd48e0f6 Merge pull request #63 from strongloop/feature/fix-lb-issue-756
Fix the test for built-in models on Windows
2014-11-10 08:56:21 -08:00
Raymond Feng db917bf03b Fix the test for built-in models on Windows
See https://github.com/strongloop/loopback/issues/756
2014-11-10 08:22:57 -08:00
Raymond Feng ed59cb2483 Fix jsdoc 2014-10-30 13:34:59 -07:00
Miroslav Bajtoš 176f96e242 Merge tag 'v2.3.0'
2.3.0
2014-10-27 11:14:50 +01:00
Miroslav Bajtoš f5ac5273a7 Merge branch 'release/2.3.0' into production 2014-10-27 11:14:40 +01:00
Miroslav Bajtoš 84f8a51138 2.3.0 2014-10-27 11:14:19 +01:00
Miroslav Bajtoš d7bdbd31b1 compiler: fix coding style violations 2014-10-27 11:13:02 +01:00
Miroslav Bajtoš b480efde8a Merge pull request #59 from TorchlightSoftware/coffee-fix2
support coffee-script models and client code
2014-10-24 20:04:21 +02:00
bitmage e936deffe2 support coffee-script models and client code
Load models for any filetypes registered in require.extensions.

 - Server side coffee-script requires a `require('coffee-script/register');`

 - Client side coffee-script requires Coffeeify.
2014-10-24 10:42:30 -07:00
Miroslav Bajtoš 3961f1c615 Merge tag 'v2.2.0'
2.2.0
2014-10-22 08:58:40 +02:00
Miroslav Bajtoš 56c74174f1 Merge branch 'release/2.2.0' into production 2014-10-22 08:58:05 +02:00
Miroslav Bajtoš 5cea78c6aa 2.2.0 2014-10-22 08:57:44 +02:00
Miroslav Bajtoš b5c585291b Merge pull request #60 from strongloop/feature/skip-builtin-loopback-models
Skip definitions of built-in loopback models
2014-10-21 18:17:49 +02:00
Miroslav Bajtoš aa4cbdd80f compiler: support module-relative model sources
Interpret model sources in the same way how `require.resolve`
interprets the path:

 - values starting with `./` and `../` are relative to the file
   where they are specified

 - other values are relative to node modules folders

This way it's possible to specify a source `loopback/common/models`
and have it resolved to whatever place the loopback is installed.
2014-10-21 18:10:22 +02:00
Miroslav Bajtoš 26abb43ad4 Skip definitions of built-in loopback models
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.
2014-10-21 18:10:22 +02:00
Miroslav Bajtoš 011296d825 Merge pull request #58 from strongloop/feature/use-loopback-2x-for-tests
Use loopback 2x for tests, update dependency versions
2014-10-21 09:45:50 +02:00
Miroslav Bajtoš c04946073f package: update dependency versions 2014-10-21 09:40:52 +02:00
Miroslav Bajtoš 927bee82f7 Use loopback 2.x in unit tests. 2014-10-21 09:38:25 +02:00
Raymond Feng fadfaffe25 Merge tag 'v2.1.0'
2.1.0
2014-10-09 12:23:19 -07:00
Raymond Feng 676f347f36 Merge branch 'release/2.1.0' into production 2014-10-09 12:23:15 -07:00
Raymond Feng 68593c8100 Bump version 2014-10-09 12:22:34 -07:00
Raymond Feng e0abff007e Merge pull request #50 from strongloop/feature/support-async-scripts
Add support async scripts
2014-10-09 12:19:21 -07:00
Raymond Feng 94cb4d6342 Add support for async boot scripts 2014-10-09 12:18:36 -07:00
Miroslav Bajtoš 38c4944e2e Merge pull request #54 from strongloop/feature/support-nested-values-in-config-overrides
Support nested values in config overrides
2014-10-09 19:33:13 +02:00
Miroslav Bajtoš 0d4b5bb7c4 Merge pull request #43 from johnsoftek/master
Custom rootDir for app config

Close #43
2014-10-09 16:36:31 +02:00
Miroslav Bajtoš d54e2b54d0 Clean up jsdoc comments.
- Fix formatting to improve the way how the text is rendered on
   http://apidocs.strongloop.com/loopback-boot/

 - Add `@property` entry for `options.appConfigRootDir`.
2014-10-09 16:36:19 +02:00
johnsoftek 18121a4208 Custom rootDir for app config 2014-10-09 16:36:19 +02:00
Miroslav Bajtoš f0836719c9 compiler: improve merging of Arrays and Objects
Add more unit-tests to cover various edge cases. Fix issues discovered
by these new tests.
2014-10-08 17:15:32 +02:00
Shelby Sanders e1d870dced config-loader: deeply merge Array and Object vals 2014-10-08 17:15:32 +02:00
Miroslav Bajtoš abda37fee9 gitignore: add Idea's *.iml files 2014-10-08 17:15:30 +02:00
Miroslav Bajtoš ac73288cef Merge pull request #51 from strongloop/feature/fix-jshint-problems
package: Add `jshint` to `devDependencies`
2014-10-08 15:06:19 +02:00
Miroslav Bajtoš ed0880d00f package: Add `jshint` to `devDependencies`
Remove dependency on a globally installed jshint instance
and fix `npm test` on machines without a global jshint.
2014-10-08 12:09:16 +02:00
Ryan Graham e974033395 Update contribution guidelines
Replace commit signing process with https://cla.strongloop.com/
2014-10-01 18:06:20 -07:00
Miroslav Bajtoš 6c26e99ab9 Merge pull request #36 from strongloop/feature/fix-CI-build
test: fix failures on CI
2014-08-19 19:37:39 +02:00
Miroslav Bajtoš 0169f22cd0 test: ensure sandbox dir is present
Fix test/browser.test.js failing on CI due to sandbox dir not present
on the first run.
2014-08-19 09:26:57 +02:00
Miroslav Bajtoš 34de593202 test: add `global.navigator` for browser tests
The debug module uses `navigator.userAgent` to detect whether the
browser support colors in console logs.
2014-08-18 14:56:20 +02:00
Miroslav Bajtoš 93bfc3a63a test: increase timeout for browserify 2014-08-18 14:35:19 +02:00
Miroslav Bajtoš edb1f1fdce index: fix jshint error
Wrap a too long line in a jsdoc comment.
2014-08-18 14:26:11 +02:00
Raymond Feng 20ed867d79 Merge pull request #34 from cajoy/master
documentation fix
2014-08-07 08:23:37 -07:00
Alex 1ae0167b7c documentation fix 2014-08-07 10:12:35 -04:00
Raymond Feng 51cf0052d0 Merge pull request #31 from fabien/fix/compile-options
Implemented modelSources, bootDirs and bootScripts options
2014-08-04 08:42:45 -07:00
Fabien Franzen 217f0ce32f Fix typo 2014-08-04 10:35:13 +02:00
Fabien Franzen f98d2cb89c Implemented modelSources, bootDirs and bootScripts options 2014-08-04 10:33:03 +02:00
Miroslav Bajtoš bc233e83d3 Merge tag 'v2.0.0'
2.0.0
2014-07-22 19:34:28 +02:00
Miroslav Bajtoš 4d55ab82aa Merge branch 'release/2.0.0' into production 2014-07-22 19:34:24 +02:00
Miroslav Bajtoš 5471129f79 2.0.0 2014-07-22 19:34:01 +02:00
Miroslav Bajtoš e2092bba17 Merge pull request #26 from strongloop/feature/simplify-model-fn-signature
executor: remove `Base` arg from model function
2014-07-22 19:28:05 +02:00
Miroslav Bajtoš c4b09c6b7a executor: remove `Base` arg from model function
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.
  };
};
```
2014-07-22 10:59:50 +02:00
Miroslav Bajtoš b5989e907e package: update dependency versions 2014-07-22 10:57:44 +02:00
Miroslav Bajtoš e42ee20d00 Merge tag 'vv2.0.0-beta3'
v2.0.0-beta3
2014-07-17 19:01:19 +02:00
Miroslav Bajtoš be74e7c1ec Merge branch 'release/v2.0.0-beta3' into production 2014-07-17 19:01:16 +02:00
Miroslav Bajtoš eac6cdf645 v2.0.0-beta3 2014-07-17 19:00:55 +02:00
Miroslav Bajtoš 6bf995c55b compiler: return a clone of instructions
When executor passes the instruction to loopback methods,
loopback modifies the data. Since we are loading the data using
`require` such changes affects also code that calls
`require` for one of the instructions files.

This change adds a deep clone step to prevent this issue.
2014-07-17 18:44:15 +02:00
Raymond Feng cb67fc4165 Merge tag 'v2.0.0-beta2'
2.0.0-beta2
2014-07-17 09:05:54 -07:00
Raymond Feng 85c40c635a Merge branch 'release/2.0.0-beta2' into production 2014-07-17 09:05:46 -07:00
Raymond Feng 9493aca34e Merge pull request #23 from strongloop/feature/fix-unit-tests
test: export Int32Array and DataView for browser
2014-07-17 09:03:13 -07:00
Miroslav Bajtoš e7250b6eac test: export Int32Array and DataView for browser
crypto-browserify uses Int32Array, which is not exposed on the VM
context in Node v0.10.
2014-07-17 10:05:08 +02:00
Miroslav Bajtoš 2bcf1f60aa Merge tag 'v2.0.0-beta2'
v2.0.0-beta2
2014-07-16 08:21:54 +02:00
Miroslav Bajtoš 431bdc9b18 Merge branch 'release/2.0.0-beta2' into production 2014-07-16 08:21:46 +02:00
Miroslav Bajtoš 53ca2f697a v2.0.0-beta2 2014-07-16 08:21:16 +02:00
Miroslav Bajtoš cfe5f40808 Merge pull request #19 from strongloop/feature/rename-models-to-models-config
Rename `models.json` to `model-config.json`
2014-07-15 20:46:47 +02:00
Miroslav Bajtoš 3129f6495c Rename `models.json` to `model-config.json`
The name `models.json` was potentially confusing since there are no
models defined in that file.
2014-07-15 11:09:39 +02:00
Miroslav Bajtoš 3522f117c0 Merge pull request #18 from strongloop/doc-changes
Doc changes
2014-07-09 07:39:02 +02:00
Rand McKinney 25704cf69a Remove non-API docs.
- Remove material that's in Confluence.
 - Change function name to boot()
 - Create header-node.md and header-browser.md
2014-07-09 07:36:45 +02:00
Miroslav Bajtoš 5e142c5188 Merge tag 'v2.0.0-beta1'
2.0.0-beta1
2014-06-26 14:56:34 +02:00
Miroslav Bajtoš fc18561cc7 Merge branch 'release/2.0.0-beta1' into production 2014-06-26 14:56:29 +02:00
Miroslav Bajtoš 5e23db50a5 2.0.0-beta1 2014-06-26 14:56:02 +02:00
Miroslav Bajtoš f222be477e Merge pull request #17 from strongloop/feature/fix-executor
Fix references to loopback; fix jshint warnings
2014-06-26 14:54:42 +02:00
Miroslav Bajtoš 92455f569c test: fix jshint warnings 2014-06-26 14:54:07 +02:00
Miroslav Bajtoš a3c347d073 compiler: fix references to loopback 2014-06-26 14:53:47 +02:00
Miroslav Bajtoš d5cd0a3b50 Merge branch 'master' into 2.0
Conflicts:
	README.md
	docs/configuration.md
	lib/executor.js
	package.json

Changes in the docs were merged manually and updated to correctly
describe the 2.x layout.
2014-06-26 14:40:24 +02:00
Miroslav Bajtoš af9c4d8cc6 Merge tag 'v1.1.0'
1.1.0
2014-06-26 11:00:26 +02:00
Miroslav Bajtoš 2cc5a88699 Merge pull request #13 from strongloop/rename-app-json-to-config-json
Rename `app.json` to `config.json`
2014-06-26 10:28:44 +02:00
Miroslav Bajtoš ac16d92a8b Rename `app.json` to `config.json`
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).
2014-06-25 08:18:04 +02:00
Miroslav Bajtoš 0a0a6f5d01 Merge branch 'master' into 2.0
Conflicts:
	index.js
	package.json
2014-06-16 19:49:45 +02:00
Miroslav Bajtoš 2eab6bf32a Merge pull request #11 from strongloop/model-boot-improvements
[2.0] Model boot improvements
2014-06-16 19:47:10 +02:00
Miroslav Bajtoš 57e96b0d38 compiler: Sort models topologically
Sort models topologically using Base->Model as edges. This way
the base models are defined before the models extending them.
2014-06-16 19:45:34 +02:00
Miroslav Bajtoš ef72efa70b executor: Split model boot into two phases
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.
2014-06-16 16:41:12 +02:00
Miroslav Bajtoš b887b33b57 compiler: Move model-sources cfg to models.json
Remove `modelSources` option from `boot()` options, add `_meta.sources`
to `models.json`.

```json
{
  "_meta": {
    "sources": ["./custom/path/to/models"]
  },
  "Car": {
    "dataSource": "db"
  }
}
```
2014-06-16 16:41:12 +02:00
Miroslav Bajtoš a9a401ad56 package: Bump up the version to 2.0.0-dev 2014-06-16 16:38:28 +02:00
Miroslav Bajtoš e22ecd39ce Merge pull request #10 from strongloop/auto-require-model-definitions
[2.0] Rework model configuration
2014-06-14 09:37:21 +02:00
Miroslav Bajtoš a204fdc1c9 Rework model configuration
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.
2014-06-13 19:40:52 +02:00
Miroslav Bajtoš fccc6e147a Merge pull request #9 from strongloop/configure-models
Configure models
2014-06-10 10:27:19 +02:00
Miroslav Bajtoš 43993bf975 Remove auto-attach.
Breaking change.

The bootstrapper no longer calls `loopback.autoAttach`. Applications
have to explicitly configure datasources for their models
via `models.json`.
2014-06-10 09:24:16 +02:00
Miroslav Bajtoš 47b5bb5f5c Change models.json to configure existing models
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.
2014-06-10 09:21:15 +02:00
120 changed files with 9233 additions and 1580 deletions

View File

@ -1,3 +1,2 @@
node_modules/
coverage/
test/sandbox/

3
.eslintrc Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "loopback"
}

50
.github/ISSUE_TEMPLATE/Bug_report.md vendored Normal file
View File

@ -0,0 +1,50 @@
---
name: Bug report
about: Create a report to help us improve
labels: bug
---
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
HELP US HELP YOU, PLEASE
- Do a quick search to avoid duplicate issues
- Provide as much information as possible (reproduction sandbox, use case for features, etc.)
- Consider using a more suitable venue for questions such as Stack Overflow, Gitter, etc.
Please fill in the *entire* template below.
-->
## Steps to reproduce
<!-- Describe how to reproduce the issue -->
## Current Behavior
<!-- Describe the observed result -->
## Expected Behavior
<!-- Describe what did you expect instead, what is the desired outcome? -->
## Link to reproduction sandbox
<!--
See https://loopback.io/doc/en/contrib/Reporting-issues.html#loopback-3x-bugs
Note: Failure to provide a sandbox application for reproduction purposes will result in the issue being closed.
-->
## Additional information
<!--
Copy+paste the output of these two commands:
node -e 'console.log(process.platform, process.arch, process.versions.node)'
npm ls --prod --depth 0 | grep loopback
-->
## Related Issues
<!-- Did you find other bugs that looked similar? -->
_See [Reporting Issues](http://loopback.io/doc/en/contrib/Reporting-issues.html) for more tips on writing good issues_

View File

@ -0,0 +1,25 @@
---
name: Feature request
about: Suggest an idea for this project
labels: feature
---
## Suggestion
<!-- A summary of what you'd like to see added or changed -->
## Use Cases
<!--
What do you want to use this for?
What shortcomings exist with current approaches?
-->
## Examples
<!-- Show how this would be used and what the behavior would be -->
## Acceptance criteria
TBD - will be filled by the team.

27
.github/ISSUE_TEMPLATE/Question.md vendored Normal file
View File

@ -0,0 +1,27 @@
---
name: Question
about: The issue tracker is not for questions. Please use Stack Overflow or other resources for help.
labels: question
---
<!-- 🚨 STOP 🚨 STOP 🚨 STOP 🚨
THE ISSUE TRACKER IS NOT FOR QUESTIONS.
DO NOT CREATE A NEW ISSUE TO ASK A QUESTION.
Please use one of the following resources for help:
**Questions**
- https://stackoverflow.com/tags/loopbackjs
- https://groups.google.com/forum/#!forum/loopbackjs
- https://gitter.im/strongloop/loopback
**Immediate support**
- https://strongloop.com/api-connect-faqs/
- https://strongloop.com/node-js/subscription-plans/
-->

11
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Report a security vulnerability
url: https://loopback.io/doc/en/contrib/Reporting-issues.html#security-issues
about: Do not report security vulnerabilities using GitHub issues. Please send an email to `reachsl@us.ibm.com` instead.
- name: Get help on StackOverflow
url: https://stackoverflow.com/tags/loopbackjs
about: Please ask and answer questions on StackOverflow.
- name: Join our mailing list
url: https://groups.google.com/forum/#!forum/loopbackjs
about: You can also post your question to our mailing list.

18
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,18 @@
<!--
Please provide a high-level description of the changes made by your pull request.
Include references to all related GitHub issues and other pull requests, for example:
Fixes #123
Implements #254
See also #23
-->
## Checklist
👉 [Read and sign the CLA (Contributor License Agreement)](https://cla.strongloop.com/agreements/strongloop/loopback-boot) 👈
- [ ] `npm test` passes on your machine
- [ ] New tests added or existing tests modified to cover all changes
- [ ] Code conforms with the [style guide](https://loopback.io/doc/en/contrib/style-guide-es6.html)
- [ ] Commit messages are following our [guidelines](https://loopback.io/doc/en/contrib/git-commit-messages.html)

24
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,24 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 14
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
- critical
- p1
- major
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been closed due to continued inactivity. Thank you for your understanding.
If you believe this to be in error, please contact one of the code owners,
listed in the `CODEOWNERS` file at the top-level of this repository.

3
.gitignore vendored
View File

@ -10,7 +10,10 @@
*.pid
*.swp
*.swo
*.iml
node_modules
generated-instructions*.json
checkstyle.xml
loopback-boot-*.tgz
/test/sandbox/
intl/zz/

View File

@ -1,23 +0,0 @@
{
"node": true,
"browser": true,
"camelcase" : true,
"eqnull" : true,
"indent": 2,
"undef": true,
"unused": true,
"quotmark": "single",
"maxlen": 80,
"trailing": true,
"newcap": true,
"nonew": true,
"sub": true,
"globals": {
"describe": true,
"it": true,
"before": true,
"beforeEach": true,
"after": true,
"afterEach": true
}
}

1
.npmrc Normal file
View File

@ -0,0 +1 @@
package-lock=false

6
.travis.yml Normal file
View File

@ -0,0 +1,6 @@
sudo: false
language: node_js
node_js:
- "8"
- "10"

30
3.0-RELEASE-NOTES.md Normal file
View File

@ -0,0 +1,30 @@
# List of notable changes made between 2.x and 3.0
All breaking changes must be described here. When adding a new entry,
always describe the impact on users and instructions for upgrading
applications from 2.x to 3.0.
## boot.compile is now async and returns context with instructions
Users that uses `boot.compile()` in the following syntax would need to update
their implementation, it now calls callback function with `instructions` in
`context` object rather than synchronously returning instructions.
Before:
```js
var APP_PATH = path.resolve('../sandbox');
var instructions = boot.compile(APP_PATH);
```
New signature:
```js
var APP_PATH = path.resolve('../sandbox');
var instructions;
boot.compile(APP_PATH, function(err, context) {
instructions = context.instructions;
});
```
Please see [PR #181](https://github.com/strongloop/loopback-boot/pull/181) for full details of change.

View File

@ -1,14 +1,545 @@
## Changes in version 1.0
2019-06-24, Version 3.3.1
=========================
- New options: `modelsRootDir`, `dsRootDir`
* chore: update LTS status (Diana Lau)
- Load configuration from files, support dynamic (scripted) options
* chore: update copyrights years (Agnes Lin)
```sh
app.json, app.local.*, app.{env}.*
datasources.json, datasources.local.*, datasources.{env}.*
```
- Scripts in `models/` and `boot/` can export `function(app)`,
this function is then called by the bootstrapper. The existing code
using `var app = require('../app')` will continue to work.
2019-03-28, Version 3.3.0
=========================
* chore: upgrade deps to avoid npm audit warnings (Raymond Feng)
2019-03-22, Version 3.2.1
=========================
* fix: set `app.booting` flag immediately (Miroslav Bajtoš)
* fix: update lodash (jannyHou)
2018-10-18, Version 3.2.0
=========================
* README: update LTS status (Miroslav Bajtoš)
* Add support for es6 modules for boot scripts (Walker)
2018-07-26, Version 3.1.1
=========================
* update: dependency (jannyHou)
* chore: update dependencies (Diana Lau)
* [WebFM] cs/pl/ru translation (candytangnb)
* chore: update license (Diana Lau)
* CODEOWNERS: move @lehni to Alumni section (Miroslav Bajtoš)
* Add support for ES6 style async boot scripts (Jürg Lehni)
2017-10-13, Version 3.1.0
=========================
* update strong-globalize to 3.1.0 (shimks)
* CODEOWNERS: add zbarbuto (Miroslav Bajtoš)
* Ignore source maps in boot (Zak Barbuto)
* CODEOWNERS: add lehni (Miroslav Bajtoš)
* Create Issue and PR Templates (#261) (Sakib Hasan)
* Add CODEOWNER file (Diana Lau)
2017-06-22, Version 3.0.1
=========================
* Update Italian translated strings Q2 2017 (Allen Boone)
* Update translated strings Q2 2017 (Allen Boone)
* Replicate new issue_template from loopback (Siddhi Pai)
* Replicate issue_template from loopback repo (Siddhi Pai)
2017-05-22, Version 3.0.0
=========================
* Upgrade deps and fix style issues (Raymond Feng)
* Provide scriptExtensions option (Supasate Choochaisri)
* Update paid support URL (Siddhi Pai)
* Refactor for modular and pluggable design (Raymond Feng)
* Add Node v7 to Travis CI platforms (Miroslav Bajtoš)
* Drop support for Node v0.10 and v0.12 (Miroslav Bajtoš)
* readme: update URL to new doc site (David Cheung)
* Update ja translation file (Candy)
* Update header-browser.md (Sequoia McDowell)
* Update translation files - round#2 (Candy)
* Normalize line endings to support both LF and CRLF (Miroslav Bajtoš)
* Remove "defaultForType" from datasource config (Miroslav Bajtoš)
* Update deps to loopback 3.0.0 RC (Miroslav Bajtoš)
* globalization: add translated strings (gunjpan)
* Start development of 3.0 (Miroslav Bajtoš)
2016-09-05, Version 2.22.0
==========================
* Replace fs.existsSync calls with fs.statSync (Joshua Estrin Skrzypek)
* Change test cases port to be dynamic (David Cheung)
* Globalization for Loopback-boot (David Cheung)
2016-07-27, Version 2.21.0
==========================
* Configurable dir for components and middleware (Doped Dude)
* test: fix security warning (Miroslav Bajtoš)
2016-07-14, Version 2.20.0
==========================
* Update URLs in CONTRIBUTING.md (#198) (Ryan Graham)
* travis: drop io.js, add Node v4 and v6 (Miroslav Bajtoš)
* Stop caching config files (Miroslav Bajtoš)
2016-06-20, Version 2.19.0
==========================
* update copyright notices and license (Ryan Graham)
* Add flag var lazyConnect to ds config (juehou)
2016-04-13, Version 2.18.1
==========================
* parse config: should ignore null values (Loïc Mahieu)
2016-04-07, Version 2.18.0
==========================
* Dynamic datasources.json from ENV and config.json (David Cheung)
* Use eslint with loopback config (Miroslav Bajtoš)
2016-02-23, Version 2.17.0
==========================
* executor: move "booted" and cb() to the next tick (Miroslav Bajtoš)
* Fix lodash 4.0.0 breaking changes (Jérémie Drouet)
* When config is overriden with null don't merge (Farid Neshat)
2015-12-22, Version 2.16.0
==========================
* executor: allow loopback versions >= 3.0.0-alpha (Miroslav Bajtoš)
2015-12-04, Version 2.15.0
==========================
* Bluemix prefers HOST/PORT over VCAP_APP_XXXX (Sai Vennam)
* Set app env if it is supplied in options object (Amir Jafarian)
2015-11-24, Version 2.14.2
==========================
* executor: preserve RegExps in middleware paths (Miroslav Bajtoš)
2015-11-24, Version 2.14.1
==========================
* Warn user if missing a config file (Amir Jafarian)
* Refer to licenses with a link (Sam Roberts)
2015-10-14, Version 2.14.0
==========================
* Support bluemix env variables for host and port (Miroslav Bajtoš)
2015-10-01, Version 2.13.0
==========================
* add env folder for boot (yorkie)
* Use strongloop conventions for licensing (Sam Roberts)
2015-09-09, Version 2.12.2
==========================
* test: fix strict mode failure (Ryan Graham)
2015-08-31, Version 2.12.1
==========================
* Add config variable checks (Simon Ho)
2015-08-28, Version 2.12.0
==========================
* Resolve ${var} values in component-config.json (Hage Yaapa)
* Resolve ${var} values in middleware.json (Hack Sparrow)
* Upgrade Travis to container-based infrastructure (Miroslav Bajtoš)
2015-08-11, Version 2.11.0
==========================
* Allow middleware array merge by a key in item objects (Raymond Feng)
2015-08-10, Version 2.10.0
==========================
* Enhance middleware config merge (Raymond Feng)
2015-08-03, Version 2.9.0
=========================
* Fix the build failure (Raymond Feng)
* Fix the model-config/datasource merge (Raymond Feng)
* Resolved style issue (Dennis Ashby)
* Added code to allow model-config to respect model-config.local.js and model-config.env.js as do other config files. (Dennis Ashby)
* Add jsdoc for `options.mixinSources` (Miroslav Bajtoš)
* allow middleware to be optional (Hage Yaapa)
2015-06-24, Version 2.8.2
=========================
* Excl. mod. main path from middleware instructions (Miroslav Bajtoš)
2015-06-10, Version 2.8.1
=========================
* Add more debug info for config loading (Ritchie Martori)
* use a new variable for better debug output (Bryan Clark)
2015-05-29, Version 2.8.0
=========================
* Support iisnode using named pipes as PORT value (Jonathan Sheely)
* support 'mixinsources' option (Pradnya Baviskar)
* compiler: Simplify verifyModelDefinitions() (Miroslav Bajtoš)
* Fix coding style issues, add API docs (Miroslav Bajtoš)
* Extend options arg to support custom model definitions (Shlomi Assaf)
* add support for mixins - [mixinDirs]: List of directories to look for files containing model mixin definition. (Pradnya Baviskar)
2015-04-23, Version 2.7.1
=========================
* executor: fix port lookup (Miroslav Bajtoš)
* Clean up compiler.tryResolveAppPath (Miroslav Bajtoš)
* Configure Travis CI builds (Miroslav Bajtoš)
2015-04-15, Version 2.7.0
=========================
* Upgrade lodash and drop underscore.string (Bryan Clark)
* add console.error message to a bad require in a boot script (Bryan Clark)
* Support per-application registry of models (Miroslav Bajtoš)
* Use filename as default value for Model name (Pradnya Baviskar)
* compiler: code cleanup (Miroslav Bajtoš)
* Improve the resolution of relative paths - resolve module relative path for component - prioritize coffeescript over json (Pradnya Baviskar)
* Resolve module paths as relative to appRootDir - for middleware (Pradnya Baviskar)
* Support for multiple apps in browserified bundle. (Krishna Raman)
* Resolve missing file extension for module relative paths (Pradnya Baviskar)
* Resolve module paths as relative to appRootDir (Pradnya Baviskar)
* Resolve relative paths in using appRootDir (Pradnya Baviskar)
* Add feature to disable component (Pradnya Baviskar)
* Fix test for different line endings on Windows (Pradnya Baviskar)
* Refactor unit test assertions to be more specific (Simon Ho)
* Add unit test to verify `app.booting flag status (Simon Ho)
2015-02-20, Version 2.6.5
=========================
* Save instructions.json in root dir Saving in node_modules dir causes complaints and missing files fixes https://github.com/strongloop/loopback-boot/issues/94 (Berkeley Martinez)
2015-02-02, Version 2.6.4
=========================
* executor: pass correct `this` to middleware (Clark Wang)
* Fix broken links (Rand McKinney)
2015-01-13, Version 2.6.3
=========================
2015-01-13, Version 2.6.2
=========================
* Don't swallow error when a sub-dependency doesn't resolve. (Samuel Reed)
2015-01-12, Version 2.6.1
=========================
* Fix "incompatible loopback version" check & msg (Miroslav Bajtoš)
2015-01-08, Version 2.6.0
=========================
* Add "booting" flag and emit "booted" event (Simon Ho)
* Configure components via `component-config.json` (Miroslav Bajtoš)
* Fix bad CLA URL in CONTRIBUTING.md (Ryan Graham)
2014-12-19, Version 2.5.2
=========================
* Dedupe boot scripts (Eric Satterwhite)
2014-12-08, Version 2.5.1
=========================
* Replace underscore with lodash (Ryan Graham)
2014-12-02, Version 2.5.0
=========================
* compiler: resolve paths in middleware params (Miroslav Bajtoš)
2014-11-27, Version 2.4.0
=========================
* Implement shorthand notation for middleware paths (Raymond Feng)
* Load middleware and phases from `middleware.json` (Miroslav Bajtoš)
* Add jscs style check, fix violations found (Miroslav Bajtoš)
* Clean up .jshintrc (Miroslav Bajtoš)
* Use `chai` instead of `must` (Miroslav Bajtoš)
2014-11-10, Version 2.3.1
=========================
* Bump version (Raymond Feng)
* Fix the test for built-in models on Windows (Raymond Feng)
* Fix jsdoc (Raymond Feng)
2014-10-27, Version 2.3.0
=========================
* compiler: fix coding style violations (Miroslav Bajtoš)
* support coffee-script models and client code (bitmage)
2014-10-22, Version 2.2.0
=========================
* compiler: support module-relative model sources (Miroslav Bajtoš)
* Skip definitions of built-in loopback models (Miroslav Bajtoš)
* package: update dependency versions (Miroslav Bajtoš)
* Use loopback 2.x in unit tests. (Miroslav Bajtoš)
2014-10-09, Version 2.1.0
=========================
* Bump version (Raymond Feng)
* Add support for async boot scripts (Raymond Feng)
* Clean up jsdoc comments. (Miroslav Bajtoš)
* Custom rootDir for app config (johnsoftek)
* compiler: improve merging of Arrays and Objects (Miroslav Bajtoš)
* config-loader: deeply merge Array and Object vals (Shelby Sanders)
* gitignore: add Idea's *.iml files (Miroslav Bajtoš)
* package: Add `jshint` to `devDependencies` (Miroslav Bajtoš)
* Update contribution guidelines (Ryan Graham)
* test: ensure sandbox dir is present (Miroslav Bajtoš)
* test: add `global.navigator` for browser tests (Miroslav Bajtoš)
* test: increase timeout for browserify (Miroslav Bajtoš)
* index: fix jshint error (Miroslav Bajtoš)
* documentation fix (Alex)
* Fix typo (Fabien Franzen)
* Implemented modelSources, bootDirs and bootScripts options (Fabien Franzen)
2014-07-22, Version 2.0.0
=========================
* executor: remove `Base` arg from model function (Miroslav Bajtoš)
* package: update dependency versions (Miroslav Bajtoš)
2014-07-17, Version v2.0.0-beta3
================================
* compiler: return a clone of instructions (Miroslav Bajtoš)
2014-07-17, Version 2.0.0-beta2
===============================
* test: export Int32Array and DataView for browser (Miroslav Bajtoš)
* v2.0.0-beta2 (Miroslav Bajtoš)
* Rename `models.json` to `model-config.json` (Miroslav Bajtoš)
* Remove non-API docs. (Rand McKinney)
2014-06-26, Version 2.0.0-beta1
===============================
* test: fix jshint warnings (Miroslav Bajtoš)
* compiler: fix references to loopback (Miroslav Bajtoš)
* Rename `app.json` to `config.json` (Miroslav Bajtoš)
* compiler: Sort models topologically (Miroslav Bajtoš)
* executor: Split model boot into two phases (Miroslav Bajtoš)
* compiler: Move model-sources cfg to models.json (Miroslav Bajtoš)
* package: Bump up the version to 2.0.0-dev (Miroslav Bajtoš)
* Rework model configuration (Miroslav Bajtoš)
* Remove auto-attach. (Miroslav Bajtoš)
* Change models.json to configure existing models (Miroslav Bajtoš)
2014-06-26, Version 1.1.0
=========================
* docs: move hand-written content to README.md (Miroslav Bajtoš)
* executor: remove direct reference to loopback (Miroslav Bajtoš)
* Update link to doc (Rand McKinney)
* package: Fix repository url (Miroslav Bajtoš)
* Drop peer dep on loopback; add a runtime check (Miroslav Bajtoš)
* Wrap too long lines (Miroslav Bajtoš)
* Add disclaimer to JSDoc and small correction. (crandmck)
2014-06-05, Version 1.0.0
=========================
* First release!

11
CODEOWNERS Normal file
View File

@ -0,0 +1,11 @@
# Lines starting with '#' are comments.
# Each line is a file pattern followed by one or more owners,
# the last matching pattern has the most precendence.
# Current maintainers
* @raymondfeng @zbarbuto
# Alumni
#
# @lehni

View File

@ -1,65 +1,24 @@
### Contributing ###
Thank you for your interest in `loopback`, an open source project
Thank you for your interest in `loopback-boot`, an open source project
administered by StrongLoop.
Contributing to loopback is easy. In a few simple steps:
Contributing to `loopback-boot` is easy. In a few simple steps:
* Ensure that your effort is aligned with the projects roadmap by
* Ensure that your effort is aligned with the project's roadmap by
talking to the maintainers, especially if you are going to spend a
lot of time on it. This project is currently maintained by
[@ritch](https://github.com/ritch), [@raymondfeng](https://github.com/raymondfeng),
and [@bajtos](https://github.com/bajtos). The preferred channel of communication
is [LoopBack Forum](https://groups.google.com/forum/#!forum/loopbackjs) or
[Github Issues](https://github.com/strongloop/loopback/issues).
lot of time on it.
* Make something better or fix a bug.
* Adhere to code style outlined in the
* Adhere to code style outlined in the [Google C++ Style Guide][] and
[Google Javascript Style Guide][].
* [Sign your patches](#signing-patches) to indicate that your are
making your contribution available under the terms of the
[Contributor License Agreement](#contributor-license-agreement).
* Sign the [Contributor License Agreement](https://cla.strongloop.com/agreements/strongloop/loopback-boot)
* Submit a pull request through Github.
### Signing patches ###
Like many open source projects, we need a contributor license agreement
from you before we can merge in your changes.
In summary, by submitting your code, you are granting us a right to use
that code under the terms of this Agreement, including providing it to
others. You are also certifying that you wrote it, and that you are
allowed to license it to us. You are not giving up your copyright in
your work. The license does not change your rights to use your own
contributions for any other purpose.
Contributor License Agreements are important because they define the
chain of ownership of a piece of software. Some companies won't allow
the use of free software without clear agreements around code ownership.
That's why many open source projects collect similar agreements from
contributors. The CLA here is based on the Apache CLA.
To signify your agreement to these terms, add the following line to the
bottom of your commit message. Use your real name and an actual e-mail
address.
```
Signed-off-by: Random J Developer <random@developer.example.org>
```
Alternatively you can use the git command line to automatically add this
line, as follows:
```
$ git commit -sm "Replace rainbows by unicorns"
```
### Contributor License Agreement ###
```
@ -188,7 +147,5 @@ $ git commit -sm "Replace rainbows by unicorns"
inaccurate in any respect. Email us at callback@strongloop.com.
```
[Google Javascript Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[license]: LICENSE
[Google C++ Style Guide]: https://google.github.io/styleguide/cppguide.html
[Google Javascript Style Guide]: https://google.github.io/styleguide/javascriptguide.xml

299
LICENSE
View File

@ -1,8 +1,8 @@
Copyright (c) 2013-2014 StrongLoop, Inc and other contributors.
Copyright (c) IBM Corp. 2014,2017. All Rights Reserved.
Node module: loopback-boot
This project is licensed under the MIT License, full text below.
loopback uses a 'dual license' model. Users may use loopback under the terms of
the MIT license, or under the StrongLoop License. The text of both is included
below.
--------
MIT license
@ -23,294 +23,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
StrongLoop License
You may obtain a copy of the License at
http://www.strongloop.com/license/
STRONGLOOP SUBSCRIPTION AGREEMENT
PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU AGREE TO THESE TERMS. IF YOU
ARE ACTING ON BEHALF OF AN ENTITY, THEN YOU REPRESENT THAT YOU HAVE THE
AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT
AGREE TO THESE TERMS, YOU SHOULD NOT AGREE TO THE TERMS OF THIS AGREEMENT OR
INSTALL OR USE THE SOFTWARE.
This StrongLoop Subscription Agreement ("Agreement") is made by and between
StrongLoop, Inc. ("StrongLoop") with its principal place of business at 107 S.
B St, Suite 220, San Mateo, CA 94401 and the person or entity entering into this
Agreement ("Customer"). The effective date ("Effective Date") of this Agreement
is the date Customer agrees to these terms or installs or uses the Software (as
defined below). This Agreement applies to Customer's use of the Software but it
shall be superseded by any signed agreement between you and StrongLoop
concerning the Software.
1. Subscriptions and Licenses.
1.1 Subscriptions. StrongLoop offers five different subscription levels to its
customers, each as more particularly described on StrongLoop's website located
at www.strongloop.com (the "StrongLoop Site"): (1) Free; (2) Developer; (3)
Professional; (4) Gold; and (5) Platinum. The actual subscription level
applicable to Customer (the "Subscription") will be specified in the purchase
order that Customer issues to StrongLoop. This Agreement applies to Customer
regardless of the level of the Subscription selected by Customer and whether or
not Customer upgrades or downgrades its Subscription. StrongLoop hereby agrees
to provide the services as described on the StrongLoop Site for each
Subscription level during the term for which Customer has purchased the
applicable Subscription, subject to Customer paying the fees applicable to the
Subscription level purchased, if any (the "Subscription Fees"). StrongLoop may
modify the services to be provided under any Subscription upon notice to
Customer.
1.2 License Grant. Subject to the terms and conditions of this Agreement,
StrongLoop grants to Customer, during the Subscription Term (as defined in
Section 7.1 (Term and Termination) of this Agreement, a limited, non-exclusive,
non-transferable right and license, to install and use the StrongLoop Suite
software (the "Software") and the documentation made available electronically as
part of the Software (the "Documentation"), either of which may be modified
during the Term (as defined in Section 7.1 below), solely for development,
production and commercial purposes so long as Customer is using the Software to
run only one process on a given operating system at a time. This Agreement,
including but not limited to the license and restrictions contained herein,
apply to Customer regardless of whether Customer accesses the Software via
download from the StrongLoop Site or through a third-party website or service,
even if Customer acquired the Software prior to agreeing to this Agreement.
1.3 License Restrictions. Customer shall not itself, or through any parent,
subsidiary, affiliate, agent or other third party:
1.3.1 sell, lease, license, distribute, sublicense or otherwise transfer
in whole or in part, any Software or the Documentation to a third party;
or
1.3.2 decompile, disassemble, translate, reverse engineer or otherwise
attempt to derive source code from the Software, in whole or in part, nor
shall Customer use any mechanical, electronic or other method to trace,
decompile, disassemble, or identify the source code of the Software or
encourage others to do so, except to the limited extent, if any, that
applicable law permits such acts notwithstanding any contractual
prohibitions, provided, however, before Customer exercises any rights that
Customer believes to be entitled to based on mandatory law, Customer shall
provide StrongLoop with thirty (30) days prior written notice and provide
all reasonably requested information to allow StrongLoop to assess
Customer's claim and, at StrongLoop's sole discretion, to provide
alternatives that reduce any adverse impact on StrongLoop's intellectual
property or other rights; or
1.3.3 allow access or permit use of the Software by any users other than
Customer's employees or authorized third-party contractors who are
providing services to Customer and agree in writing to abide by the terms
of this Agreement, provided further that Customer shall be liable for any
failure by such employees and third-party contractors to comply with the
terms of this Agreement and no usage restrictions, if any, shall be
exceeded; or
1.3.4 create, develop, license, install, use, or deploy any third party
software or services to circumvent or provide access, permissions or
rights which violate the license keys embedded within the Software; or
1.3.5 modify or create derivative works based upon the Software or
Documentation; or disclose the results of any benchmark test of the
Software to any third party without StrongLoop's prior written approval;
or
1.3.6 change any proprietary rights notices which appear in the Software
or Documentation; or
1.3.7 use the Software as part of a time sharing or service bureau
purposes or in any other resale capacity.
1.4 Third-Party Software. The Software may include individual certain software
that is owned by third parties, including individual open source software
components (the "Third-Party Software"), each of which has its own copyright and
its own applicable license conditions. Such third-party software is licensed to
Customer under the terms of the applicable third-party licenses and/or copyright
notices that can be found in the LICENSES file, the Documentation or other
materials accompanying the Software, except that Sections 5 (Warranty
Disclaimer) and 6 (Limitation of Liability) also govern Customer's use of the
third-party software. Customer agrees to comply with the terms and conditions
of the relevant third-party software licenses.
2. Support Services. StrongLoop has no obligation to provide any support for
the Software other than the support services specifically described on the
StrongLoop Site for the Subscription level procured by Customer. However,
StrongLoop has endeavored to establish a community of users of the Software who
have provided their own feedback, hints and advice regarding their experiences
in using the Software. You can find that community and user feedback on the
StrongLoop Site. The use of any information, content or other materials from,
contained in or on the StrongLoop Site are subject to the StrongLoop website
terms of use located here http://www.strongloop.com/terms-of-service.
3. Confidentiality. For purposes of this Agreement, "Confidential Information"
means any and all information or proprietary materials (in every form and media)
not generally known in the relevant trade or industry and which has been or is
hereafter disclosed or made available by StrongLoop to Customer in connection
with the transactions contemplated under this Agreement, including (i) all trade
secrets, (ii) existing or contemplated Software, services, designs, technology,
processes, technical data, engineering, techniques, methodologies and concepts
and any related information, and (iii) information relating to business plans,
sales or marketing methods and customer lists or requirements. For a period of
five (5) years from the date of disclosure of the applicable Confidential
Information, Customer shall (i) hold the Confidential Information in trust and
confidence and avoid the disclosure or release thereof to any other person or
entity by using the same degree of care as it uses to avoid unauthorized use,
disclosure, or dissemination of its own Confidential Information of a similar
nature, but not less than reasonable care, and (ii) not use the Confidential
Information for any purpose whatsoever except as expressly contemplated under
this Agreement; provided that, to the extent the Confidential Information
constitutes a trade secret under law, Customer agrees to protect such
information for so long as it qualifies as a trade secret under applicable law.
Customer shall disclose the Confidential Information only to those of its
employees and contractors having a need to know such Confidential Information
and shall take all reasonable precautions to ensure that such employees and
contractors comply with the provisions of this Section. The obligations of
Customer under this Section shall not apply to information that Customer can
demonstrate (i) was in its possession at the time of disclosure and without
restriction as to confidentiality, (ii) at the time of disclosure is generally
available to the public or after disclosure becomes generally available to the
public through no breach of agreement or other wrongful act by Customer, (iii)
has been received from a third party without restriction on disclosure and
without breach of agreement by Customer, or (iv) is independently developed by
Customer without regard to the Confidential Information. In addition, Customer
may disclose Confidential Information as required to comply with binding orders
of governmental entities that have jurisdiction over it; provided that Customer
gives StrongLoop reasonable written notice to allow StrongLoop to seek a
protective order or other appropriate remedy, discloses only such Confidential
Information as is required by the governmental entity, and uses commercially
reasonable efforts to obtain confidential treatment for any Confidential
Information disclosed. Notwithstanding the above, Customer agrees that
StrongLoop, its employees and agents shall be free to use and employ their
general skills, know-how, and expertise, and to use, disclose, and employ any
generalized ideas, concepts, know-how, methods, techniques or skills gained or
learned during the Term or thereafter.
4. Ownership. StrongLoop shall retain all intellectual property and proprietary
rights in the Software, Documentation, and related works, including but not
limited to any derivative work of the foregoing and StrongLoop's licensors shall
retain all intellectual property and proprietary rights in any Third-Party
Software that may be provided with or as a part of the Software. Customer shall
do nothing inconsistent with StrongLoop's or its licensors' title to the
Software and the intellectual property rights embodied therein, including, but
not limited to, transferring, loaning, selling, assigning, pledging, or
otherwise disposing, encumbering, or suffering a lien or encumbrance upon or
against any interest in the Software. The Software (including any Third-Party
Software) contain copyrighted material, trade secrets and other proprietary
material of StrongLoop and/or its licensors.
5. Warranty Disclaimer. THE SOFTWARE (INCLUDING ANY THIRD-PARTY SOFTWARE) AND
DOCUMENTATION MADE AVAILABLE TO CUSTOMER ARE PROVIDED "AS-IS" AND STRONGLOOP,
ON BEHALF OF ITSELF AND ITS LICENSORS, EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY
KIND, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, TITLE,
PERFORMANCE, AND ACCURACY AND ANY IMPLIED WARRANTIES ARISING FROM STATUTE,
COURSE OF DEALING, COURSE OF PERFORMANCE, OR USAGE OF TRADE. STRONGLOOP DOES
NOT WARRANT THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR
ERROR-FREE, THAT DEFECTS IN THE SOFTWARE WILL BE CORRECTED OR THAT THE SOFTWARE
WILL PROVIDE OR ENSURE ANY PARTICULAR RESULTS OR OUTCOME. NO ORAL OR WRITTEN
INFORMATION OR ADVICE GIVEN BY STRONGLOOP OR ITS AUTHORIZED REPRESENTATIVES
SHALL CREATE A WARRANTY OR IN ANY WAY INCREASE THE SCOPE OF THIS WARRANTY.
STRONGLOOP IS NOT OBLIGATED TO PROVIDE CUSTOMER WITH UPGRADES TO THE SOFTWARE,
BUT MAY ELECT TO DO SO IN ITS SOLE DISCRETION. SOME JURISDICTIONS DO NOT ALLOW
THE EXCLUSION OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY NOT APPLY TO
CUSTOMER.WITHOUT LIMITING THE GENERALITY OF THE FOREGOING DISCLAIMER, THE
SOFTWARE AND DOCUMENTATION ARE NOT DESIGNED, MANUFACTURED OR INTENDED FOR USE IN
THE PLANNING, CONSTRUCTION, MAINTENANCE, CONTROL, OR DIRECT OPERATION OF NUCLEAR
FACILITIES, AIRCRAFT NAVIGATION, CONTROL OR COMMUNICATION SYSTEMS, WEAPONS
SYSTEMS, OR DIRECT LIFE SUPPORT SYSTEMS.
6. Limitation of Liability.
6.1 Exclusion of Liability. IN NO EVENT WILL STRONGLOOP OR ITS LICENSORS
BE LIABLE UNDER THIS AGREEMENT FOR ANY INDIRECT, RELIANCE, PUNITIVE,
CONSEQUENTIAL, SPECIAL, EXEMPLARY, OR INCIDENTAL DAMAGES OF ANY KIND AND
HOWEVER CAUSED (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF
BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION AND
THE LIKE), EVEN IF STRONGLOOP HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES. CUSTOMER BEARS FULL RESPONSIBILITY FOR USE OF THE SOFTWARE AND
THE SUBSCRIPTION AND STRONGLOOP DOES NOT GUARANTEE THAT THE USE OF THE
SOFTWARE AND SUBSCRIPTION WILL ENSURE THAT CUSTOMER'S NETWORK WILL BE
AVAILABLE, SECURE, MONITORED OR PROTECTED AGAINST ANY DOWNTIME, DENIAL OF
SERVICE ATTACKS, SECUITY BREACHES, HACKERS AND THE LIKE. IN NO EVENT WILL
STRONGLOOP'S CUMULATIVE LIABILITY FOR ANY DAMAGES, LOSSES AND CAUSES OF
ACTION (WHETHER IN CONTRACT, TORT, INCLUDING NEGLIGENCE, OR OTHERWISE)
ARISING OUT OF OR RELATED TO THIS AGREEMENT EXCEED THE GREATER OF ONE
HUNDRED DOLLARS (US$100) OR THE TOTAL SUBSCRIPTION FEES PAID BY CUSTOMER
TO STRONGLOOP IN THE TWELVE (12) MONTHS PRECEDING THE DATE THE CLAIM
ARISES.
6.2 Limitation of Damages. IN NO EVENT WILL STRONGLOOP'S LICENSORS HAVE
ANY LIABILITY FOR ANY CLAIM ARISING IN CONNECTION WITH THIS AGREEMENT.
THE PROVISIONS OF THIS SECTION 6 ALLOCATE RISKS UNDER THIS AGREEMENT
BETWEEN CUSTOMER, STRONGLOOP AND STRONGLOOP'S SUPPLIERS. THE FOREGOING
LIMITATIONS, EXCLUSIONS AND DISCLAIMERS APPLY TO THE MAXIMUM EXTENT
PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS IN ITS ESSENTIAL
PURPOSE.
6.3 Failure of Essential Purpose. THE PARTIES AGREE THAT THESE
LIMITATIONS SHALL APPLY EVEN IF THIS AGREEMENT OR ANY LIMITED REMEDY
SPECIFIED HEREIN IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE.
6.4 Allocation of Risk. The sections on limitation of liability and
disclaimer of warranties allocate the risks in the Agreement between the
parties. This allocation is an essential element of the basis of the
bargain between the parties.
7. Term and Termination.
7.1 This Agreement shall commence on the Effective Date and continue for so long
as Customer has a valid Subscription and is current on the payment of any
Subscription Fees required to be paid for that Subscription (the "Subscription
Term"). Either party may terminate this Agreement immediately upon written
notice to the other party, and the Subscription and licenses granted hereunder
automatically terminate upon the termination of this Agreement. This Agreement
will terminate immediately without notice from StrongLoop if Customer fails to
comply with or otherwise breaches any provision of this Agreement.
7.2 All Sections other than Section 1.1 (Subscriptions) and 1.2 (Licenses) shall
survive the expiration or termination of this Agreement.
8. Subscription Fees and Payments. StrongLoop, Customer agrees to pay
StrongLoop the Subscription Fees as described on the StrongLoop Site for the
Subscription purchased unless a different amount has been agreed to in a
separate agreement between Customer and StrongLoop. In addition, Customer shall
pay all sales, use, value added, withholding, excise taxes and other tax, duty,
custom and similar fees levied upon the delivery or use of the Software and the
Subscriptions described in this Agreement. Fees shall be invoiced in full upon
StrongLoop's acceptance of Customer's purchase order for the Subscription. All
invoices shall be paid in US dollars and are due upon receipt and shall be paid
within thirty (30) days. Payments shall be made without right of set-off or
chargeback. If Customer does not pay the invoices when due, StrongLoop may
charge interest at one percent (1%) per month or the highest rate permitted by
law, whichever is lower, on the unpaid balance from the original due date. If
Customer fails to pay fees in accordance with this Section, StrongLoop may
suspend fulfilling its obligations under this Agreement (including but not
limited to suspending the services under the Subscription) until payment is
received by StrongLoop. If any applicable law requires Customer to withhold
amounts from any payments to StrongLoop under this Agreement, (a) Customer shall
effect such withholding, remit such amounts to the appropriate taxing
authorities and promptly furnish StrongLoop with tax receipts evidencing the
payments of such amounts and (b) the sum payable by Customer upon which the
deduction or withholding is based shall be increased to the extent necessary to
ensure that, after such deduction or withholding, StrongLoop receives and
retains, free from liability for such deduction or withholding, a net amount
equal to the amount StrongLoop would have received and retained absent the
required deduction or withholding.
9. General.
9.1 Compliance with Laws. Customer shall abide by all local, state, federal and
international laws, rules, regulations and orders applying to Customer's use of
the Software, including, without limitation, the laws and regulations of the
United States that may restrict the export and re-export of certain commodities
and technical data of United States origin, including the Software. Customer
agrees that it will not export or re-export the Software without the appropriate
United States or foreign government licenses.
9.2 Entire Agreement. This Agreement constitutes the entire agreement between
the parties concerning the subject matter hereof. This Agreement supersedes all
prior or contemporaneous discussions, proposals and agreements between the
parties relating to the subject matter hereof. No amendment, modification or
waiver of any provision of this Agreement shall be effective unless in writing
and signed by both parties. Any additional or different terms on any purchase
orders issued by Customer to StrongLoop shall not be binding on either party,
are hereby rejected by StrongLoop and void.
9.3 Severability. If any provision of this Agreement is held to be invalid or
unenforceable, the remaining portions shall remain in full force and effect and
such provision shall be enforced to the maximum extent possible so as to effect
the intent of the parties and shall be reformed to the extent necessary to make
such provision valid and enforceable.
9.4 Waiver. No waiver of rights by either party may be implied from any actions
or failures to enforce rights under this Agreement.
9.5 Force Majeure. Neither party shall be liable to the other for any delay or
failure to perform due to causes beyond its reasonable control (excluding
payment of monies due).
9.6 No Third Party Beneficiaries. Unless otherwise specifically stated, the
terms of this Agreement are intended to be and are solely for the benefit of
StrongLoop and Customer and do not create any right in favor of any third party.
9.7 Governing Law and Jurisdiction. This Agreement shall be governed by the
laws of the State of California, without reference to the principles of
conflicts of law. The provisions of the Uniform Computerized Information
Transaction Act and United Nations Convention on Contracts for the International
Sale of Goods shall not apply to this Agreement. The parties shall attempt to
resolve any dispute related to this Agreement informally, initially through
their respective management, and then by non-binding mediation in San Francisco
County, California. Any litigation related to this Agreement shall be brought
in the state or federal courts located in San Francisco County, California, and
only in those courts and each party irrevocably waives any objections to such
venue.
9.8 Notices. All notices must be in writing and shall be effective three (3)
days after the date sent to the other party's headquarters, Attention Chief
Financial Officer.

331
README.md
View File

@ -1,10 +1,40 @@
# LoopBack Boot
LoopBack Boot is a convention-based bootstrapper for LoopBack applications.
**⚠️ LoopBack 3 has reached end of life. We are no longer accepting pull requests or providing
support for community users. The only exception is fixes for critical bugs and security
vulnerabilities provided as part of support for IBM API Connect customers. (See
[Module Long Term Support Policy](#module-long-term-support-policy) below.)**
**For full documentation, see the official StrongLoop documentation:**
We urge all LoopBack 3 users to migrate their applications to LoopBack 4 as
soon as possible. Refer to our
[Migration Guide](https://loopback.io/doc/en/lb4/migration-overview.html)
for more information on how to upgrade.
* [Creating a LoopBack application](http://docs.strongloop.com/display/LB/Creating+a+LoopBack+application)
## Overview
A convention-based bootstrapper for LoopBack applications.
For full documentation, see the official StrongLoop documentation: [Defining boot scripts](https://loopback.io/doc/en/lb2/Defining-boot-scripts) and [Creating a LoopBack application](https://loopback.io/doc/en/lb2/Creating-an-application).
The loopback-boot module initializes (bootstraps) a LoopBack application. Specifically, it:
- Configures data-sources.
- Defines custom models
- Configures models and attaches models to data-sources.
- Configures application settings
- Runs additional boot scripts, so you can put custom setup code in multiple small files instead of in the main application file.
For more information, see [Defining boot scripts](https://loopback.io/doc/en/lb2/Defining-boot-scripts).
### Version notes
The version range `1.x` is backwards compatible with `app.boot` provided
by LoopBack 1.x versions and the project layout scaffolded by `slc lb project`
up to slc version 2.5.
The version range `2.x` supports the new project layout as scaffolded by
`yo loopback`.
This document describes the configuration conventions of the `2.x` versions.
## Installation
@ -23,293 +53,22 @@ app.use(loopback.rest());
app.listen();
```
See [API docs](http://apidocs.strongloop.com/loopback-boot/#api) for
See [API docs](http://apidocs.strongloop.com/loopback-boot/) for
complete API reference.
## Configurations and conventions
## Module Long Term Support Policy
The bootstrapping process takes care of the following tasks:
This module adopts the [
Module Long Term Support (LTS)](http://github.com/CloudNativeJS/ModuleLTS) policy,
with the following End Of Life (EOL) dates:
- Configuration of data-sources.
- Definition and configuration of custom Models, attaching models to
data-sources.
- Configuration of app settings like `host`, `port` or `restApiRoot`.
- Running additional boot scripts to keep the custom setup code in multiple
small files as opposed to keeping everything in the main app file.
| Version | Status | Published | EOL |
| ------- | --------------- | --------- | -------- |
| 3.x | End-of-Life | May 2017 | Dec 2020 |
| 2.x | End-of-Life | Jul 2014 | Apr 2019 |
Below is the typical project layout. See the following sections for description
of the project files.
Learn more about our LTS plan in [docs](https://loopback.io/doc/en/contrib/Long-term-support.html).
```
project/
app.js
app.json
datasources.json
models.json
models/
boot/
```
## License
### App settings
The settings are loaded from the file `app.json` in the project root directory
and can be accessed via `app.get('option-name')` from the code.
Additionally, the following files can provide values to override `app.json`:
- `app.local.js` or `app.local.json`
- `app.{env}.js` or `app.{env}.json`, where `{env}` is the value of `NODE_ENV`
(typically `development` or `production`)
**NOTE:** The additional files can override the top-level keys with
value-types (strings, numbers) only. Nested objects and arrays are
not supported at the moment.
#### Example settings
*app.json*
```json
{
"host": "localhost",
"port": 3000,
"restApiRoot": "/api"
}
```
*app.production.js*
```js
module.exports = {
host: process.env.CUSTOM_HOST,
port: process.env.CUSTOM_PORT
};
```
### Data sources
The configuration of data sources is loaded from the file `datasources.json`
in the project root directory, the data sources can be accessed via
`app.datasources['datasource-name']` from the code.
Additionally, the following files can provide values to override
`datasources.json`:
- `datasources.local.js` or `datasources.local.json`
- `datasources.{env}.js` or `datasources.{env}.json`,
where `{env}` is the value of `NODE_ENV`
(typically `development` or `production`)
**NOTE:** The additional files can override the top-level data-source options
with value-types (strings, numbers) only. Nested objects and arrays are
not supported at the moment.
#### Example data sources
*datasources.json*
```js
{
// the key is the datasource name
// the value is the config object to pass to
// app.dataSource(name, config).
db: {
connector: 'memory'
}
}
```
*datasources.production.json*
```js
{
db: {
connector: 'mongodb',
database: 'myapp',
user: 'myapp',
password: 'secret'
}
}
```
### Models
App models are loaded from the file `models.json`.
#### Example models
The following is example JSON for two `Model` definitions:
`Dealership` and `Location`.
```js
{
// the key is the model name
"Dealership": {
// a reference, by name, to a dataSource definition
"dataSource": "my-db",
// the options passed to Model.extend(name, properties, options)
"options": {
"relations": {
"cars": {
"type": "hasMany",
"model": "Car",
"foreignKey": "dealerId"
}
}
},
// the properties passed to Model.extend(name, properties, options)
"properties": {
"id": {"id": true},
"name": "String",
"zip": "Number",
"address": "String"
}
},
"Car": {
"dataSource": "my-db"
// options can be specified at the top level too
"relations": {
"dealer": {
"type": "belongsTo",
"model": "Dealership",
"foreignKey": "dealerId"
},
}
"properties": {
"id": {
"type": "String",
"required": true,
"id": true
},
"make": {
"type": "String",
"required": true
},
"model": {
"type": "String",
"required": true
}
}
}
}
```
#### Adding custom methods to models
The models created from `models.json` come with the set of built-in methods
like `find` and `create`. To implement your custom methods, you should
create a javascript file in `models/` directory named after the model
and define the methods there.
Example:
*models/car.js*
```js
module.exports = function(app) {
var Car = app.models.Car;
Car.prototype.honk = function(duration, cb) {
// make some noise for `duration` seconds
cb();
};
};
```
### Boot scripts
When the data sources and models are configured, the bootstrapper invokes
all scripts in the `boot/` folder. The scripts are sorted lexicographically
ingoring case.
#### Example boot script
*boot/authentication.js*
```js
module.exports = function(app) {
app.enableAuth();
};
```
## Running in a browser
The bootstrap process is implemented in two steps that can be called
independently.
### Build
The first step loads all configuration files, merges values from additional
config files like `app.local.js` and produces a set of instructions
that can be used to boot the application.
These instructions must be included in the browser bundle together
with all configuration scripts from `models/` and `boot/`.
Don't worry, you don't have to understand these details.
Just call `boot.compileToBrowserify`, it will take care of everything for you.
*build file (Gruntfile.js, gulpfile.js)*
```js
var browserify = require('browserify');
var boot = require('loopback-boot');
var b = browserify({
basedir: appDir,
});
// add the main application file
b.require('./browser-app.js', { expose: 'loopback-app' });
// add boot instructions
boot.compileToBrowserify(appDir, b);
// create the bundle
var out = fs.createWriteStream('browser-bundle.js');
b.bundle().pipe(out);
// handle out.on('error') and out.on('close')
```
### Run
In the browser, the main application file should call loopback-boot
to setup the loopback application by executing the instructions
contained in the browser bundle:
*browser-app.js*
```js
var loopback = require('loopback');
var boot = require('loopback-boot');
var app = module.exports = loopback();
boot(app);
```
The app object created above can be accessed via `require('loopback-app')`,
where `loopback-app` is the identifier used for the main app file in
the browserify build shown above.
Here is a simple example demonstrating the concept:
*index.html*
```xml
<script src="app.bundle.js"></script>
<script>
var app = require('loopback-app');
var User = app.models.User;
User.login(
{ email: 'test@example.com', password: '12345' },
function(err, res) {
if (err) {
console.error('Login failed: ', err);
} else {
console.log('Logged in.');
}
}
);
</script>
```
This module is provided under dual MIT/StrongLoop license. See [LICENSE](LICENSE) for details.

View File

@ -1,4 +1,11 @@
var execute = require('./lib/executor');
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const Bootstrapper = require('./lib/bootstrapper');
/**
* The browser version of `bootLoopBackApp`.
@ -10,15 +17,30 @@ var execute = require('./lib/executor');
* the browser bundle, see `boot.compileToBrowserify`.
*
* @param {Object} app The loopback app to boot, as returned by `loopback()`.
* @param {Object|string} [options] options as described in
* `boot.compileToBrowserify`.
*
* @header bootBrowserApp(app)
* @header boot(app)
*/
exports = module.exports = function bootBrowserApp(app) {
exports = module.exports = function bootBrowserApp(app, options, callback) {
// Only using options.id to identify the browserified bundle to load for
// this application. If no Id was provided, load the default bundle.
let moduleName = 'loopback-boot#instructions';
const appId = options && typeof options === 'object' && options.appId;
if (appId)
moduleName += '-' + appId;
// The name of the module containing instructions
// is hard-coded in lib/bundler
var instructions = require('loopback-boot#instructions');
execute(app, instructions);
const instructions = require(moduleName);
const bootstrapper = new Bootstrapper(options);
bootstrapper.phases = ['starting', 'start', 'started'];
const context = {
app: app,
instructions: instructions,
};
return bootstrapper.run(context, callback);
};
exports.execute = execute;

View File

@ -1,11 +1,8 @@
{
"content": [
"README.md",
{
"title": "API",
"depth": 2
},
"docs/header-node.md",
"index.js",
"docs/header-browser.md",
"browser.js"
]
}

35
docs/coffee.md Normal file
View File

@ -0,0 +1,35 @@
## Server Files in Coffee-Script
In order to create application files in coffee-script, you'll need to register the coffee-script extension:
```javascript
require('coffee-script/register');
```
You'll need to do this at any entry points for the app (e.g. the server.js file, mocha.opts, and gulp/grunt). It is recommended to leave the entry point of the app (server.js by default, specified as 'main' in package.json) as a javascript file, and then register coffee-script, and proceed with any coffee-requires.
## Client Files in Coffee-Script
You can use the Coffeeify module to include Coffee files in your browser package. Use a build script like this:
```javascript
// assuming you haven't done so already
require('coffee-script/register');
var b = browserify({
basedir: appDir,
extensions: ['.coffee'], //causes browserify to look for this extension
debug: true
});
b.transform('coffeeify'); //adds coffee compiler to the build pipeline
b.require('./app.coffee', { expose: 'browser-app' }); //requiring your file will set the entry point
boot.compileToBrowserify(appDir, b);
var bundlePath = sandbox.resolve('browser-app-bundle.js'); //remember, the final result is still '.js'
var out = fs.createWriteStream(bundlePath);
b.bundle().pipe(out);
```

15
docs/header-browser.md Normal file
View File

@ -0,0 +1,15 @@
## Browser API
Use this API in the `app.js` file that you process by browserify and run in the browser.
```js
var loopback = require('loopback');
var boot = require('loopback-boot');
var app = module.exports = loopback();
boot(app);
```
### Browserify Note
Loopback-boot will *not work correctly* with `fullpaths` option set in browserify/watchify.

11
docs/header-node.md Normal file
View File

@ -0,0 +1,11 @@
## Node API
Use this API in the `app.js` file of your server-side Node.js application.
```js
var loopback= require('loopback');
var boot = require('loopback-boot');
var app = module.exports = loopback();
boot(app, __dirname);
```

View File

@ -0,0 +1,133 @@
## Migrating from 1.x to 2.x
**Starting point: a sample 1.x project**
*models.json*
```json
{
"car": {
"properties": {
"color": "string",
},
"dataSource": "db"
}
}
```
*models/car.js*
```js
var app = require('../app');
var Car = app.models.Car;
Car.prototype.honk = function(duration, cb) {
// make some noise for `duration` seconds
cb();
};
```
*app.js*
```js
var loopback = require('loopback');
var boot = require('loopback-boot');
var app = loopback();
boot(app, __dirname);
```
### App settings
The files with applications settings were renamed from `app.*` to `config.*`.
Rename the following files to upgrade a 1.x project for loopback-boot 2.x:
- `app.json` to `config.json`
- `app.local.json` to `config.local.json`
- `app.local.js` to `config.local.js`
- etc.
### Data sources
The configuration of data sources remains the same in both 1.x and 2.x
versions.
### Models
**The 2.x version of loopback-boot no longer creates Models, it's up to the
developer to create them before booting the app.**
The folder `models/` has a different semantincs in 2.x than in 1.x. Instead
of extending Models already defined by `app.boot` and `models.json`,
it provides a set of Model definitions that do not depend on
any application that may use them.
Perform the following steps to update a 1.x project for loopback-boot 2.x.
All code samples are referring to the sample project described above.
1. Move all Model-definition metadata from `models.json`
to new per-model json files in `models/` directory.
*models/car.json*
```json
{
"name": "car",
"properties": {
"color": "string",
}
}
```
*models.json*
```json
{
"car": {
"dataSource": "db"
}
}
```
2. Change per-model javascript files to export a function that adds
custom methods to the model class.
*models/car.js*
```js
module.exports = function(Car, Base) {
Car.prototype.honk = function(duration, cb) {
// make some noise for `duration` seconds
cb();
};
};
```
3. If your model definitions are not in `./models`, then add an entry
to `models.json` to specify the paths where to look for model definitions.
*models.json*
```json
{
"_meta": {
"sources": ["./custom/path/to/models"]
},
"Car": {
"dataSource": "db"
}
}
```
### Attaching built-in models
Models provided by LoopBack, such as `User` or `Role`, are no longer
automatically attached to default data-sources. The data-source configuration
entry `defaultForType` is silently ignored.
You have to explicitly configure all built-in models used by your application
in the `models.json` file.
```
{
"Role": { "dataSource": "db" }
}
```

180
index.js
View File

@ -1,7 +1,16 @@
var ConfigLoader = require('./lib/config-loader');
var compile = require('./lib/compiler');
var execute = require('./lib/executor');
var addInstructionsToBrowserify = require('./lib/bundler');
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
// Strong globalize
const g = require('./lib/globalize');
const PluginBase = require('./lib/plugin-base');
const Bootstrapper = require('./lib/bootstrapper');
const addInstructionsToBrowserify = require('./lib/bundler');
/**
* Initialize an application from an options object or
@ -20,21 +29,65 @@ var addInstructionsToBrowserify = require('./lib/bundler');
* 1. Creates DataSources from the `datasources.json` file in the application
* root directory.
*
* 2. Creates Models from the `models.json` file in the application
* 2. Configures Models from the `model-config.json` file in the application
* root directory.
*
* If the argument is an object, then it looks for `model`, `dataSources`,
* and `appRootDir` properties of the object.
* 3. Configures the LoopBack Application object from the `config.json` file
* in the application root directory. These properties can be accessed
* using `app.get('propname')`.
*
* If the argument is an object, then it looks for `models`, `dataSources`,
* 'config', `modelsRootDir`, `dsRootDir`, `appConfigRootDir` and `appRootDir`
* properties of the object.
*
* If the object has no `appRootDir` property then it sets the current working
* directory as the application root directory.
*
* The execution environment, {env}, is established from, in order,
* - `options.env`
* - `process.env.NODE_ENV`,
* - the literal `development`.
*
* Then it:
*
* 1. Creates DataSources from the `options.dataSources` object.
* 1. Creates DataSources from the `options.dataSources` object, if provided;
* otherwise, it searches for the files
* - `datasources.json`,
* - `datasources.local.js` or `datasources.local.json` (only one),
* - `datasources.{env}.js` or `datasources.{env}.json` (only one)
*
* 2. Creates Models from the `options.models` object.
* in the directory designated by 'options.dsRootDir', if present, or the
* application root directory. It merges the data source definitions from
* the files found.
*
* In both cases, the function loads JavaScript files in the `/models` and
* `/boot` subdirectories of the application root directory with `require()`.
* 2. Creates Models from the `options.models` object, if provided;
* otherwise, it searches for the files
* - `model-config.json`,
* - `model-config.local.js` or `model-config.local.json` (only one),
* - `model-config.{env}.js` or `model-config.{env}.json` (only one)
*
* in the directory designated by 'options.modelsRootDir', if present, or
* the application root directory. It merges the model definitions from the
* files found.
*
* 3. Configures the Application object from the `options.config` object,
* if provided;
* otherwise, it searches for the files
* - `config.json`,
* - `config.local.js` or `config.local.json` (only one),
* - `config.{env}.js` or `config.{env}.json` (only one)
*
* in the directory designated by 'options.appConfigRootDir', if present, or
* the application root directory. It merges the properties from the files
* found.
*
* In both cases, the function loads JavaScript files in the
* `/boot` subdirectory of the application root directory with `require()`.
*
* **NOTE:** The version 2.0 of loopback-boot changed the way how models
* are created. The `model-config.json` file contains only configuration
* options like dataSource and extra relations. To define a model,
* create a per-model JSON file in `models/` directory.
*
* **NOTE:** Mixing `bootLoopBackApp(app, bootConfig)` and
* `app.model(name, modelConfig)` in multiple
@ -47,46 +100,111 @@ var addInstructionsToBrowserify = require('./lib/bundler');
* @param app LoopBack application created by `loopback()`.
* @options {String|Object} options Boot options; If String, this is
* the application root directory; if object, has below properties.
* @property {String} appRootDir Directory to use when loading JSON and
* JavaScript files (optional).
* @property {String} [appRootDir] Directory to use when loading JSON and
* JavaScript files.
* Defaults to the current directory (`process.cwd()`).
* @property {Object} models Object containing `Model` definitions (optional).
* @property {Object} dataSources Object containing `DataSource`
* definitions (optional).
* @property {String} modelsRootDir Directory to use when loading `models.json`
* and `models/*.js`. Defaults to `appRootDir`.
* @property {String} datasourcesRootDir Directory to use when loading
* @property {String} [appConfigRootDir] Directory to use when loading
* `config.json`. Defaults to `appRootDir`.
* @property {Object} [models] Object containing `Model` configurations.
* @property {Array} [modelDefinitions] List of model definitions to use.
* When `options.modelDefinitions` is provided, loopback-boot does not
* search filesystem and use only the models provided in this argument.
* @property {Object} [dataSources] Object containing `DataSource` definitions.
* @property {String} [modelsRootDir] Directory to use when loading
* `model-config.json`. Defaults to `appRootDir`.
* @property {String} [dsRootDir] Directory to use when loading
* `datasources.json`. Defaults to `appRootDir`.
* @property {String} env Environment type, defaults to `process.env.NODE_ENV`
* @property {String} [middlewareRootDir] Directory to use when loading
* `middleware.json`. Defaults to `appRootDir`.
* @property {String} [componentRootDir] Directory to use when loading
* `component-config.json`. Defaults to `appRootDir`.
* @property {String} [env] Environment type, defaults to `process.env.NODE_ENV`
* or `development`. Common values are `development`, `staging` and
* `production`; however the applications are free to use any names.
* @property {Array.<String>} [modelSources] List of directories where to look
* for files containing model definitions.
* @property {Object} [middleware] Middleware configuration to use instead
* of `{appRootDir}/middleware.json`
* @property {Object} [components] Component configuration to use instead
* of `{appRootDir}/component-config.json`
* @property {Array.<String>} [mixinDirs] List of directories where to look
* for files containing model mixin definitions. All files (mixins) found
* in these directory are loaded.
* @property {Array.<String>} [mixinSources] List of directories where to look
* for files containing model mixin definitions. Only mixins used by
* application models are loaded from these directories.
* @property {Array.<String>} [bootDirs] List of directories where to look
* for boot scripts.
* @property {Array.<String>} [bootScripts] List of script files to execute
* on boot.
* @property {String|Function|Boolean} [normalization] Mixin normalization
* format: false, 'none', 'classify', 'dasherize' - defaults to 'classify'.
* @end
* @param {Function} [callback] Callback function.
*
* @header bootLoopBackApp(app, [options])
* @header boot(app, [options], [callback])
*/
exports = module.exports = function bootLoopBackApp(app, options) {
exports = module.exports = function bootLoopBackApp(app, options, callback) {
if (typeof options === 'string') {
// The 2nd arg is appRootDir
options = {appRootDir: options};
}
if (typeof options === 'function' && callback === undefined) {
callback = options;
options = {};
}
options = options || {};
// backwards compatibility with loopback's app.boot
options.env = options.env || app.get('env');
var instructions = compile(options);
execute(app, instructions);
const bootstrapper = new Bootstrapper(options);
const context = {
bootstrapper: bootstrapper,
app: app,
};
return bootstrapper.run(context, callback);
};
exports.compile = function(options, done) {
const bootstrapper = new Bootstrapper(options);
bootstrapper.phases = ['load', 'compile'];
const context = {};
return bootstrapper.run(context, done);
};
/**
* Compile boot instructions and add them to a browserify bundler.
* @param {Object|String} options as described in `bootLoopBackApp` above.
* @property {String} [appId] Application identifier used to load the correct
* boot configuration when building multiple applications using browserify.
* @end
* @param {Object} bundler A browserify bundler created by `browserify()`.
*
* @header boot.compileToBrowserify(options, bundler)
*/
exports.compileToBrowserify = function(options, bundler) {
addInstructionsToBrowserify(compile(options), bundler);
exports.compileToBrowserify = function(options, bundler, done) {
return exports.compile(options, function(err, context) {
if (err) return done(err);
addInstructionsToBrowserify({instructions: context.instructions},
bundler);
done();
});
};
//-- undocumented low-level API --//
exports.ConfigLoader = ConfigLoader;
exports.compile = compile;
exports.execute = execute;
exports.addInstructionsToBrowserify = addInstructionsToBrowserify;
exports.Bootstrapper = Bootstrapper;
exports.PluginBase = PluginBase;
exports.execute = function(app, instructions, done) {
const bootstrapper = new Bootstrapper(
{phases: ['starting', 'start', 'started']},
);
const context = {
app: app,
instructions: instructions,
};
return bootstrapper.run(context, done);
};

17
intl/cs/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Nelze vyřešit cestu \"{0}\"",
"34319676975b1abf107da7a056abb434": "Neplatný formát normalizace - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "Aplikace `app` je založena na nekompatibilní verzí Loopback {0}. Podporované verze: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "VAROVÁNÍ: Hlavní konfigurační soubor \"{0}{{.json}}\" chybí",
"4d052d84c8620730afd4a30832f11724": "Nelze konfigurovat neznámý model {0}",
"4ed668e9187650d898acf97707df445a": "Fáze {{phase}} \"{0}\" není definována v hlavní konfiguraci.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Konflikt řazení: Nelze přidat \"{0}\" za \"{1}\", protože již bylo uvedené opačné pořadí",
"70654dc6eb565613a33344efed3de998": "Nezdařilo se načíst zaváděcí skript: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} se neinterpretuje na platnou hodnotu, vráceno jako {1}. \"{2}\" musí být rozdělitelný v proměnné prostředí nebo {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "{{middleware}} \"{0}\" v {{phase}} \"{1}\" není definováno v hlavní konfiguraci.",
"a3aa22086ae4976cd013065c9a3ff81c": "Nelze použít {0}: ",
"be2cf2868ba54624fe38e9908dde5e9e": "Data v {{model-config.json}} jsou v nepodporovaném formátu {{1.x}}.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "Vyřazení pokynů {{middleware}}, klient {{loopback}} nepodporuje {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Middleware \"{0}\" nelze nalézt: {1}"
}

17
intl/de/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Pfad \"{0}\" kann nicht aufgelöst werden",
"34319676975b1abf107da7a056abb434": "Ungültiges Normalisierungsformat - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "Die `app` wird von einer nicht kompatiblen Loopback-Version {0} betrieben. Unterstützte Versionen: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "WARNUNG: Hauptkonfigurationsdatei \"{0}{{.json}}\" fehlt",
"4d052d84c8620730afd4a30832f11724": "Unbekanntes Modell {0} kann nicht konfiguriert werden",
"4ed668e9187650d898acf97707df445a": "Die {{phase}} \"{0}\" ist in der Hauptkonfiguration nicht definiert.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Sortierungskonflikt: \"{0}\" kann nicht nach \"{1}\" hinzugefügt werden, da die entgegengesetzte Reihenfolge bereits angegeben wurde",
"70654dc6eb565613a33344efed3de998": "Laden von Boot-Script fehlgeschlagen: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} wird nicht in einen gültigen Wert aufgelöst; zurückgegeben als {1}. \"{2}\" muss in der Umgebungsvariable oder über {{app.get()}} auflösbar sein.",
"91a742b7c3568cf6b6755741a70b3c52": "Die {{middleware}} \"{0}\" in {{phase}} \"{1}\" ist in der Hauptkonfiguration nicht definiert.",
"a3aa22086ae4976cd013065c9a3ff81c": "{0} kann nicht angewendet werden: ",
"be2cf2868ba54624fe38e9908dde5e9e": "Die Daten in {{model-config.json}} haben das nicht unterstützte {{1.x}}-Format.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "{{middleware}}-Anweisungen werden verworfen, {{loopback}}-Client unterstützt {{middleware}} nicht.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Middleware \"{0}\" nicht gefunden: {1}"
}

16
intl/en/messages.json Normal file
View File

@ -0,0 +1,16 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Cannot resolve path \"{0}\"",
"34319676975b1abf107da7a056abb434": "Invalid normalization format - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "The `app` is powered by an incompatible loopback version {0}. Supported versions: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "WARNING: Main config file \"{0}{{.json}}\" is missing",
"4d052d84c8620730afd4a30832f11724": "Cannot configure unknown model {0}",
"4ed668e9187650d898acf97707df445a": "The {{phase}} \"{0}\" is not defined in the main config.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Ordering conflict: cannot add \"{0}\" after \"{1}\", because the opposite order was already specified",
"70654dc6eb565613a33344efed3de998": "Failed loading boot script: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} does not resolve to a valid value, returned as {1}. \"{2}\" must be resolvable in Environment variable or by {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "The {{middleware}} \"{0}\" in {{phase}} \"{1}\"is not defined in the main config.",
"a3aa22086ae4976cd013065c9a3ff81c": "Cannot apply {0}: ",
"be2cf2868ba54624fe38e9908dde5e9e": "The data in {{model-config.json}} is in the unsupported {{1.x}} format.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "Discarding {{middleware}} instructions, {{loopback}} client does not support {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Middleware \"{0}\" not found: {1}"
}

17
intl/es/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "No se puede resolver la vía de acceso \"{0}\"",
"34319676975b1abf107da7a056abb434": "Formato de normalización no válido - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "La `app` está basada en una versión de loopback incompatible {0}. Versiones soportadas: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "AVISO: falta el archivo de configuración principal \"{0}{{.json}}\"",
"4d052d84c8620730afd4a30832f11724": "No se puede configurar el modelo desconocido {0}",
"4ed668e9187650d898acf97707df445a": "La {{phase}} \"{0}\" no está definida en la configuración principal.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Conflicto de orden: no se puede añadir \"{0}\" después de \"{1}\", porque ya se ha especificado el orden inverso.",
"70654dc6eb565613a33344efed3de998": "No se ha podido cargar el script de arranque: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} no se resuelve como un valor válido, se ha devuelto como {1}. \"{2}\" debe poder resolverse en la variable de entorno o por medio de {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "El {{middleware}} \"{0}\" en la {{phase}} \"{1}\" no está definido en la configuración principal.",
"a3aa22086ae4976cd013065c9a3ff81c": "No se puede aplicar {0}: ",
"be2cf2868ba54624fe38e9908dde5e9e": "Los datos de {{model-config.json}} están en un formato {{1.x}} no soportado.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "Descartando instrucciones de {{middleware}}, el cliente de {{loopback}} no da soporte a {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Middleware \"{0}\" no encontrado: {1}"
}

17
intl/fr/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Impossible de résoudre le chemin \"{0}\"",
"34319676975b1abf107da7a056abb434": "Format de normalisation non valide - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "L'application `app` est basée sur une version loopback {0} incompatible. Versions prises en charge : {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "AVERTISSEMENT : le fichier de configuration principal \"{0}{{.json}}\" est manquant",
"4d052d84c8620730afd4a30832f11724": "Impossible de configurer le modèle inconnu {0}",
"4ed668e9187650d898acf97707df445a": "{{phase}} \"{0}\" n'est pas défini dans la configuration principale.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Conflit concernant l'ordre : impossible d'ajouter \"{0}\" après \"{1}\" car l'ordre opposé à déjà été spécifié",
"70654dc6eb565613a33344efed3de998": "Echec du chargement du script d'amorçage : {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} n'est pas résolu en une valeur valide, renvoyé sous forme de {1}. \"{2}\" doit pouvoir être résolu dans la variable d'environnement ou par {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "Le {{middleware}} \"{0}\" dans {{phase}} \"{1}\" n'est pas défini dans la configuration principale.",
"a3aa22086ae4976cd013065c9a3ff81c": "Impossible d'appliquer {0} : ",
"be2cf2868ba54624fe38e9908dde5e9e": "Les données contenues dans {{model-config.json}} sont au format {{1.x}} qui n'est pas pris en charge.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "Les instructions {{middleware}} sont ignorées ; le client {{loopback}} ne prend pas en charge {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Middleware \"{0}\" introuvable : {1}"
}

17
intl/it/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Impossibile risolvere il percorso \"{0}\"",
"34319676975b1abf107da7a056abb434": "Formato di normalizzazione non valido - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "La `app` si basa su una versione loopback non compatibile {0}. Versioni supportate: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "AVVERTENZA: file di configurazione principale \"{0}{{.json}}\" mancante",
"4d052d84c8620730afd4a30832f11724": "Impossibile configurare il modello {0} sconosciuto",
"4ed668e9187650d898acf97707df445a": "{{phase}} \"{0}\" non definita nella configurazione principale.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Conflitto di ordinamento: impossibile aggiungere \"{0}\" dopo \"{1}\", perché è già stato specificato l'ordine opposto",
"70654dc6eb565613a33344efed3de998": "Caricamento dello script di boot non riuscito: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} non viene risolto in un valore valido, restituito come {1}. \"{2}\" deve essere risolto in una variabile di ambiente o da {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "{{middleware}} \"{0}\" in {{phase}} \"{1}\" non definito nella configurazione principale.",
"a3aa22086ae4976cd013065c9a3ff81c": "Impossibile applicare {0}: ",
"be2cf2868ba54624fe38e9908dde5e9e": "I dati in {{model-config.json}} sono nel formato {{1.x}} non supportato.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "Eliminazione delle istruzioni {{middleware}}, il client {{loopback}} non supporta {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Middleware \"{0}\" non trovato: {1}"
}

17
intl/ja/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "パス \"{0}\" を解決できません",
"34319676975b1abf107da7a056abb434": "無効な正規化形式 - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "「アプリケーション」は、互換性のない loopback バージョン {0} を使用しています。サポートされるバージョン: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "警告: メイン構成ファイル \"{0}{{.json}}\" が欠落しています",
"4d052d84c8620730afd4a30832f11724": "不明なモデル {0} を構成できません",
"4ed668e9187650d898acf97707df445a": "メイン構成内に {{phase}} \"{0}\" が定義されていません。",
"6447e6b342a2c51ab0bc53b3cbdf3742": "順序付けの競合: \"{0}\" を \"{1}\" の後に追加することはできません。既に逆の順序が指定されています",
"70654dc6eb565613a33344efed3de998": "ブート・スクリプトのロードに失敗しました: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} は有効な値に解決されず、{1} として返されました。 \"{2}\" は環境変数または {{app.get()}} で解決できなければなりません。",
"91a742b7c3568cf6b6755741a70b3c52": "{{phase}} \"{1}\" の {{middleware}} \"{0}\" がメイン構成内に定義されていません。",
"a3aa22086ae4976cd013065c9a3ff81c": "{0} を適用できません: ",
"be2cf2868ba54624fe38e9908dde5e9e": "{{model-config.json}} のデータが、サポートされていない {{1.x}} 形式になっています。",
"ec551b6f2fafd8d40af801ebe5bb09f6": "{{middleware}} 命令を破棄します。{{loopback}} クライアントでは {{middleware}} はサポートされません。",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "ミドルウェア \"{0}\" が見つかりません: {1}"
}

17
intl/ko/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "\"{0}\" 경로를 해석할 수 없음",
"34319676975b1abf107da7a056abb434": "올바르지 않은 정규화 형식 - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "`앱`이 호환되지 않는 루프백 버전 {0}을(를) 기반으로 합니다. 지원되는 버전: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "경고: 기본 구성 파일 \"{0}{{.json}}\"이(가) 누락됨",
"4d052d84c8620730afd4a30832f11724": "알 수 없는 모델 {0}을(를) 구성할 수 없음",
"4ed668e9187650d898acf97707df445a": "{{phase}} \"{0}\"이(가) 기본 구성에 정의되어 있지 않습니다.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "순서 지정 충돌: 반대 순서로 이미 지정되어서 \"{1}\" 뒤에 \"{0}\"을(를) 추가할 수 없음",
"70654dc6eb565613a33344efed3de998": "부트 스크립트를 로드하는 데 실패함: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0}이(가) 올바른 값으로 해석되지 않아서 {1}(으)로 리턴되었습니다. \"{2}\"은(는) 환경 변수에서 또는 {{app.get()}}에 의해 해석 가능해야 합니다. ",
"91a742b7c3568cf6b6755741a70b3c52": "{{phase}} \"{1}\"의 {{middleware}} \"{0}\"이(가) 기본 구성에 정의되어 있지 않습니다. ",
"a3aa22086ae4976cd013065c9a3ff81c": "{0}을(를) 적용할 수 없음: ",
"be2cf2868ba54624fe38e9908dde5e9e": "{{model-config.json}}의 데이터가 지원되지 않는 {{1.x}} 형식입니다. ",
"ec551b6f2fafd8d40af801ebe5bb09f6": "{{middleware}} 지시사항을 버리십시오. {{loopback}} 클라이언트가 {{middleware}}을(를) 지원하지 않습니다.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "미들웨어 \"{0}\"을(를) 찾을 수 없음: {1}"
}

17
intl/nl/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Pad \"{0}\" kan niet worden omgezet.",
"34319676975b1abf107da7a056abb434": "Ongeldige normalisatie-indeling - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "De 'app' wordt aangestuurd door een incompatibele versie van loopback, {0}. Ondersteunde versies: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "WAARSCHUWING: Hoofdconfiguratiebestand \"{0}{{.json}}\" ontbreekt.",
"4d052d84c8620730afd4a30832f11724": "Configuratie van onbekend model {0} kan niet ongedaan worden gemaakt",
"4ed668e9187650d898acf97707df445a": "De {{phase}} \"{0}\" is niet gedefinieerd in de hoofdconfiguratie.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Volgordeconflict: \"{0}\" kan niet worden toegevoegd na \"{1}\", omdat de omgekeerde volgorde al is opgegeven.",
"70654dc6eb565613a33344efed3de998": "Laden van opstartscript is mislukt: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} wordt niet omgezet in een geldige waarde; wordt geretourneerd als {1}. \"{2}\" moet omgezet kunnen worden in een omgevingsvariabele of door {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "De {{middleware}} \"{0}\" in {{phase}} \"{1}\" is niet gedefinieerd in de hoofdconfiguratie.",
"a3aa22086ae4976cd013065c9a3ff81c": "{0} kan niet worden toegepast: ",
"be2cf2868ba54624fe38e9908dde5e9e": "De gegevens in {{model-config.json}} hebben de niet ondersteunde indeling {{1.x}}.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "{{middleware}} instructies worden verwijderd, {{loopback}}-client ondersteunt geen {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Middleware \"{0}\" is niet gevonden: {1}"
}

17
intl/pl/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Nie można rozstrzygnąć ścieżki \"{0}\"",
"34319676975b1abf107da7a056abb434": "Niepoprawny format normalizacji — \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "Aplikacja jest obsługiwana przez niezgodną wersję aplikacji LoopBack {0}. Obsługiwane wersje: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "OSTRZEŻENIE: Brak głównego pliku konfiguracyjnego \"{0}{{.json}}\"",
"4d052d84c8620730afd4a30832f11724": "Nie można skonfigurować nieznanego modelu {0}",
"4ed668e9187650d898acf97707df445a": "Faza {{phase}} \"{0}\" nie została zdefiniowana w konfiguracji głównej.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Konflikt porządkowania: nie można dodać elementu \"{0}\" po elemencie \"{1}\", ponieważ została już określona odwrotna kolejność",
"70654dc6eb565613a33344efed3de998": "Nie powiodło się ładowanie skryptu startowego: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} nie umożliwia rozstrzygnięcia na poprawną wartość, zwrócono jako {1}. \"{2}\" musi umożliwiać rozstrzygnięcie w zmiennej środowiskowej lub przez metodę {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "Warstwa pośrednia {{middleware}} \"{0}\" w fazie {{phase}} \"{1}\" nie została zdefiniowana w konfiguracji głównej.",
"a3aa22086ae4976cd013065c9a3ff81c": "Nie można zastosować {0}: ",
"be2cf2868ba54624fe38e9908dde5e9e": "Dane w pliku {{model-config.json}} mają nieobsługiwany format {{1.x}}.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "Odrzucanie instrukcji warstwy pośredniej {{middleware}}, klient {{loopback}} nie obsługuje warstwy pośredniej {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Warstwa pośrednia \"{0}\" nie została znaleziona: {1}"
}

17
intl/pt/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Não é possível resolver caminho \"{0}\"",
"34319676975b1abf107da7a056abb434": "Formato de normalização inválido - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "O `app` é desenvolvido com uma versão de loopback incompatível {0}. Versões suportadas: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "AVISO: o arquivo de configuração principal \"{0}{{.json}}\" está ausente",
"4d052d84c8620730afd4a30832f11724": "Não é possível configurar modelo desconhecido {0}",
"4ed668e9187650d898acf97707df445a": "A {{phase}} \"{0}\" não foi definida na configuração principal.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Conflito de ordem: não é possível incluir \"{0}\" após \"{1}\", porque a ordem oposta já foi especificada",
"70654dc6eb565613a33344efed3de998": "Falha ao carregar script de inicialização: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} não resolve para um valor válido, retornado como {1}. \"{2}\" deve ser resolvível na variável de ambiente ou pelo {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "O {{middleware}} \"{0}\" em {{phase}} \"{1}\" não é definido na configuração principal.",
"a3aa22086ae4976cd013065c9a3ff81c": "Não é possível aplicar {0}: ",
"be2cf2868ba54624fe38e9908dde5e9e": "Os dados em {{model-config.json}} estão no formato não suportado {{1.x}}.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "Descartando instruções de {{middleware}}, cliente de {{loopback}} não suporta {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Middleware \"{0}\" não localizado: {1}"
}

17
intl/ru/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "Не удалось определить путь \"{0}\"",
"34319676975b1abf107da7a056abb434": "Недопустимый формат нормализации - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "Приложение `app` создано на основе несовместимой версии loopback {0}. Поддерживаемые версии: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "ПРЕДУПРЕЖДЕНИЕ: отсутствует главный файл конфигурации \"{0}{{.json}}\"",
"4d052d84c8620730afd4a30832f11724": "Не удается настроить неизвестную модель {0}",
"4ed668e9187650d898acf97707df445a": "Этап {{phase}} \"{0}\" не определен в главной конфигурации.",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Конфликт упорядочения: не удается добавить \"{0}\" после \"{1}\", та как уже указан другой порядок",
"70654dc6eb565613a33344efed3de998": "Не удалось загрузить сценарий загрузки: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} не удается определить в допустимое значение, возвращено как {1}. \"{2}\" должен определяться как переменная среды или с помощью {{app.get()}}.",
"91a742b7c3568cf6b6755741a70b3c52": "{{middleware}} \"{0}\" на этапе {{phase}} \"{1}\"не определено в главной конфигурации.",
"a3aa22086ae4976cd013065c9a3ff81c": "Не удается применить {0}: ",
"be2cf2868ba54624fe38e9908dde5e9e": "Данные в {{model-config.json}} указаны в неподдерживаемом формате {{1.x}}.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "Инструкции {{middleware}} отменяются, клиент {{loopback}} не поддерживает {{middleware}}.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Не найдено промежуточное ПО \"{0}\": {1}"
}

17
intl/tr/messages.json Normal file
View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "\"{0}\" yolu çözülemiyor",
"34319676975b1abf107da7a056abb434": "Geçersiz normalleştirme biçimi - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "`app` uyumsuz olan geri döngü {0} sürümüyle güçlendirilmiş. Desteklenen sürümler: {1}",
"3f93b626dd9a1c33d67490f6e71018b5": "UYARI: Ana yapılandırma dosyası \"{0}{{.json}}\" eksik",
"4d052d84c8620730afd4a30832f11724": "Bilinmeyen {0} modeli yapılandırılamıyor",
"4ed668e9187650d898acf97707df445a": "{{phase}} \"{0}\", ana yapılandırmada tanımlı değil",
"6447e6b342a2c51ab0bc53b3cbdf3742": "Sıralama çakışması: \"{0}\", \"{1}\" sonrasına eklenemez; karşıt sıra belirtilmiş",
"70654dc6eb565613a33344efed3de998": "Önyükleme komut dosyasının yüklenmesi başarısız oldu: {0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} geçerli bir değere çözülmüyor, {1} olarak döndürüldü. \"{2}\" ortam değişkeninde ya da {{app.get()}} ile çözülebilir olmalıdır.",
"91a742b7c3568cf6b6755741a70b3c52": "{{phase}} \"{1}\" aşamasındaki {{middleware}} \"{0}\" ana yapılandırmada tanımlı değil.",
"a3aa22086ae4976cd013065c9a3ff81c": "{0} uygulanamıyor: ",
"be2cf2868ba54624fe38e9908dde5e9e": "{{model-config.json}} içindeki verileri desteklenmeyen {{1.x}} biçiminde.",
"ec551b6f2fafd8d40af801ebe5bb09f6": "{{middleware}} yönergeleri atılıyor, {{middleware}}, {{loopback}} istemcisi tarafından desteklenmiyor.",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "Ara katman \"{0}\" bulunamadı: {1}"
}

View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "无法解析路径“{0}”",
"34319676975b1abf107da7a056abb434": "标准化格式无效 -“{0}”",
"3a7049e42006e8bc19e0f4fc8df63b6b": "应用程序由不兼容的回环版本 {0} 支持。受支持的版本:{1}",
"3f93b626dd9a1c33d67490f6e71018b5": "警告:缺失主要配置文件“{0}{{.json}}”",
"4d052d84c8620730afd4a30832f11724": "无法配置未知的模型 {0}",
"4ed668e9187650d898acf97707df445a": "未在主配置中定义 {{phase}}“{0}”。",
"6447e6b342a2c51ab0bc53b3cbdf3742": "顺序冲突:不能在“{1}”之后添加“{0}”,因为已指定相反顺序",
"70654dc6eb565613a33344efed3de998": "无法装入引导脚本:{0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} 未解析为有效值,返回为 {1}。“{2}”必须可在环境变量中解析或由 {{app.get()}} 解析。",
"91a742b7c3568cf6b6755741a70b3c52": "未在主配置中定义 {{phase}}“{1}”中的 {{middleware}}“{0}”。",
"a3aa22086ae4976cd013065c9a3ff81c": "无法应用 {0}",
"be2cf2868ba54624fe38e9908dde5e9e": "{{model-config.json}} 中的数据不是受支持的 {{1.x}} 格式。",
"ec551b6f2fafd8d40af801ebe5bb09f6": "正在丢弃 {{middleware}} 指示信息,{{loopback}} 客户机不支持 {{middleware}}。",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "找不到中间件“{0}”:{1}"
}

View File

@ -0,0 +1,17 @@
{
"1e5fea50eef843cbffd1d438494912c8": "無法解析路徑 \"{0}\"",
"34319676975b1abf107da7a056abb434": "無效的正規化格式 - \"{0}\"",
"3a7049e42006e8bc19e0f4fc8df63b6b": "'app' 採用不相容的 LoopBack 版本 {0}。支援的版本:{1}",
"3f93b626dd9a1c33d67490f6e71018b5": "警告:遺漏主要配置檔 \"{0}{{.json}}\"",
"4d052d84c8620730afd4a30832f11724": "無法配置不明模型 {0}",
"4ed668e9187650d898acf97707df445a": "主要配置中未定義 {{phase}} \"{0}\"。",
"6447e6b342a2c51ab0bc53b3cbdf3742": "排序衝突:不能將 \"{0}\" 新增至 \"{1}\" 後面,因為已指定相反順序",
"70654dc6eb565613a33344efed3de998": "載入啟動 Script 時失敗:{0}\n{1}",
"7f7bdcadb75abfef1bd8a126d547dd6d": "{0} 未解析成有效的值,傳回 {1}。\"{2}\" 必須可在環境變數中解析或由 {{app.get()}} 解析。",
"91a742b7c3568cf6b6755741a70b3c52": "主要配置中未定義 {{phase}} \"{1}\" 中的 {{middleware}} \"{0}\"。",
"a3aa22086ae4976cd013065c9a3ff81c": "無法套用 {0}",
"be2cf2868ba54624fe38e9908dde5e9e": "{{model-config.json}} 中的資料採用不受支援的 {{1.x}} 格式。",
"ec551b6f2fafd8d40af801ebe5bb09f6": "正在捨棄 {{middleware}} 指令,{{loopback}} 用戶端不支援 {{middleware}}。",
"fdc23df1bd0fe55fe3faabcc89ff60f3": "找不到中介軟體 \"{0}\"{1}"
}

222
lib/bootstrapper.js vendored Normal file
View File

@ -0,0 +1,222 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const _ = require('lodash');
const assert = require('assert');
const async = require('async');
const utils = require('./utils');
const path = require('path');
const pluginLoader = require('./plugin-loader');
const debug = require('debug')('loopback:boot:bootstrapper');
const Promise = require('bluebird');
const arrayToObject = require('./utils').arrayToObject;
module.exports = Bootstrapper;
function createPromiseCallback() {
let cb;
const promise = new Promise(function(resolve, reject) {
cb = function(err, data) {
if (err) return reject(err);
return resolve(data);
};
});
cb.promise = promise;
return cb;
}
const builtinPlugins = [
'application', 'datasource', 'model', 'mixin',
'middleware', 'component', 'boot-script', 'swagger',
];
const builtinPhases = [
'load', 'compile', 'starting', 'start', 'started',
];
function loadAndRegisterPlugins(bootstrapper, options) {
const loader = pluginLoader(options);
const loaderContext = {};
loader.load(loaderContext);
loader.compile(loaderContext);
for (const i in loaderContext.instructions.pluginScripts) {
bootstrapper.use('/boot/' + i, loaderContext.instructions.pluginScripts[i]);
}
}
/**
* Create a new Bootstrapper with options
* @param options
* @constructor
*/
function Bootstrapper(options) {
this.plugins = [];
options = options || {};
if (typeof options === 'string') {
options = {appRootDir: options};
}
// For setting properties without modifying the original object
options = Object.create(options);
const appRootDir = options.appRootDir = options.appRootDir || process.cwd();
const env = options.env || process.env.NODE_ENV || 'development';
const scriptExtensions = options.scriptExtensions ?
arrayToObject(options.scriptExtensions) :
require.extensions;
const appConfigRootDir = options.appConfigRootDir || appRootDir;
options.rootDir = appConfigRootDir;
options.env = env;
options.scriptExtensions = scriptExtensions;
this.options = options;
this.phases = options.phases || builtinPhases;
this.builtinPlugins = options.plugins || builtinPlugins;
assert(Array.isArray(this.phases), 'Invalid phases: ' + this.phases);
assert(Array.isArray(this.plugins), 'Invalid plugins: ' +
this.builtinPlugins);
const self = this;
self.builtinPlugins.forEach(function(p) {
const factory = require('./plugins/' + p);
self.use('/boot/' + p, factory(options));
});
try {
loadAndRegisterPlugins(self, options);
} catch (err) {
debug('Cannot load & register plugins: %s', err.stack || err);
}
}
/**
* Register a handler to a given path
* @param {String} path
* @param {Function} handler
*/
Bootstrapper.prototype.use = function(path, handler) {
const plugin = {
path: path,
handler: handler,
};
this.plugins.push(plugin);
};
/**
* Get a list of plugins for the given path
* @param {String} path
* @returns {*}
*/
Bootstrapper.prototype.getPlugins = function(path) {
if (path[path.length - 1] !== '/') {
path = path + '/';
}
return this.plugins.filter(function(p) {
return p.path.indexOf(path) === 0;
});
};
/**
* Get a list of extensions for the given path
* @param {String} path
* @returns {*}
*/
Bootstrapper.prototype.getExtensions = function(path) {
if (path[path.length - 1] !== '/') {
path = path + '/';
}
return this.plugins.filter(function(p) {
if (p.path.indexOf(path) === -1) return false;
const name = p.path.substring(path.length);
return name && name.indexOf('/') === -1;
});
};
/**
* Add more phases. The order of phases is decided by the sequence of phase
* names
* @param {String[]} phases An array of phase names
* @returns {String[]} New list of phases
*/
Bootstrapper.prototype.addPhases = function(phases) {
this.phases = utils.mergePhaseNameLists(this.phases, phases || []);
return this.phases;
};
function pluginIteratorFactory(context, phase) {
return function executePluginPhase(plugin, done) {
let result;
if (typeof plugin.handler[phase] !== 'function') {
debug('Skipping %s.%s', plugin.handler.name, phase);
return done();
}
debug('Invoking %s.%s', plugin.handler.name, phase);
try {
if (plugin.handler[phase].length === 2) {
plugin.handler[phase](context, done);
} else {
result = plugin.handler[phase](context);
Promise.resolve(result)
.then(function onPluginPhaseResolved(value) {
done(null, value);
}, function onPluginPhaseRejected(err) {
debug('Unable to invoke %s.%s()', plugin.name, phase, err);
done(err);
});
}
} catch (err) {
debug('Unable to invoke %s.%s()', plugin.name, phase, err);
done(err);
}
};
}
/**
* Invoke the plugins phase by phase with the given context
* @param {Object} context Context object
* @param {Function} done Callback function. If not provided, a promise will be
* returned
* @returns {*}
*/
Bootstrapper.prototype.run = function(context, done) {
if (!done) {
done = createPromiseCallback();
}
const options = this.options;
const appRootDir = options.appRootDir = options.appRootDir || process.cwd();
const env = options.env || process.env.NODE_ENV || 'development';
const appConfigRootDir = options.appConfigRootDir || appRootDir;
options.rootDir = appConfigRootDir;
options.env = env;
context = context || {};
const phases = context.phases || this.phases;
if (phases.includes('starting') || phases.includes('start')) {
context.app.booting = true;
}
const bootPlugins = this.getExtensions('/boot');
async.eachSeries(phases, function(phase, done) {
debug('Phase %s', phase);
async.eachSeries(bootPlugins, pluginIteratorFactory(context, phase), done);
}, function(err) {
if (phases.includes('started')) {
context.app.booting = false;
}
return done(err, context);
});
return done.promise;
};

View File

@ -1,45 +1,116 @@
var fs = require('fs');
var path = require('path');
var commondir = require('commondir');
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const fs = require('fs');
const path = require('path');
const commondir = require('commondir');
const cloneDeep = require('lodash').cloneDeep;
const g = require('./globalize');
/**
* Add boot instructions to a browserify bundler.
* @param {Object} instructions Boot instructions.
* @param {Object} bundler A browserify object created by `browserify()`.
*/
module.exports = function addInstructionsToBrowserify(instructions, bundler) {
bundleScripts(instructions.files, bundler);
bundleInstructions(instructions, bundler);
module.exports = function addInstructionsToBrowserify(context, bundler) {
addPlugins(bundler);
// bundlePluginScripts(context, bundler);
bundleModelScripts(context, bundler);
bundleMixinScripts(context, bundler);
bundleComponentScripts(context, bundler);
bundleOtherScripts(context, bundler);
bundleInstructions(context, bundler);
};
function bundleScripts(files, bundler) {
for (var key in files) {
var list = files[key];
if (!list.length) continue;
function addPlugins(bundler) {
const dir = path.join(__dirname, './plugins');
const files = fs.readdirSync(dir);
files.forEach(function(f) {
bundler.require(path.join(dir, f),
{expose: './plugins/' + path.basename(f, '.js')});
});
}
var root = commondir(files[key].map(path.dirname));
function bundleOtherScripts(context, bundler) {
const list = context.instructions.bootScripts;
addScriptsToBundle('boot', list, bundler);
}
for (var ix in list) {
var filepath = list[ix];
function bundlePluginScripts(context, bundler) {
const list = context.instructions.pluginScripts;
addScriptsToBundle('plugins', list, bundler);
}
function bundleModelScripts(context, bundler) {
bundleSourceFiles(context, 'models', bundler);
}
function bundleMixinScripts(context, bundler) {
bundleSourceFiles(context, 'mixins', bundler);
}
function bundleComponentScripts(context, bundler) {
bundleSourceFiles(context, 'components', bundler);
}
function bundleSourceFiles(context, type, bundler) {
const files = context.instructions[type]
.map(function(m) { return m.sourceFile; })
.filter(function(f) { return !!f; });
const instructionToFileMapping = context.instructions[type]
.map(function(m) { return files.indexOf(m.sourceFile); });
addScriptsToBundle(type, files, bundler);
// Update `sourceFile` properties with the new paths
instructionToFileMapping.forEach(function(fileIx, sourceIx) {
if (fileIx === -1) return;
context.instructions[type][sourceIx].sourceFile = files[fileIx];
});
}
function addScriptsToBundle(name, list, bundler) {
if (!list.length) return;
const root = commondir(list.map(path.dirname));
for (const ix in list) {
const filepath = list[ix];
// Build a short unique id that does not expose too much
// information about the file system, but still preserves
// useful information about where is the file coming from.
var fileid = 'loopback-boot#' + key + '#' + path.relative(root, filepath);
const fileid =
'loopback-boot#' + name + '#' + path.relative(root, filepath);
// Add the file to the bundle.
bundler.require(filepath, {expose: fileid});
// Rewrite the instructions entry with the new id that will be
// Rewrite the context entry with the new id that will be
// used to load the file via `require(fileid)`.
list[ix] = fileid;
}
}
}
function bundleInstructions(instructions, bundler) {
var instructionsString = JSON.stringify(instructions, null, 2);
function bundleInstructions(context, bundler) {
const instructions = cloneDeep(context.instructions);
const hasMiddleware = instructions.middleware.phases.length ||
instructions.middleware.middleware.length;
if (hasMiddleware) {
g.warn(
'Discarding {{middleware}} instructions,' +
' {{loopback}} client does not support {{middleware}}.',
);
}
delete instructions.middleware;
const instructionsString = JSON.stringify(instructions, null, 2);
/* The following code does not work due to a bug in browserify
* https://github.com/substack/node-browserify/issues/771
@ -49,11 +120,19 @@ function bundleInstructions(instructions, bundler) {
b.require(instructionsStream, { expose: 'loopback-boot#instructions' });
*/
let instructionId = 'instructions';
// Create an unique instruction identifier using the application ID.
// This is only useful when multiple loopback applications are being bundled
// together.
if (instructions.appId)
instructionId += '-' + instructions.appId;
// Write the instructions to a file in our node_modules folder.
// The location should not really matter as long as it is .gitignore-ed
var instructionsFile = path.resolve(__dirname,
'..', 'node_modules', 'instructions.json');
const instructionsFile = path.resolve(__dirname,
'..', 'generated-' + instructionId + '.json');
fs.writeFileSync(instructionsFile, instructionsString, 'utf-8');
bundler.require(instructionsFile, { expose: 'loopback-boot#instructions' });
const moduleName = 'loopback-boot#' + instructionId;
bundler.require(instructionsFile, {expose: moduleName});
}

View File

@ -1,126 +0,0 @@
var assert = require('assert');
var fs = require('fs');
var path = require('path');
var ConfigLoader = require('./config-loader');
var debug = require('debug')('loopback:boot:compiler');
/**
* Gather all bootstrap-related configuration data and compile it into
* a single object containing instruction for `boot.execute`.
*
* @options {String|Object} options Boot options; If String, this is
* the application root directory; if object, has the properties
* described in `bootLoopBackApp` options above.
* @return {Object}
*
* @header boot.compile(options)
*/
module.exports = function compile(options) {
options = options || {};
if(typeof options === 'string') {
options = { appRootDir: options };
}
var appRootDir = options.appRootDir = options.appRootDir || process.cwd();
var env = options.env || process.env.NODE_ENV || 'development';
var appConfig = options.app || ConfigLoader.loadAppConfig(appRootDir, env);
assertIsValidConfig('app', appConfig);
var modelsRootDir = options.modelsRootDir || appRootDir;
var modelsConfig = options.models ||
ConfigLoader.loadModels(modelsRootDir, env);
assertIsValidConfig('model', modelsConfig);
var dsRootDir = options.dsRootDir || appRootDir;
var dataSourcesConfig = options.dataSources ||
ConfigLoader.loadDataSources(dsRootDir, env);
assertIsValidConfig('data source', dataSourcesConfig);
// require directories
var modelsScripts = findScripts(path.join(modelsRootDir, 'models'));
var bootScripts = findScripts(path.join(appRootDir, 'boot'));
return {
app: appConfig,
dataSources: dataSourcesConfig,
models: modelsConfig,
files: {
models: modelsScripts,
boot: bootScripts
}
};
};
function assertIsValidConfig(name, config) {
if(config) {
assert(typeof config === 'object',
name + ' config must be a valid JSON object');
}
}
/**
* Find all javascript files (except for those prefixed with _)
* and all directories.
* @param {String} dir Full path of the directory to enumerate.
* @return {Array.<String>} A list of absolute paths to pass to `require()`.
* @private
*/
function findScripts(dir) {
assert(dir, 'cannot require directory contents without directory name');
var files = tryReadDir(dir);
// sort files in lowercase alpha for linux
files.sort(function(a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) {
return -1;
} else if (b < a) {
return 1;
} else {
return 0;
}
});
var results = [];
files.forEach(function(filename) {
// ignore index.js and files prefixed with underscore
if ((filename === 'index.js') || (filename[0] === '_')) {
return;
}
var filepath = path.resolve(path.join(dir, filename));
var ext = path.extname(filename);
var stats = fs.statSync(filepath);
// only require files supported by require.extensions (.txt .md etc.)
if (stats.isFile()) {
if (ext in require.extensions)
results.push(filepath);
else
debug('Skipping file %s - unknown extension', filepath);
} else {
try {
path.join(require.resolve(filepath));
} catch(err) {
debug('Skipping directory %s - %s', filepath, err.code || err);
}
}
});
return results;
}
function tryReadDir() {
try {
return fs.readdirSync.apply(fs, arguments);
} catch(e) {
return [];
}
}

View File

@ -1,154 +0,0 @@
var fs = require('fs');
var path = require('path');
var ConfigLoader = exports;
/**
* Load application config from `app.json` and friends.
* @param {String} rootDir Directory where to look for files.
* @param {String} env Environment, usually `process.env.NODE_ENV`
* @returns {Object}
*/
ConfigLoader.loadAppConfig = function(rootDir, env) {
return loadNamed(rootDir, env, 'app', mergeAppConfig);
};
/**
* Load data-sources config from `datasources.json` and friends.
* @param {String} rootDir Directory where to look for files.
* @param {String} env Environment, usually `process.env.NODE_ENV`
* @returns {Object}
*/
ConfigLoader.loadDataSources = function(rootDir, env) {
return loadNamed(rootDir, env, 'datasources', mergeDataSourceConfig);
};
/**
* Load models config from `models.json` and friends.
* @param {String} rootDir Directory where to look for files.
* @param {String} env Environment, usually `process.env.NODE_ENV`
* @returns {Object}
*/
ConfigLoader.loadModels = function(rootDir, env) {
/*jshint unused:false */
return tryReadJsonConfig(rootDir, 'models') || {};
};
/*-- Implementation --*/
/**
* Load named configuration.
* @param {String} rootDir Directory where to look for files.
* @param {String} env Environment, usually `process.env.NODE_ENV`
* @param {String} name
* @param {function(target:Object, config:Object, filename:String)} mergeFn
* @returns {Object}
*/
function loadNamed(rootDir, env, name, mergeFn) {
var files = findConfigFiles(rootDir, env, name);
var configs = loadConfigFiles(files);
return mergeConfigurations(configs, mergeFn);
}
/**
* Search `appRootDir` for all files containing configuration for `name`.
* @param {String} appRootDir
* @param {String} env Environment, usually `process.env.NODE_ENV`
* @param {String} name
* @returns {Array.<String>} Array of absolute file paths.
*/
function findConfigFiles(appRootDir, env, name) {
var master = ifExists(name + '.json');
if (!master) return [];
var candidates = [
master,
ifExistsWithAnyExt(name + '.local'),
ifExistsWithAnyExt(name + '.' + env)
];
return candidates.filter(function(c) { return c !== undefined; });
function ifExists(fileName) {
var filepath = path.resolve(appRootDir, fileName);
return fs.existsSync(filepath) ? filepath : undefined;
}
function ifExistsWithAnyExt(fileName) {
return ifExists(fileName + '.js') || ifExists(fileName + '.json');
}
}
/**
* Load configuration files into an array of objects.
* Attach non-enumerable `_filename` property to each object.
* @param {Array.<String>} files
* @returns {Array.<Object>}
*/
function loadConfigFiles(files) {
return files.map(function(f) {
var config = require(f);
Object.defineProperty(config, '_filename', {
enumerable: false,
value: f
});
return config;
});
}
/**
* Merge multiple configuration objects into a single one.
* @param {Array.<Object>} configObjects
* @param {function(target:Object, config:Object, filename:String)} mergeFn
*/
function mergeConfigurations(configObjects, mergeFn) {
var result = configObjects.shift() || {};
while(configObjects.length) {
var next = configObjects.shift();
mergeFn(result, next, next._filename);
}
return result;
}
function mergeDataSourceConfig(target, config, fileName) {
for (var ds in target) {
var err = applyCustomConfig(target[ds], config[ds]);
if (err) {
throw new Error('Cannot apply ' + fileName + ' to `' + ds + '`: ' + err);
}
}
}
function mergeAppConfig(target, config, fileName) {
var err = applyCustomConfig(target, config);
if (err) {
throw new Error('Cannot apply ' + fileName + ': ' + err);
}
}
function applyCustomConfig(target, config) {
for (var key in config) {
var value = config[key];
if (typeof value === 'object') {
return 'override for the option `' + key + '` is not a value type.';
}
target[key] = value;
}
return null; // no error
}
/**
* Try to read a config file with .json extension
* @param cwd Dirname of the file
* @param fileName Name of the file without extension
* @returns {Object|undefined} Content of the file, undefined if not found.
*/
function tryReadJsonConfig(cwd, fileName) {
try {
return require(path.join(cwd, fileName + '.json'));
} catch(e) {
if(e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
}
}

View File

@ -1,199 +0,0 @@
var assert = require('assert');
var _ = require('underscore');
var semver = require('semver');
var debug = require('debug')('loopback:boot:executor');
/**
* Execute bootstrap instructions gathered by `boot.compile`.
*
* @options {Object} app The loopback app to boot.
* @options {Object} instructions Boot instructions.
*
* @header boot.execute(instructions)
*/
module.exports = function execute(app, instructions) {
patchAppLoopback(app);
assertLoopBackVersion(app);
setHost(app, instructions);
setPort(app, instructions);
setApiRoot(app, instructions);
applyAppConfig(app, instructions);
setupDataSources(app, instructions);
setupModels(app, instructions);
autoAttach(app);
runBootScripts(app, instructions);
enableAnonymousSwagger(app, instructions);
};
function patchAppLoopback(app) {
if (app.loopback) return;
// app.loopback was introduced in 1.9.0
// patch the app object to make loopback-boot work with older versions too
try {
app.loopback = require('loopback');
} catch(err) {
if (err.code === 'MODULE_NOT_FOUND') {
console.error(
'When using loopback-boot with loopback <1.9, '+
'the loopback module must be available for `require(\'loopback\')`.');
}
throw err;
}
}
function assertLoopBackVersion(app) {
var RANGE = '1.x || 2.x';
var loopback = app.loopback;
if (!semver.satisfies(loopback.version || '1.0.0', RANGE)) {
throw new Error(
'The `app` is powered by an incompatible loopback version %s. ' +
'Supported versions: %s',
loopback.version || '(unknown)',
RANGE);
}
}
function setHost(app, instructions) {
//jshint camelcase:false
var host =
process.env.npm_config_host ||
process.env.OPENSHIFT_SLS_IP ||
process.env.OPENSHIFT_NODEJS_IP ||
process.env.HOST ||
instructions.app.host ||
process.env.npm_package_config_host ||
app.get('host');
if(host !== undefined) {
assert(typeof host === 'string', 'app.host must be a string');
app.set('host', host);
}
}
function setPort(app, instructions) {
//jshint camelcase:false
var port = _.find([
process.env.npm_config_port,
process.env.OPENSHIFT_SLS_PORT,
process.env.OPENSHIFT_NODEJS_PORT,
process.env.PORT,
instructions.app.port,
process.env.npm_package_config_port,
app.get('port'),
3000
], _.isFinite);
if(port !== undefined) {
var portType = typeof port;
assert(portType === 'string' || portType === 'number',
'app.port must be a string or number');
app.set('port', port);
}
}
function setApiRoot(app, instructions) {
var restApiRoot =
instructions.app.restApiRoot ||
app.get('restApiRoot') ||
'/api';
assert(restApiRoot !== undefined, 'app.restBasePath is required');
assert(typeof restApiRoot === 'string',
'app.restApiRoot must be a string');
assert(/^\//.test(restApiRoot),
'app.restApiRoot must start with "/"');
app.set('restApiRoot', restApiRoot);
}
function applyAppConfig(app, instructions) {
var appConfig = instructions.app;
for(var configKey in appConfig) {
var cur = app.get(configKey);
if(cur === undefined || cur === null) {
app.set(configKey, appConfig[configKey]);
}
}
}
function setupDataSources(app, instructions) {
forEachKeyedObject(instructions.dataSources, function(key, obj) {
app.dataSource(key, obj);
});
}
function setupModels(app, instructions) {
forEachKeyedObject(instructions.models, function(key, obj) {
app.model(key, obj);
});
runScripts(app, instructions.files.models);
}
function forEachKeyedObject(obj, fn) {
if(typeof obj !== 'object') return;
Object.keys(obj).forEach(function(key) {
fn(key, obj[key]);
});
}
function runScripts(app, list) {
if (!list || !list.length) return;
list.forEach(function(filepath) {
var exports = tryRequire(filepath);
if (isFunctionNotModelCtor(exports, app.loopback.Model))
exports(app);
});
}
function isFunctionNotModelCtor(fn, Model) {
return typeof fn === 'function' &&
!(fn.prototype instanceof Model);
}
function tryRequire(modulePath) {
try {
return require.apply(this, arguments);
} catch(e) {
if(e.code === 'MODULE_NOT_FOUND') {
debug('Warning: cannot require %s - module not found.', modulePath);
return undefined;
}
console.error('failed to require "%s"', modulePath);
throw e;
}
}
// Deprecated, will be removed soon
function autoAttach(app) {
try {
app.loopback.autoAttach();
} catch(e) {
if(e.name === 'AssertionError') {
console.warn(e);
} else {
throw e;
}
}
}
function runBootScripts(app, instructions) {
runScripts(app, instructions.files.boot);
}
function enableAnonymousSwagger(app, instructions) {
// disable token requirement for swagger, if available
var swagger = app.remotes().exports.swagger;
if (!swagger) return;
var appConfig = instructions.app;
var requireTokenForSwagger = appConfig.swagger &&
appConfig.swagger.requireToken;
swagger.requireToken = requireTokenForSwagger || false;
}

11
lib/globalize.js Normal file
View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const path = require('path');
const SG = require('strong-globalize');
SG.SetRootDir(path.join(__dirname, '..'), {autonomousMsgLoading: 'all'});
module.exports = SG();

338
lib/plugin-base.js Normal file
View File

@ -0,0 +1,338 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const fs = require('fs');
const path = require('path');
const debug = require('debug')('loopback:boot:plugin');
const assert = require('assert');
const _ = require('lodash');
const util = require('./utils');
const g = require('./globalize');
module.exports = PluginBase;
function PluginBase(options, name, artifact) {
this.options = options || {};
this.name = name || options.name;
this.artifact = artifact || options.artifact;
}
PluginBase.prototype.getRootDir = function() {
return this.options.rootDir;
};
PluginBase.prototype.load = function(context) {
const rootDir = this.getRootDir() || this.options.rootDir;
const env = this.options.env;
assert(this.name, 'Plugin name must to be set');
debug('Root dir: %s, env: %s, artifact: %s', rootDir, env, this.artifact);
let config = {};
if (this.options[this.name]) {
// First check if options have the corresponding config object
debug('Artifact: %s is using provided config obj instead' +
' of config file');
config = this.options[this.name];
} else {
if (this.artifact) {
config = this.loadNamed(rootDir, env, this.artifact);
}
}
// Register as context.configurations.<plugin-name>
return this.configure(context, config);
};
PluginBase.prototype.configure = function(context, config) {
config = config || {};
// Register as context.configurations.<plugin-name>
if (!context.configurations) {
context.configurations = {};
}
context.configurations[this.name] = config;
return config;
};
PluginBase.prototype.merge = function(target, config, keyPrefix) {
return this._mergeObjects(target, config, keyPrefix);
};
/**
* Load named configuration.
* @param {String} rootDir Directory where to look for files.
* @param {String} env Environment, usually `process.env.NODE_ENV`
* @param {String} name
* @returns {Object}
*/
PluginBase.prototype.loadNamed = function(rootDir, env, name) {
const files = this.findConfigFiles(rootDir, env, name);
debug('Looking in dir %s for %s configs', rootDir, this.name);
if (files.length) {
debug('found %s %s files: %j', env, name, files);
files.forEach(function(f) {
debug(' %s', f);
});
}
const configs = this._loadConfigFiles(files);
const merged = this._mergeConfigurations(configs);
debug('merged %s %s configuration %j', env, name, merged);
return merged;
};
/**
* Search `rootDir` for all files containing configuration for `name`.
* @param {String} rootDir Root directory
* @param {String} env Environment, usually `process.env.NODE_ENV`
* @param {String} name Name
* @param {Array.<String>} exts An array of extension names
* @returns {Array.<String>} Array of absolute file paths.
*/
PluginBase.prototype.findConfigFiles = function(rootDir, env, name, exts) {
const master = ifExists(name + '.json');
if (!master && (ifExistsWithAnyExt(name + '.local') ||
ifExistsWithAnyExt(name + '.' + env))) {
g.warn('WARNING: Main config file "%s{{.json}}" is missing', name);
}
if (!master) return [];
const candidates = [
master,
ifExistsWithAnyExt(name + '.local'),
ifExistsWithAnyExt(name + '.' + env),
];
return candidates.filter(function(c) {
return c !== undefined;
});
function ifExists(fileName) {
const filePath = path.resolve(rootDir, fileName);
return util.fileExistsSync(filePath) ? filePath : undefined;
}
function ifExistsWithAnyExt(fileName) {
const extensions = exts || ['js', 'json'];
let file;
for (let i = 0, n = extensions.length; i < n; i++) {
file = ifExists(fileName + '.' + extensions[i]);
if (file) {
return file;
}
}
}
};
/**
* Load configuration files into an array of objects.
* Attach non-enumerable `_filename` property to each object.
* @param {Array.<String>} files
* @returns {Array.<Object>}
*/
PluginBase.prototype._loadConfigFiles = function(files) {
return files.map(function(f) {
let config = require(f);
config = _.cloneDeep(config);
Object.defineProperty(config, '_filename', {
enumerable: false,
value: f,
});
return config;
});
};
/**
* Merge multiple configuration objects into a single one.
* @param {Array.<Object>} configObjects
*/
PluginBase.prototype._mergeConfigurations = function(configObjects) {
const result = configObjects.shift() || {};
while (configObjects.length) {
const next = configObjects.shift();
this.merge(result, next, next._filename);
}
return result;
};
PluginBase.prototype._mergeObjects = function(target, config, keyPrefix) {
for (const key in config) {
const fullKey = keyPrefix ? keyPrefix + '.' + key : key;
const err = this._mergeSingleItemOrProperty(target, config, key, fullKey);
if (err) throw err;
}
return null; // no error
};
PluginBase.prototype._mergeNamedItems = function(arr1, arr2, key) {
assert(Array.isArray(arr1), 'invalid array: ' + arr1);
assert(Array.isArray(arr2), 'invalid array: ' + arr2);
key = key || 'name';
const result = [].concat(arr1);
for (let i = 0, n = arr2.length; i < n; i++) {
const item = arr2[i];
let found = false;
if (item[key]) {
for (let j = 0, k = result.length; j < k; j++) {
if (result[j][key] === item[key]) {
this._mergeObjects(result[j], item);
found = true;
break;
}
}
}
if (!found) {
result.push(item);
}
}
return result;
};
PluginBase.prototype._mergeSingleItemOrProperty =
function(target, config, key, fullKey) {
const origValue = target[key];
const newValue = config[key];
if (!hasCompatibleType(origValue, newValue)) {
return 'Cannot merge values of incompatible types for the option `' +
fullKey + '`.';
}
if (Array.isArray(origValue)) {
return this._mergeArrays(origValue, newValue, fullKey);
}
if (newValue !== null && typeof origValue === 'object') {
return this._mergeObjects(origValue, newValue, fullKey);
}
target[key] = newValue;
return null; // no error
};
PluginBase.prototype._mergeArrays = function(target, config, keyPrefix) {
if (target.length !== config.length) {
return 'Cannot merge array values of different length' +
' for the option `' + keyPrefix + '`.';
}
// Use for(;;) to iterate over undefined items, for(in) would skip them.
for (let ix = 0; ix < target.length; ix++) {
const fullKey = keyPrefix + '[' + ix + ']';
const err = this._mergeSingleItemOrProperty(target, config, ix, fullKey);
if (err) return err;
}
return null; // no error
};
function hasCompatibleType(origValue, newValue) {
if (origValue === null || origValue === undefined)
return true;
if (Array.isArray(origValue))
return Array.isArray(newValue);
if (typeof origValue === 'object')
return typeof newValue === 'object';
// Note: typeof Array() is 'object' too,
// we don't need to explicitly check array types
return typeof newValue !== 'object';
}
PluginBase.prototype.compile = function(context) {
let instructions;
if (typeof this.buildInstructions === 'function') {
const rootDir = this.options.rootDir;
const config = context.configurations[this.name] || {};
instructions = this.buildInstructions(context, rootDir, config);
} else {
instructions = context.configurations[this.name];
}
// Register as context.instructions.<plugin-name>
if (!context.instructions) {
context.instructions = {};
if (this.options.appId) {
context.instructions.appId = this.options.appId;
}
}
context.instructions[this.name] = instructions;
return undefined;
};
const DYNAMIC_CONFIG_PARAM = /\$\{(\w+)\}$/;
function getConfigVariable(app, param, useEnvVars) {
let configVariable = param;
const match = configVariable.match(DYNAMIC_CONFIG_PARAM);
if (match) {
const varName = match[1];
if (useEnvVars && process.env[varName] !== undefined) {
debug('Dynamic Configuration: Resolved via process.env: %s as %s',
process.env[varName], param);
configVariable = process.env[varName];
} else if (app.get(varName) !== undefined) {
debug('Dynamic Configuration: Resolved via app.get(): %s as %s',
app.get(varName), param);
const appValue = app.get(varName);
configVariable = appValue;
} else {
// previously it returns the original string such as "${restApiRoot}"
// it will now return `undefined`, for the use case of
// dynamic datasources url:`undefined` to fallback to other parameters
configVariable = undefined;
g.warn('%s does not resolve to a valid value, returned as %s. ' +
'"%s" must be resolvable in Environment variable or by {{app.get()}}.',
param, configVariable, varName);
debug('Dynamic Configuration: Cannot resolve variable for `%s`, ' +
'returned as %s', varName, configVariable);
}
}
return configVariable;
}
PluginBase.prototype.getUpdatedConfigObject = function(context, config, opts) {
const app = context.app;
const useEnvVars = opts && opts.useEnvVars;
function interpolateVariables(config) {
// config is a string and contains a config variable ('${var}')
if (typeof config === 'string')
return getConfigVariable(app, config, useEnvVars);
// anything but an array or object
if (typeof config !== 'object' || config == null)
return config;
// recurse into array elements
if (Array.isArray(config))
return config.map(interpolateVariables);
// Not a plain object. Examples: RegExp, Date,
if (!config.constructor || config.constructor !== Object)
return config;
// recurse into object props
const interpolated = {};
Object.keys(config).forEach(function(configKey) {
const value = config[configKey];
if (Array.isArray(value)) {
interpolated[configKey] = value.map(interpolateVariables);
} else if (typeof value === 'string') {
interpolated[configKey] = getConfigVariable(app, value, useEnvVars);
} else if (value === null) {
interpolated[configKey] = value;
} else if (typeof value === 'object' && Object.keys(value).length) {
interpolated[configKey] = interpolateVariables(value);
} else {
interpolated[configKey] = value;
}
});
return interpolated;
}
return interpolateVariables(config);
};

66
lib/plugin-loader.js Normal file
View File

@ -0,0 +1,66 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const util = require('util');
const utils = require('./utils');
const path = require('path');
const async = require('async');
const debug = require('debug')('loopback:boot:plugin-loader');
const PluginBase = require('./plugin-base');
const _ = require('lodash');
module.exports = function(options) {
return new PluginScript(options);
};
function PluginScript(options) {
PluginBase.call(this, options, 'pluginScripts', null);
}
util.inherits(PluginScript, PluginBase);
PluginScript.prototype.load = function(context) {
const options = this.options;
const appRootDir = options.rootDir;
// require directories
let pluginDirs = options.pluginDirs || []; // precedence
pluginDirs = pluginDirs.concat(path.join(appRootDir, 'plugins'));
utils.resolveRelativePaths(pluginDirs, appRootDir);
let pluginScripts = options.pluginScripts || [];
utils.resolveRelativePaths(pluginScripts, appRootDir);
pluginDirs.forEach(function(dir) {
pluginScripts = pluginScripts.concat(
utils.findScripts(dir, options.scriptExtensions),
);
const envdir = dir + '/' + options.env;
pluginScripts = pluginScripts.concat(
utils.findScripts(envdir, options.scriptExtensions),
);
});
pluginScripts = _.uniq(pluginScripts);
debug('Plugin scripts: %j', pluginScripts);
this.configure(context, pluginScripts);
return pluginScripts;
};
PluginScript.prototype.compile = function(context) {
const pluginScripts = context.configurations.pluginScripts;
context.instructions = context.instructions || {};
const plugins = context.instructions.pluginScripts = {};
const self = this;
pluginScripts.forEach(function(ps) {
debug('Loading %s', ps);
const factory = require(ps);
const handler = factory(self.options);
const name = handler.name || path.basename(ps, '.js');
debug('Loaded plugin name: %s', name);
plugins[name] = handler;
});
};

135
lib/plugins/application.js Normal file
View File

@ -0,0 +1,135 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const util = require('util');
const assert = require('assert');
const semver = require('semver');
const PluginBase = require('../plugin-base');
const g = require('../globalize');
module.exports = function(options) {
return new Application(options);
};
function Application(options) {
PluginBase.call(this, options, 'application', 'config');
}
util.inherits(Application, PluginBase);
function assertLoopBackVersion(app) {
const RANGE = '2.x || 3.x';
const loopback = app.loopback;
// remove any pre-release tag from the version string,
// because semver has special treatment of pre-release versions,
// while loopback-boot treats pre-releases the same way as regular versions
const version = (loopback.version || '1.0.0').replace(/-.*$/, '');
if (!semver.satisfies(version, RANGE)) {
const msg = g.f(
'The `app` is powered by an incompatible loopback version %s. ' +
'Supported versions: %s',
loopback.version || '(unknown)',
RANGE,
);
throw new Error(msg);
}
}
function setEnv(app, env) {
if (env !== undefined)
app.set('env', env);
}
function setHost(app, appConfig) {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
const host =
process.env.npm_config_host ||
process.env.OPENSHIFT_SLS_IP ||
process.env.OPENSHIFT_NODEJS_IP ||
process.env.HOST ||
process.env.VCAP_APP_HOST ||
appConfig.host ||
process.env.npm_package_config_host ||
app.get('host');
if (host !== undefined) {
assert(typeof host === 'string', 'app.host must be a string');
app.set('host', host);
}
}
function setPort(app, appConfig) {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
const port = find([
process.env.npm_config_port,
process.env.OPENSHIFT_SLS_PORT,
process.env.OPENSHIFT_NODEJS_PORT,
process.env.PORT,
process.env.VCAP_APP_PORT,
appConfig.port,
process.env.npm_package_config_port,
app.get('port'),
3000,
], function(p) {
return p != null;
});
if (port !== undefined) {
const portType = typeof port;
assert(portType === 'string' || portType === 'number',
'app.port must be a string or number');
app.set('port', port);
}
}
function find(array, predicate) {
return array.filter(predicate)[0];
}
function setApiRoot(app, appConfig) {
const restApiRoot =
appConfig.restApiRoot ||
app.get('restApiRoot') ||
'/api';
assert(restApiRoot !== undefined, 'app.restBasePath is required');
assert(typeof restApiRoot === 'string',
'app.restApiRoot must be a string');
assert(/^\//.test(restApiRoot),
'app.restApiRoot must start with "/"');
app.set('restApiRoot', restApiRoot);
}
function applyAppConfig(app, appConfig) {
for (const configKey in appConfig) {
const cur = app.get(configKey);
if (cur === undefined || cur === null) {
app.set(configKey, appConfig[configKey]);
}
}
}
Application.prototype.starting = function(context) {
const app = context.app;
assertLoopBackVersion(app);
const appConfig = context.instructions.application;
setEnv(app, context.instructions.env || this.options.env);
setHost(app, appConfig);
setPort(app, appConfig);
setApiRoot(app, appConfig);
applyAppConfig(app, appConfig);
};
Application.prototype.started = function(context, done) {
const app = context.app;
process.nextTick(function() {
app.emit('booted');
done();
});
};

105
lib/plugins/boot-script.js Normal file
View File

@ -0,0 +1,105 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const util = require('util');
const utils = require('../utils');
const path = require('path');
const async = require('async');
const debug = require('debug')('loopback:boot:script');
const PluginBase = require('../plugin-base');
const _ = require('lodash');
const g = require('../globalize');
module.exports = function(options) {
return new Script(options);
};
function Script(options) {
PluginBase.call(this, options, 'bootScripts', null);
}
util.inherits(Script, PluginBase);
Script.prototype.load = function(context) {
const options = this.options;
const appRootDir = options.rootDir;
// require directories
let bootDirs = options.bootDirs || []; // precedence
bootDirs = bootDirs.concat(path.join(appRootDir, 'boot'));
utils.resolveRelativePaths(bootDirs, appRootDir);
let bootScripts = options.bootScripts || [];
utils.resolveRelativePaths(bootScripts, appRootDir);
bootDirs.forEach(function(dir) {
bootScripts = bootScripts.concat(
utils.findScripts(dir, options.scriptExtensions),
);
const envdir = dir + '/' + options.env;
bootScripts = bootScripts.concat(
utils.findScripts(envdir, options.scriptExtensions),
);
});
// de-dedup boot scripts -ERS
// https://github.com/strongloop/loopback-boot/issues/64
bootScripts = _.uniq(bootScripts);
debug('Boot scripts: %j', bootScripts);
this.configure(context, bootScripts);
return bootScripts;
};
Script.prototype.start = function(context, done) {
const app = context.app;
const instructions = context.instructions[this.name];
runScripts(app, instructions, done);
};
function runScripts(app, list, callback) {
list = list || [];
const functions = [];
list.forEach(function(filepath) {
debug('Requiring script %s', filepath);
try {
let exports = require(filepath);
if (exports.__esModule) exports = exports.default;
if (typeof exports === 'function') {
debug('Exported function detected %s', filepath);
functions.push({
path: filepath,
func: exports,
});
}
} catch (err) {
g.error('Failed loading boot script: %s\n%s', filepath, err.stack);
throw err;
}
});
async.eachSeries(functions, function(f, done) {
debug('Running script %s', f.path);
let cb = function(err) {
debug('Async function %s %s', err ? 'failed' : 'finished', f.path);
done(err);
// Make sure done() isn't called twice, e.g. if a script returns a
// thenable object and also calls the passed callback.
cb = function() {};
};
try {
const result = f.func(app, cb);
if (result && typeof result.then === 'function') {
result.then(function() { cb(); }, cb);
} else if (f.func.length < 2) {
debug('Sync function finished %s', f.path);
done();
}
} catch (err) {
debug('Sync function failed %s', f.path, err);
done(err);
}
}, callback);
}

52
lib/plugins/component.js Normal file
View File

@ -0,0 +1,52 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const util = require('util');
const debug = require('debug')('loopback:boot:component');
const PluginBase = require('../plugin-base');
const utils = require('../utils');
const resolveAppScriptPath = utils.resolveAppScriptPath;
module.exports = function(options) {
return new Component(options);
};
function Component(options) {
PluginBase.call(this, options, 'components', 'component-config');
}
util.inherits(Component, PluginBase);
Component.prototype.getRootDir = function() {
return this.options.componentRootDir || this.options.rootDir;
};
Component.prototype.buildInstructions = function(context, rootDir, config) {
return Object.keys(config)
.filter(function(name) {
return !!config[name];
}).map(function(name) {
return {
sourceFile: resolveAppScriptPath(rootDir, name, {strict: true}),
config: config[name],
};
});
};
Component.prototype.start = function(context) {
const app = context.app;
const self = this;
context.instructions[this.name].forEach(function(data) {
debug('Configuring component %j', data.sourceFile);
const configFn = require(data.sourceFile);
data.config = self.getUpdatedConfigObject(context, data.config,
{useEnvVars: true});
configFn(app, data.config);
});
};

41
lib/plugins/datasource.js Normal file
View File

@ -0,0 +1,41 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const util = require('util');
const utils = require('../utils');
const PluginBase = require('../plugin-base');
const debug = require('debug')('loopback:boot:datasource');
module.exports = function(options) {
return new DataSource(options);
};
function DataSource(options) {
PluginBase.call(this, options, 'dataSources', 'datasources');
}
util.inherits(DataSource, PluginBase);
DataSource.prototype.getRootDir = function() {
return this.options.dsRootDir;
};
DataSource.prototype.start = function(context) {
const app = context.app;
const self = this;
const lazyConnect = process.env.LB_LAZYCONNECT_DATASOURCES;
utils.forEachKeyedObject(context.instructions[this.name], function(key, obj) {
obj = self.getUpdatedConfigObject(context, obj, {useEnvVars: true});
debug('Registering data source %s %j', key, obj);
if (lazyConnect) {
obj.lazyConnect =
lazyConnect === 'false' || lazyConnect === '0' ? false : true;
}
app.dataSource(key, obj);
});
};

266
lib/plugins/middleware.js Normal file
View File

@ -0,0 +1,266 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const util = require('util');
const assert = require('assert');
const path = require('path');
const _ = require('lodash');
const cloneDeepWith = _.cloneDeepWith;
const cloneDeep = _.cloneDeep;
const debug = require('debug')('loopback:boot:middleware');
const PluginBase = require('../plugin-base');
const utils = require('../utils');
const g = require('../globalize');
const resolveAppScriptPath = utils.resolveAppScriptPath;
module.exports = function(options) {
return new Middleware(options);
};
function Middleware(options) {
PluginBase.call(this, options, 'middleware', 'middleware');
}
util.inherits(Middleware, PluginBase);
Middleware.prototype.getRootDir = function() {
return this.options.middlewareRootDir || this.options.rootDir;
};
Middleware.prototype.merge = function(target, config, fileName) {
let err, phase;
for (phase in config) {
if (phase in target) {
err = this.mergePhaseConfig(target[phase], config[phase], phase);
} else {
err = g.f('The {{phase}} "%s" is not defined in the main config.', phase);
}
if (err)
throw new Error(g.f('Cannot apply %s: ', fileName) + err);
}
};
Middleware.prototype.mergePhaseConfig = function(target, config, phase) {
let err, mw;
for (mw in config) {
if (mw in target) {
const targetMiddleware = target[mw];
const configMiddleware = config[mw];
if (Array.isArray(targetMiddleware) && Array.isArray(configMiddleware)) {
// Both are arrays, combine them
target[mw] = this._mergeNamedItems(targetMiddleware, configMiddleware);
} else if (Array.isArray(targetMiddleware)) {
if (typeof configMiddleware === 'object' &&
Object.keys(configMiddleware).length) {
// Config side is an non-empty object
target[mw] = this._mergeNamedItems(targetMiddleware,
[configMiddleware]);
}
} else if (Array.isArray(configMiddleware)) {
if (typeof targetMiddleware === 'object' &&
Object.keys(targetMiddleware).length) {
// Target side is an non-empty object
target[mw] = this._mergeNamedItems([targetMiddleware],
configMiddleware);
} else {
// Target side is empty
target[mw] = configMiddleware;
}
} else {
err = this._mergeObjects(targetMiddleware, configMiddleware);
}
} else {
err = g.f('The {{middleware}} "%s" in {{phase}} "%s"' +
'is not defined in the main config.', mw, phase);
}
if (err) return err;
}
};
Middleware.prototype.buildInstructions = function(context, rootDir, config) {
const phasesNames = Object.keys(config);
const middlewareList = [];
phasesNames.forEach(function(phase) {
const phaseConfig = config[phase];
Object.keys(phaseConfig).forEach(function(middleware) {
let allConfigs = phaseConfig[middleware];
if (!Array.isArray(allConfigs))
allConfigs = [allConfigs];
allConfigs.forEach(function(config) {
const resolved = resolveMiddlewarePath(rootDir, middleware, config);
// resolved.sourceFile will be false-y if an optional middleware
// is not resolvable.
// if a non-optional middleware is not resolvable, it will throw
// at resolveAppPath() and not reach here
if (!resolved.sourceFile) {
return g.log('Middleware "%s" not found: %s',
middleware,
resolved.optional);
}
const middlewareConfig = cloneDeep(config);
middlewareConfig.phase = phase;
if (middlewareConfig.params) {
middlewareConfig.params = resolveMiddlewareParams(
rootDir, middlewareConfig.params,
);
}
const item = {
sourceFile: resolved.sourceFile,
config: middlewareConfig,
};
if (resolved.fragment) {
item.fragment = resolved.fragment;
}
middlewareList.push(item);
});
});
});
const flattenedPhaseNames = phasesNames
.map(function getBaseName(name) {
return name.replace(/:[^:]+$/, '');
})
.filter(function differsFromPreviousItem(value, ix, source) {
// Skip duplicate entries. That happens when
// `name:before` and `name:after` are both translated to `name`
return ix === 0 || value !== source[ix - 1];
});
return {
phases: flattenedPhaseNames,
middleware: middlewareList,
};
};
function resolveMiddlewarePath(rootDir, middleware, config) {
const resolved = {
optional: !!config.optional,
};
const segments = middleware.split('#');
let pathName = segments[0];
const fragment = segments[1];
const middlewarePath = pathName;
const opts = {
strict: true,
optional: !!config.optional,
};
if (fragment) {
resolved.fragment = fragment;
}
if (pathName.indexOf('./') === 0 || pathName.indexOf('../') === 0) {
// Relative path
pathName = path.resolve(rootDir, pathName);
}
const resolveOpts = _.extend(opts, {
// Workaround for strong-agent to allow probes to detect that
// strong-express-middleware was loaded: exclude the path to the
// module main file from the source file path.
// For example, return
// node_modules/strong-express-metrics
// instead of
// node_modules/strong-express-metrics/index.js
fullResolve: false,
});
const sourceFile = resolveAppScriptPath(rootDir, middlewarePath, resolveOpts);
if (!fragment) {
resolved.sourceFile = sourceFile;
return resolved;
}
// Try to require the module and check if <module>.<fragment> is a valid
// function
const m = require(sourceFile);
if (typeof m[fragment] === 'function') {
resolved.sourceFile = sourceFile;
return resolved;
}
/*
* module/server/middleware/fragment
* module/middleware/fragment
*/
const candidates = [
pathName + '/server/middleware/' + fragment,
pathName + '/middleware/' + fragment,
// TODO: [rfeng] Should we support the following flavors?
// pathName + '/lib/' + fragment,
// pathName + '/' + fragment
];
let err, ix;
for (ix in candidates) {
try {
resolved.sourceFile = resolveAppScriptPath(rootDir, candidates[ix], opts);
delete resolved.fragment;
return resolved;
} catch (e) {
// Report the error for the first candidate when no candidate matches
if (!err) err = e;
}
}
throw err;
}
// Match values starting with `$!./` or `$!../`
const MIDDLEWARE_PATH_PARAM_REGEX = /^\$!(\.\/|\.\.\/)/;
function resolveMiddlewareParams(rootDir, params) {
return cloneDeepWith(params, function resolvePathParam(value) {
if (typeof value === 'string' && MIDDLEWARE_PATH_PARAM_REGEX.test(value)) {
return path.resolve(rootDir, value.slice(2));
} else {
return undefined; // no change
}
});
}
Middleware.prototype.start = function(context) {
const self = this;
const app = context.app;
const instructions = context.instructions.middleware;
if (!instructions) {
// the browserified client does not support middleware
return;
}
// Phases can be empty
const phases = instructions.phases || [];
assert(Array.isArray(phases),
'Middleware phases must be an array');
const middleware = instructions.middleware;
assert(Array.isArray(middleware),
'Middleware must be an array');
debug('Defining middleware phases %j', phases);
app.defineMiddlewarePhases(phases);
middleware.forEach(function(data) {
debug('Configuring middleware %j%s', data.sourceFile,
data.fragment ? ('#' + data.fragment) : '');
let factory = require(data.sourceFile);
if (data.fragment) {
factory = factory[data.fragment].bind(factory);
}
assert(typeof factory === 'function',
'Middleware factory must be a function');
data.config = self.getUpdatedConfigObject(context, data.config,
{useEnvVars: true});
app.middlewareFromConfig(factory, data.config);
});
};

200
lib/plugins/mixin.js Normal file
View File

@ -0,0 +1,200 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const util = require('util');
const fs = require('fs');
const path = require('path');
const PluginBase = require('../plugin-base');
const _ = require('lodash');
const debug = require('debug')('loopback:boot:mixin');
const utils = require('../utils');
const g = require('../globalize');
const tryResolveAppPath = utils.tryResolveAppPath;
const getExcludedExtensions = utils.getExcludedExtensions;
const findScripts = utils.findScripts;
const FILE_EXTENSION_JSON = utils.FILE_EXTENSION_JSON;
module.exports = function(options) {
return new Mixin(options);
};
function Mixin(options) {
PluginBase.call(this, options, 'mixins', null);
}
util.inherits(Mixin, PluginBase);
Mixin.prototype.buildInstructions = function(context, rootDir, config) {
const modelsMeta = context.configurations.mixins._meta || {};
const modelInstructions = context.instructions.models;
const mixinSources = this.options.mixinSources || modelsMeta.mixins ||
['./mixins'];
const scriptExtensions = this.options.scriptExtensions || require.extensions;
const mixinInstructions = buildAllMixinInstructions(
rootDir, this.options, mixinSources, scriptExtensions, modelInstructions,
);
return mixinInstructions;
};
function buildAllMixinInstructions(appRootDir, options, mixinSources,
scriptExtensions, modelInstructions) {
// load mixins from `options.mixins`
let sourceFiles = options.mixins || [];
const mixinDirs = options.mixinDirs || [];
const instructionsFromMixins = loadMixins(sourceFiles, options.normalization);
// load mixins from `options.mixinDirs`
sourceFiles = findMixinDefinitions(appRootDir, mixinDirs, scriptExtensions);
if (sourceFiles === undefined) return;
const instructionsFromMixinDirs = loadMixins(sourceFiles,
options.normalization);
/* If `mixinDirs` and `mixinSources` have any directories in common,
* then remove the common directories from `mixinSources` */
mixinSources = _.difference(mixinSources, mixinDirs);
// load mixins from `options.mixinSources`
sourceFiles = findMixinDefinitions(appRootDir, mixinSources,
scriptExtensions);
if (sourceFiles === undefined) return;
let instructionsFromMixinSources = loadMixins(sourceFiles,
options.normalization);
// Fetch unique list of mixin names, used in models
let modelMixins = fetchMixinNamesUsedInModelInstructions(modelInstructions);
modelMixins = _.uniq(modelMixins);
// Filter-in only mixins, that are used in models
instructionsFromMixinSources = filterMixinInstructionsUsingWhitelist(
instructionsFromMixinSources, modelMixins,
);
const mixins = _.assign(
instructionsFromMixins,
instructionsFromMixinDirs,
instructionsFromMixinSources,
);
return _.values(mixins);
}
function findMixinDefinitions(appRootDir, sourceDirs, scriptExtensions) {
let files = [];
sourceDirs.forEach(function(dir) {
const path = tryResolveAppPath(appRootDir, dir);
if (!path) {
debug('Skipping unknown module source dir %j', dir);
return;
}
files = files.concat(findScripts(path, scriptExtensions));
});
return files;
}
function loadMixins(sourceFiles, normalization) {
const mixinInstructions = {};
sourceFiles.forEach(function(filepath) {
const dir = path.dirname(filepath);
const ext = path.extname(filepath);
let name = path.basename(filepath, ext);
const metafile = path.join(dir, name + FILE_EXTENSION_JSON);
name = normalizeMixinName(name, normalization);
const meta = {};
meta.name = name;
if (utils.fileExistsSync(metafile)) {
// May overwrite name, not sourceFile
_.extend(meta, require(metafile));
}
meta.sourceFile = filepath;
mixinInstructions[meta.name] = meta;
});
return mixinInstructions;
}
function fetchMixinNamesUsedInModelInstructions(modelInstructions) {
return _.flatten(modelInstructions
.map(function(model) {
return model.definition && model.definition.mixins ?
Object.keys(model.definition.mixins) : [];
}));
}
function filterMixinInstructionsUsingWhitelist(instructions, includeMixins) {
const instructionKeys = Object.keys(instructions);
includeMixins = _.intersection(instructionKeys, includeMixins);
const filteredInstructions = {};
instructionKeys.forEach(function(mixinName) {
if (includeMixins.indexOf(mixinName) !== -1) {
filteredInstructions[mixinName] = instructions[mixinName];
}
});
return filteredInstructions;
}
function normalizeMixinName(str, normalization) {
switch (normalization) {
case false:
case 'none':
return str;
case undefined:
case 'classify':
str = String(str).replace(/([A-Z]+)/g, ' $1').trim();
str = String(str).replace(/[\W_]/g, ' ').toLowerCase();
str = str.replace(/(?:^|\s|-)\S/g, function(c) {
return c.toUpperCase();
});
str = str.replace(/\s+/g, '');
return str;
case 'dasherize':
str = String(str).replace(/([A-Z]+)/g, ' $1').trim();
str = String(str).replace(/[\W_]/g, ' ').toLowerCase();
str = str.replace(/\s+/g, '-');
return str;
default:
if (typeof normalization === 'function') {
return normalization(str);
}
const err = new Error(g.f('Invalid normalization format - "%s"',
normalization));
err.code = 'INVALID_NORMALIZATION_FORMAT';
throw err;
}
}
Mixin.prototype.starting = function(context) {
const app = context.app;
const instructions = context.instructions.mixins;
const modelBuilder = (app.registry || app.loopback).modelBuilder;
const BaseClass = app.loopback.Model;
const mixins = instructions || [];
if (!modelBuilder.mixins || !mixins.length) return;
mixins.forEach(function(obj) {
debug('Requiring mixin %s', obj.sourceFile);
const mixin = require(obj.sourceFile);
if (typeof mixin === 'function' || mixin.prototype instanceof BaseClass) {
debug('Defining mixin %s', obj.name);
modelBuilder.mixins.define(obj.name, mixin); // TODO (name, mixin, meta)
} else {
debug('Skipping mixin file %s - `module.exports` is not a function' +
' or Loopback model', obj);
}
});
};

319
lib/plugins/model.js Normal file
View File

@ -0,0 +1,319 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const assert = require('assert');
const util = require('util');
const PluginBase = require('../plugin-base');
const path = require('path');
const debug = require('debug')('loopback:boot:model');
const _ = require('lodash');
const toposort = require('toposort');
const utils = require('../utils');
const tryReadDir = utils.tryReadDir;
const assertIsValidConfig = utils.assertIsValidConfig;
const tryResolveAppPath = utils.tryResolveAppPath;
const fixFileExtension = utils.fixFileExtension;
const g = require('../globalize');
module.exports = function(options) {
return new Model(options);
};
function Model(options) {
PluginBase.call(this, options, 'models', 'model-config');
}
util.inherits(Model, PluginBase);
Model.prototype.getRootDir = function() {
return this.options.modelsRootDir;
};
Model.prototype.load = function(context) {
const config = PluginBase.prototype.load.apply(this, arguments);
assertIsValidModelConfig(config);
return config;
};
Model.prototype.buildInstructions = function(context, rootDir, modelsConfig) {
const modelsMeta = modelsConfig._meta || {};
delete modelsConfig._meta;
context.configurations.mixins._meta = modelsMeta;
const modelSources = this.options.modelSources || modelsMeta.sources ||
['./models'];
const modelInstructions = buildAllModelInstructions(
rootDir, modelsConfig, modelSources, this.options.modelDefinitions,
this.options.scriptExtensions,
);
return modelInstructions;
};
function buildAllModelInstructions(rootDir, modelsConfig, sources,
modelDefinitions, scriptExtensions) {
let registry = verifyModelDefinitions(rootDir, modelDefinitions,
scriptExtensions);
if (!registry) {
registry = findModelDefinitions(rootDir, sources, scriptExtensions);
}
const modelNamesToBuild = addAllBaseModels(
registry,
Object.keys(modelsConfig),
);
const instructions = modelNamesToBuild
.map(function createModelInstructions(name) {
const config = modelsConfig[name];
const definition = registry[name] || {};
debug('Using model "%s"\nConfiguration: %j\nDefinition %j',
name, config, definition.definition);
return {
name: name,
config: config,
definition: definition.definition,
sourceFile: definition.sourceFile,
};
});
return sortByInheritance(instructions);
}
function addAllBaseModels(registry, modelNames) {
const result = [];
const visited = {};
while (modelNames.length) {
const name = modelNames.shift();
if (visited[name]) continue;
visited[name] = true;
result.push(name);
const definition = registry[name] && registry[name].definition;
if (!definition) continue;
const base = getBaseModelName(definition);
// ignore built-in models like User
if (!registry[base]) continue;
modelNames.push(base);
}
return result;
}
function getBaseModelName(modelDefinition) {
if (!modelDefinition)
return undefined;
return modelDefinition.base ||
modelDefinition.options && modelDefinition.options.base;
}
function sortByInheritance(instructions) {
// create edges Base name -> Model name
const edges = instructions
.map(function(inst) {
return [getBaseModelName(inst.definition), inst.name];
});
const sortedNames = toposort(edges);
const instructionsByModelName = {};
instructions.forEach(function(inst) {
instructionsByModelName[inst.name] = inst;
});
return sortedNames
// convert to instructions
.map(function(name) {
return instructionsByModelName[name];
})
// remove built-in models
.filter(function(inst) {
return !!inst;
});
}
function verifyModelDefinitions(rootDir, modelDefinitions, scriptExtensions) {
if (!modelDefinitions || modelDefinitions.length < 1) {
return undefined;
}
const registry = {};
modelDefinitions.forEach(function(definition, idx) {
if (definition.sourceFile) {
const fullPath = path.resolve(rootDir, definition.sourceFile);
definition.sourceFile = fixFileExtension(
fullPath,
tryReadDir(path.dirname(fullPath)),
scriptExtensions,
);
if (!definition.sourceFile) {
debug('Model source code not found: %s - %s', definition.sourceFile);
}
}
debug('Found model "%s" - %s %s',
definition.definition.name,
'from options',
definition.sourceFile ?
path.relative(rootDir, definition.sourceFile) :
'(no source file)');
const modelName = definition.definition.name;
if (!modelName) {
debug('Skipping model definition without Model name ' +
'(from options.modelDefinitions @ index %s)',
idx);
return;
}
registry[modelName] = definition;
});
return registry;
}
function findModelDefinitions(rootDir, sources, scriptExtensions) {
const registry = {};
sources.forEach(function(src) {
const srcDir = tryResolveAppPath(rootDir, src, {strict: false});
if (!srcDir) {
debug('Skipping unknown module source dir %j', src);
return;
}
const files = tryReadDir(srcDir);
files
.filter(function(f) {
return f[0] !== '_' && path.extname(f) === '.json';
})
.forEach(function(f) {
const fullPath = path.resolve(srcDir, f);
const entry = loadModelDefinition(rootDir, fullPath, files,
scriptExtensions);
const modelName = entry.definition.name;
if (!modelName) {
debug('Skipping model definition without Model name: %s',
path.relative(srcDir, fullPath));
return;
}
registry[modelName] = entry;
});
});
return registry;
}
function loadModelDefinition(rootDir, jsonFile, allFiles, scriptExtensions) {
const definition = require(jsonFile);
const basename = path.basename(jsonFile, path.extname(jsonFile));
definition.name = definition.name || _.upperFirst(_.camelCase(basename));
// find a matching file with a supported extension like `.js` or `.coffee`
const sourceFile = fixFileExtension(jsonFile, allFiles, scriptExtensions);
if (sourceFile === undefined) {
debug('Model source code not found: %s', sourceFile);
}
debug('Found model "%s" - %s %s', definition.name,
path.relative(rootDir, jsonFile),
sourceFile ? path.relative(rootDir, sourceFile) : '(no source file)');
return {
definition: definition,
sourceFile: sourceFile,
};
}
function assertIsValidModelConfig(config) {
assertIsValidConfig('model', config);
for (const name in config) {
const entry = config[name];
const options = entry.options || {};
const unsupported = entry.properties ||
entry.base || options.base ||
entry.plural || options.plural;
if (unsupported) {
throw new Error(g.f(
'The data in {{model-config.json}} ' +
'is in the unsupported {{1.x}} format.',
));
}
}
}
// Regular expression to match built-in loopback models
const LOOPBACK_MODEL_REGEXP = new RegExp(
['', 'node_modules', 'loopback', '[^\\/\\\\]+', 'models', '[^\\/\\\\]+\\.js$']
.join('\\' + path.sep),
);
function isBuiltinLoopBackModel(app, data) {
// 1. Built-in models are exposed on the loopback object
if (!app.loopback[data.name]) return false;
// 2. Built-in models have a script file `loopback/{facet}/models/{name}.js`
const srcFile = data.sourceFile;
return srcFile &&
LOOPBACK_MODEL_REGEXP.test(srcFile);
}
Model.prototype.start = function(context) {
const app = context.app;
const instructions = context.instructions[this.name];
const registry = app.registry || app.loopback;
instructions.forEach(function(data) {
const name = data.name;
let model;
if (!data.definition) {
model = registry.getModel(name);
if (!model) {
throw new Error(g.f('Cannot configure unknown model %s', name));
}
debug('Configuring existing model %s', name);
} else if (isBuiltinLoopBackModel(app, data)) {
model = registry.getModel(name);
assert(model, 'Built-in model ' + name + ' should have been defined');
debug('Configuring built-in LoopBack model %s', name);
} else {
debug('Creating new model %s %j', name, data.definition);
model = registry.createModel(data.definition);
if (data.sourceFile) {
debug('Loading customization script %s', data.sourceFile);
const code = require(data.sourceFile);
if (typeof code === 'function') {
debug('Customizing model %s', name);
code(model);
} else {
debug('Skipping model file %s - `module.exports` is not a function',
data.sourceFile);
}
}
}
data._model = model;
});
instructions.forEach(function(data) {
// Skip base models that are not exported to the app
if (!data.config) return;
app.model(data._model, data.config);
});
};

31
lib/plugins/swagger.js Normal file
View File

@ -0,0 +1,31 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const util = require('util');
const PluginBase = require('../plugin-base');
module.exports = function(options) {
return new Swagger(options);
};
function Swagger(options) {
PluginBase.call(this, options, 'apis', null);
}
util.inherits(Swagger, PluginBase);
Swagger.prototype.start = function(context) {
const app = context.app;
const appConfig = context.instructions.application;
// disable token requirement for swagger, if available
const swagger = app.remotes().exports.swagger;
if (!swagger) return;
const requireTokenForSwagger = appConfig.swagger &&
appConfig.swagger.requireToken;
swagger.requireToken = requireTokenForSwagger || false;
};

368
lib/utils.js Normal file
View File

@ -0,0 +1,368 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const debug = require('debug')('loopback:boot');
const path = require('path');
const Module = require('module');
const fs = require('fs');
const assert = require('assert');
const _ = require('lodash');
const g = require('./globalize');
exports.arrayToObject = arrayToObject;
exports.tryReadDir = tryReadDir;
exports.resolveRelativePaths = resolveRelativePaths;
exports.assertIsValidConfig = assertIsValidConfig;
exports.fileExistsSync = fileExistsSync;
exports.fixFileExtension = fixFileExtension;
exports.findScripts = findScripts;
exports.resolveAppScriptPath = resolveAppScriptPath;
exports.getExcludedExtensions = getExcludedExtensions;
exports.tryResolveAppPath = tryResolveAppPath;
exports.forEachKeyedObject = forEachKeyedObject;
exports.mergePhaseNameLists = mergePhaseNameLists;
const FILE_EXTENSION_JSON = exports.FILE_EXTENSION_JSON = '.json';
/**
* Find all javascript files (except for those prefixed with _)
* and all directories.
* @param {String} dir Full path of the directory to enumerate.
* @return {Array.<String>} A list of absolute paths to pass to `require()`.
*/
function findScripts(dir, scriptExtensions) {
assert(dir, 'cannot require directory contents without directory name');
const files = tryReadDir(dir);
scriptExtensions = scriptExtensions || require.extensions;
// sort files in lowercase alpha for linux
files.sort(function(a, b) {
a = a.toLowerCase();
b = b.toLowerCase();
if (a < b) {
return -1;
} else if (b < a) {
return 1;
} else {
return 0;
}
});
const results = [];
files.forEach(function(filename) {
// ignore index.js and files prefixed with underscore
if (filename === 'index.js' || filename[0] === '_') {
return;
}
const filepath = path.resolve(path.join(dir, filename));
const stats = fs.statSync(filepath);
// only require files supported by specified extensions
if (stats.isFile()) {
if (scriptExtensions && isPreferredExtension(filename, scriptExtensions))
results.push(filepath);
else
debug('Skipping file %s - unknown extension', filepath);
} else {
debug('Skipping directory %s', filepath);
}
});
return results;
}
function tryReadDir() {
try {
return fs.readdirSync.apply(fs, arguments);
} catch (e) {
return [];
}
}
function resolveRelativePaths(relativePaths, appRootDir) {
const resolveOpts = {strict: false};
relativePaths.forEach(function(relativePath, k) {
const resolvedPath = tryResolveAppPath(
appRootDir,
relativePath,
resolveOpts,
);
if (resolvedPath !== undefined) {
relativePaths[k] = resolvedPath;
} else {
debug('skipping boot script %s - unknown file', relativePath);
}
});
}
function getExcludedExtensions() {
return {
'.json': '.json',
'.node': 'node',
/**
* This is a temporary workaround for #246
* See discussion here for full description of the underlying issue
* https://github.com/strongloop/loopback-boot/pull/245#issuecomment-311052798
*/
'.map': 'map',
};
}
function arrayToObject(array) {
return array.reduce(function(obj, val) {
obj[val] = val;
return obj;
}, {});
}
function isPreferredExtension(filename, includeExtensions) {
assert(!!includeExtensions, '"includeExtensions" argument is required');
const ext = path.extname(filename);
return (ext in includeExtensions) && !(ext in getExcludedExtensions());
}
function fixFileExtension(filepath, files, scriptExtensions) {
const results = [];
let otherFile;
/* Prefer coffee scripts over json */
if (scriptExtensions && isPreferredExtension(filepath, scriptExtensions)) {
return filepath;
}
const basename = path.basename(filepath, FILE_EXTENSION_JSON);
const sourceDir = path.dirname(filepath);
files.forEach(function(f) {
otherFile = path.resolve(sourceDir, f);
const stats = fs.statSync(otherFile);
if (stats.isFile()) {
const otherFileExtension = path.extname(f);
if (!(otherFileExtension in getExcludedExtensions()) &&
path.basename(f, otherFileExtension) == basename) {
if (!scriptExtensions || otherFileExtension in scriptExtensions) {
results.push(otherFile);
}
}
}
});
return results.length > 0 ? results[0] : undefined;
}
function resolveAppPath(rootDir, relativePath, resolveOptions) {
const resolvedPath = tryResolveAppPath(rootDir, relativePath, resolveOptions);
if (resolvedPath === undefined && !resolveOptions.optional) {
const err = new Error(g.f('Cannot resolve path "%s"', relativePath));
err.code = 'PATH_NOT_FOUND';
throw err;
}
return resolvedPath;
}
function resolveAppScriptPath(rootDir, relativePath, resolveOptions) {
const resolvedPath = resolveAppPath(rootDir, relativePath, resolveOptions);
if (!resolvedPath) {
return false;
}
const sourceDir = path.dirname(resolvedPath);
const files = tryReadDir(sourceDir);
const fixedFile = fixFileExtension(resolvedPath, files);
return (fixedFile === undefined ? resolvedPath : fixedFile);
}
function tryResolveAppPath(rootDir, relativePath, resolveOptions) {
let fullPath;
const start = relativePath.substring(0, 2);
/* In order to retain backward compatibility, we need to support
* two ways how to treat values that are not relative nor absolute
* path (e.g. `relativePath = 'foobar'`)
* - `resolveOptions.strict = true` searches in `node_modules` only
* - `resolveOptions.strict = false` attempts to resolve the value
* as a relative path first before searching `node_modules`
*/
resolveOptions = resolveOptions || {strict: true};
let isModuleRelative = false;
if (relativePath[0] === '/') {
fullPath = relativePath;
} else if (start === './' || start === '..') {
fullPath = path.resolve(rootDir, relativePath);
} else if (!resolveOptions.strict) {
isModuleRelative = true;
fullPath = path.resolve(rootDir, relativePath);
}
if (fullPath) {
// This check is needed to support paths pointing to a directory
if (fileExistsSync(fullPath)) {
return fullPath;
}
try {
fullPath = require.resolve(fullPath);
return fullPath;
} catch (err) {
if (!isModuleRelative) {
debug('Skipping %s - %s', fullPath, err);
return undefined;
}
}
}
// Handle module-relative path, e.g. `loopback/common/models`
// Module.globalPaths is a list of globally configured paths like
// [ env.NODE_PATH values, $HOME/.node_modules, etc. ]
// Module._nodeModulePaths(rootDir) returns a list of paths like
// [ rootDir/node_modules, rootDir/../node_modules, etc. ]
const modulePaths = Module.globalPaths
.concat(Module._nodeModulePaths(rootDir));
fullPath = modulePaths
.map(function(candidateDir) {
const absPath = path.join(candidateDir, relativePath);
try {
// NOTE(bajtos) We need to create a proper String object here,
// otherwise we can't attach additional properties to it
/* jshint -W053 */
const filePath = new String(require.resolve(absPath));
filePath.unresolvedPath = absPath;
return filePath;
} catch (err) {
return absPath;
}
})
.filter(function(candidate) {
return fileExistsSync(candidate.toString());
})
[0];
if (fullPath) {
if (fullPath.unresolvedPath && resolveOptions.fullResolve === false)
return fullPath.unresolvedPath;
// Convert String object back to plain string primitive
return fullPath.toString();
}
debug('Skipping %s - module not found', fullPath);
return undefined;
}
function assertIsValidConfig(name, config) {
if (config) {
assert(typeof config === 'object',
name + ' config must be a valid JSON object');
}
}
function forEachKeyedObject(obj, fn) {
if (typeof obj !== 'object') return;
Object.keys(obj).forEach(function(key) {
fn(key, obj[key]);
});
}
/**
* Extend the list of builtin phases by merging in an array of phases
* requested by a user while preserving the relative order of phases
* as specified by both arrays.
*
* If the first new name does not match any existing phase, it is inserted
* as the first phase in the new list. The same applies for the second phase,
* and so on, until an existing phase is found.
*
* Any new names in the middle of the array are inserted immediatelly after
* the last common phase. For example, extending
* `["initial", "session", "auth"]` with `["initial", "preauth", "auth"]`
* results in `["initial", "preauth", "session", "auth"]`.
*
*
* **Example**
*
* ```js
* var result = mergePhaseNameLists(
* ['initial', 'session', 'auth', 'routes', 'files', 'final'],
* ['initial', 'postinit', 'preauth', 'auth',
* 'routes', 'subapps', 'final', 'last']
* );
*
* // result: [
* // 'initial', 'postinit', 'preauth', 'session', 'auth',
* // 'routes', 'subapps', 'files', 'final', 'last'
* // ]
* ```
*
* @param {Array} currentNames The current list of phase names.
* @param {Array} namesToMerge The items to add (zip merge) into the target
* array.
* @returns {Array} A new array containing combined items from both arrays.
*
* @header mergePhaseNameLists
*/
function mergePhaseNameLists(currentNames, namesToMerge) {
if (!namesToMerge.length) return currentNames.slice();
const targetArray = currentNames.slice();
let targetIx = targetArray.indexOf(namesToMerge[0]);
if (targetIx === -1) {
// the first new item does not match any existing one
// start adding the new items at the start of the list
targetArray.splice(0, 0, namesToMerge[0]);
targetIx = 0;
}
// merge (zip) two arrays
for (let sourceIx = 1; sourceIx < namesToMerge.length; sourceIx++) {
const valueToAdd = namesToMerge[sourceIx];
const previousValue = namesToMerge[sourceIx - 1];
const existingIx = targetArray.indexOf(valueToAdd, targetIx);
if (existingIx === -1) {
// A new phase - try to add it after the last one,
// unless it was already registered
if (targetArray.indexOf(valueToAdd) !== -1) {
const errMsg = g.f('Ordering conflict: cannot add "%s' +
'" after "%s", because the opposite order was ' +
' already specified', valueToAdd, previousValue);
throw new Error(errMsg);
}
const previousIx = targetArray.indexOf(previousValue);
targetArray.splice(previousIx + 1, 0, valueToAdd);
} else {
// An existing phase - move the pointer
targetIx = existingIx;
}
}
return targetArray;
}
/**
* Check synchronously if a filepath points to an existing file.
* Replaces calls to fs.existsSync, which is deprecated (see:
* https://github.com/nodejs/node/pull/166).
*
* @param {String} filepath The absolute path to check
* @returns {Boolean} True if the file exists
*/
function fileExistsSync(filepath) {
try {
fs.statSync(filepath);
return true;
} catch (e) {
return false;
}
}

View File

@ -1,6 +1,6 @@
{
"name": "loopback-boot",
"version": "1.1.0",
"version": "3.3.1",
"description": "Convention-based bootstrapper for LoopBack applications",
"keywords": [
"StrongLoop",
@ -12,28 +12,40 @@
"type": "git",
"url": "https://github.com/strongloop/loopback-boot"
},
"engines": {
"node": ">=8"
},
"main": "index.js",
"browser": "browser.js",
"scripts": {
"pretest": "jshint .",
"test": "mocha"
},
"license": {
"name": "Dual MIT/StrongLoop",
"url": "https://github.com/strongloop/loopback-boot/blob/master/LICENSE"
"test": "mocha",
"posttest": "npm run lint",
"lint": "eslint .",
"lint:fix": "eslint . --fix"
},
"license": "MIT",
"dependencies": {
"commondir": "0.0.1",
"debug": "^0.8.1",
"semver": "^2.3.0",
"underscore": "^1.6.0"
"async": "^2.4.0",
"bluebird": "^3.5.3",
"commondir": "^1.0.1",
"debug": "^4.1.1",
"lodash": "^4.17.11",
"semver": "^5.1.0",
"strong-globalize": "^4.1.1",
"toposort": "^2.0.2"
},
"devDependencies": {
"loopback": "^1.5.0",
"mocha": "^1.19.0",
"must": "^0.11.0",
"supertest": "^0.13.0",
"fs-extra": "^0.9.1",
"browserify": "^4.1.8"
}
"browserify": "^16.2.3",
"chai": "^4.2.0",
"coffeeify": "^3.0.1",
"coffeescript": "^2.3.1",
"dirty-chai": "^2.0.1",
"eslint": "^6.6.0",
"eslint-config-loopback": "^13.1.0",
"fs-extra": "^7.0.1",
"loopback": "^3.0.0",
"mocha": "^5.2.0",
"supertest": "^4.0.2"
},
"author": "IBM Corp."
}

40
test/acceptance.test.js Normal file
View File

@ -0,0 +1,40 @@
// Copyright IBM Corp. 2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const path = require('path');
const loopback = require('loopback');
const chai = require('chai');
const dirtyChai = require('dirty-chai');
const expect = chai.expect;
chai.use(dirtyChai);
const bootLoopBackApp = require('..');
describe('bootLoopBackApp', function() {
let app;
beforeEach(function() {
app = loopback();
});
it('sets app.booting immediately', function() {
const appDir = path.join(__dirname, './fixtures/empty-app');
// Start the bootstrapper
const promise = bootLoopBackApp(app, appDir);
// Still in the original turn of the event loop,
// verify that the app is signalling "boot in progress"
expect(app.booting).to.equal(true);
// Wait for bootstrapper to finish
return promise.then(() => {
// Verify that app is signalling "boot has finished"
expect(app.booting).to.equal(false);
});
});
});

122
test/bootstrapper.test.js vendored Normal file
View File

@ -0,0 +1,122 @@
// Copyright IBM Corp. 2016,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const path = require('path');
const loopback = require('loopback');
const chai = require('chai');
const dirtyChai = require('dirty-chai');
const expect = chai.expect;
chai.use(dirtyChai);
const Bootstrapper = require('../lib/bootstrapper');
describe('Bootstrapper', function() {
let app;
beforeEach(function() {
app = loopback();
process.bootFlags = [];
});
it('should honor options.phases', function(done) {
const options = {
app: app,
appRootDir: path.join(__dirname, './fixtures/simple-app'),
phases: ['load'],
};
const bootstrapper = new Bootstrapper(options);
const context = {
app: app,
};
bootstrapper.run(context, function(err) {
if (err) return done(err);
const configs = context.configurations;
expect(configs.application, 'application').to.be.an('object');
expect(configs.bootScripts, 'bootScripts').to.be.an('array');
expect(configs.middleware, 'middleware').to.be.an('object');
expect(configs.models, 'models').to.be.an('object');
expect(configs.tracker, 'tracker').to.eql('load');
expect(context.instructions, 'instructions').to.be.undefined();
expect(process.bootFlags.length).to.eql(0);
done();
});
});
it('should honor options.plugins', function(done) {
const options = {
app: app,
appRootDir: path.join(__dirname, './fixtures/simple-app'),
plugins: ['application', 'boot-script'],
};
const bootstrapper = new Bootstrapper(options);
const context = {
app: app,
};
bootstrapper.run(context, function(err) {
if (err) return done(err);
const configs = context.configurations;
const instructions = context.instructions;
expect(configs.application, 'application').to.be.an('object');
expect(configs.middleware, 'middleware').to.be.undefined();
expect(configs.models, 'models').to.be.undefined();
expect(configs.bootScripts, 'bootScripts').to.be.an('array');
expect(instructions.application, 'application').to.be.an('object');
expect(instructions.tracker, 'instruction: tracker').to.eql('compile');
expect(context.executions.tracker, 'execution: tracker').to.eql('start');
expect(process.bootFlags, 'process: bootFlags').to.eql([
'barLoaded',
'barSyncLoaded',
'fooLoaded',
'promiseLoaded',
'thenableLoaded',
'barStarted',
'barFinished',
'barSyncExecuted',
'promiseStarted',
'promiseFinished',
'thenableStarted',
'thenableFinished',
'umdLoaded',
]);
done();
});
});
it('searches boot file extensions specified in options.scriptExtensions',
function(done) {
const options = {
app: app,
appRootDir: path.join(__dirname, './fixtures/simple-app'),
scriptExtensions: ['.customjs', '.customjs2'],
};
const bootstrapper = new Bootstrapper(options);
const context = {
app: app,
};
bootstrapper.run(context, function(err) {
if (err) return done(err);
expect(process.bootFlags, 'process: bootFlags').to.eql([
'customjs',
'customjs2',
]);
done();
});
});
afterEach(function() {
delete process.bootFlags;
});
});

View File

@ -0,0 +1,125 @@
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const boot = require('../');
const async = require('async');
const exportBrowserifyToFile = require('./helpers/browserify').exportToSandbox;
const packageFilter = require('./helpers/browserify').packageFilter;
const fs = require('fs');
const path = require('path');
const expect = require('chai').expect;
const browserify = require('browserify');
const sandbox = require('./helpers/sandbox');
const vm = require('vm');
const createBrowserLikeContext = require('./helpers/browser').createContext;
const printContextLogs = require('./helpers/browser').printContextLogs;
describe('browser support for multiple apps', function() {
this.timeout(60000); // 60s to give browserify enough time to finish
beforeEach(sandbox.reset);
it('has API for bundling and booting multiple apps', function(done) {
const app1Dir = path.resolve(__dirname, './fixtures/browser-app');
const app2Dir = path.resolve(__dirname, './fixtures/browser-app-2');
const apps = [
{
appDir: app1Dir,
appFile: './app.js',
moduleName: 'browser-app',
},
{
appDir: app2Dir,
appFile: './app.js',
moduleName: 'browser-app2',
appId: 'browserApp2',
},
];
browserifyTestApps(apps, function(err, bundlePath) {
if (err) return done(err);
const bundledApps = executeBundledApps(bundlePath, apps, function(err) {
const app1 = bundledApps.defaultApp;
const app2 = bundledApps.browserApp2;
expect(app1.settings).to.have.property('custom-key', 'custom-value');
expect(Object.keys(app1.models)).to.include('Customer');
expect(Object.keys(app1.models)).to.not.include('Robot');
expect(app1.models.Customer.settings).to.have.property('_customized',
'Customer');
expect(Object.keys(app2.models)).to.include('Robot');
expect(Object.keys(app2.models)).to.not.include('Customer');
done();
});
});
});
});
function browserifyTestApps(apps, next) {
const b = browserify({
debug: true,
basedir: path.resolve(__dirname, './fixtures'),
packageFilter,
});
const bundles = [];
for (const i in apps) {
const appDir = apps[i].appDir;
let appFile = apps[i].appFile;
const moduleName = apps[i].moduleName;
const appId = apps[i].appId;
appFile = path.join(appDir, appFile);
b.require(appFile, {expose: moduleName});
let opts = appDir;
if (appId) {
opts = {
appId: appId,
appRootDir: appDir,
};
}
bundles.push(opts);
}
async.eachSeries(bundles, function(opts, done) {
boot.compileToBrowserify(opts, b, done);
}, function(err) {
exportBrowserifyToFile(b, 'browser-app-bundle.js', next);
});
}
function executeBundledApps(bundlePath, apps, done) {
const code = fs.readFileSync(bundlePath);
const context = createBrowserLikeContext();
vm.runInContext(code, context, bundlePath);
const ids = [];
let script = 'var apps = {};\n';
for (const i in apps) {
const moduleName = apps[i].moduleName;
const id = apps[i].appId || 'defaultApp';
ids.push(id);
script += 'apps.' + id + ' = require("' + moduleName + '");\n';
}
script += 'apps;\n';
const appsInContext = vm.runInContext(script, context);
async.each(ids, function(id, done) {
appsInContext[id].once('booted', function() {
done();
});
}, function(err) {
printContextLogs(context);
done(err, appsInContext);
});
return appsInContext;
}

View File

@ -1,91 +1,143 @@
var boot = require('../');
var fs = require('fs');
var path = require('path');
var expect = require('must');
var browserify = require('browserify');
var sandbox = require('./helpers/sandbox');
var vm = require('vm');
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const boot = require('../');
const exportBrowserifyToFile = require('./helpers/browserify').exportToSandbox;
const packageFilter = require('./helpers/browserify').packageFilter;
const fs = require('fs');
const path = require('path');
const expect = require('chai').expect;
const browserify = require('browserify');
const sandbox = require('./helpers/sandbox');
const vm = require('vm');
const createBrowserLikeContext = require('./helpers/browser').createContext;
const printContextLogs = require('./helpers/browser').printContextLogs;
const compileStrategies = {
default: function(appDir) {
const b = browserify({
basedir: appDir,
debug: true,
packageFilter,
});
b.require('./app.js', {expose: 'browser-app'});
return b;
},
coffee: function(appDir) {
const b = browserify({
basedir: appDir,
extensions: ['.coffee'],
debug: true,
packageFilter,
});
b.transform('coffeeify');
b.require('./app.coffee', {expose: 'browser-app'});
return b;
},
};
describe('browser support', function() {
this.timeout(60000); // 60s to give browserify enough time to finish
beforeEach(sandbox.reset);
it('has API for bundling and executing boot instructions', function(done) {
var appDir = path.resolve(__dirname, './fixtures/browser-app');
const appDir = path.resolve(__dirname, './fixtures/browser-app');
browserifyTestApp(appDir, function(err, bundlePath) {
if (err) return done(err);
var app = executeBundledApp(bundlePath);
const app = executeBundledApp(bundlePath, function(err) {
if (err) return done(err);
// configured in fixtures/browser-app/boot/configure.js
expect(app.settings).to.have.property('custom-key', 'custom-value');
expect(Object.keys(app.models)).to.include('Customer');
expect(app.models.Customer.settings).to.have.property(
'_customized',
'Customer',
);
// configured in fixtures/browser-app/component-config.json
// and fixtures/browser-app/components/dummy-component.js
expect(app.dummyComponentOptions).to.eql({option: 'value'});
done();
});
});
});
it('loads mixins', function(done) {
const appDir = path.resolve(__dirname, './fixtures/browser-app');
const options = {
appRootDir: appDir,
};
browserifyTestApp(options, function(err, bundlePath) {
if (err) return done(err);
const app = executeBundledApp(bundlePath, function(err) {
const modelBuilder = app.registry.modelBuilder;
const registry = modelBuilder.mixins.mixins;
expect(Object.keys(registry)).to.eql(['TimeStamps']);
expect(app.models.Customer.timeStampsMixin).to.eql(true);
done();
});
});
});
function browserifyTestApp(appDir, next) {
var b = browserify({
basedir: appDir,
it('supports coffee-script files', function(done) {
// add coffee-script to require.extensions
require('coffeescript/register');
const appDir = path.resolve(__dirname, './fixtures/coffee-app');
browserifyTestApp(appDir, 'coffee', function(err, bundlePath) {
if (err) return done(err);
const app = executeBundledApp(bundlePath, function(err) {
// configured in fixtures/browser-app/boot/configure.coffee
expect(app.settings).to.have.property('custom-key', 'custom-value');
expect(Object.keys(app.models)).to.include('Customer');
expect(app.models.Customer.settings).to.have.property(
'_customized',
'Customer',
);
done();
});
});
});
});
b.require('./app.js', { expose: 'browser-app' });
boot.compileToBrowserify(appDir, b);
function browserifyTestApp(options, strategy, next) {
// set default args
if (typeof strategy === 'function' && !next) {
next = strategy;
strategy = undefined;
}
if (!strategy) strategy = 'default';
var bundlePath = sandbox.resolve('browser-app-bundle.js');
var out = fs.createWriteStream(bundlePath);
b.bundle({ debug: true }).pipe(out);
const appDir = typeof options === 'object' ? options.appRootDir : options;
const b = compileStrategies[strategy](appDir);
out.on('error', function(err) { return next(err); });
out.on('close', function() {
next(null, bundlePath);
boot.compileToBrowserify(options, b, function(err) {
exportBrowserifyToFile(b, 'browser-app-bundle.js', next);
});
}
function executeBundledApp(bundlePath) {
var code = fs.readFileSync(bundlePath);
var context = createBrowserLikeContext();
function executeBundledApp(bundlePath, done) {
const code = fs.readFileSync(bundlePath);
const context = createBrowserLikeContext();
vm.runInContext(code, context, bundlePath);
var app = vm.runInContext('require("browser-app")', context);
const app = vm.runInContext('require("browser-app")', context);
app.once('booted', function(err) {
printContextLogs(context);
done(err, app);
});
return app;
}
function createBrowserLikeContext() {
return vm.createContext({
// required by browserify
XMLHttpRequest: function() { throw new Error('not implemented'); },
// used by loopback to detect browser runtime
window: {},
// allow the browserified code to log messages
// call `printContextLogs(context)` to print the accumulated messages
console: {
log: function() {
this._logs.log.push(Array.prototype.slice.call(arguments));
},
warn: function() {
this._logs.warn.push(Array.prototype.slice.call(arguments));
},
error: function() {
this._logs.error.push(Array.prototype.slice.call(arguments));
},
_logs: {
log: [],
warn: [],
error: []
},
}
});
}
function printContextLogs(context) {
for (var k in context.console._logs) {
var items = context.console._logs[k];
for (var ix in items) {
console[k].apply(console, items[ix]);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

15
test/fixtures/browser-app-2/app.js vendored Normal file
View File

@ -0,0 +1,15 @@
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const loopback = require('loopback');
const boot = require('../../../');
const app = module.exports = loopback();
boot(app, {
appId: 'browserApp2',
appRootDir: __dirname,
});

View File

@ -0,0 +1,5 @@
{
"db": {
"connector": "remote"
}
}

View File

@ -0,0 +1,11 @@
{
"_meta": {
"sources": [
"./models",
"loopback/common/models"
]
},
"Robot": {
"dataSource": "db"
}
}

View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = function(Robot) {
Robot.settings._customized = 'Robot';
Robot.base.settings._customized = 'Robot';
};

View File

@ -0,0 +1,4 @@
{
"name": "Robot",
"base": "PersistedModel"
}

View File

@ -1,5 +1,12 @@
var loopback = require('loopback');
var boot = require('../../../');
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var app = module.exports = loopback();
boot(app);
'use strict';
const loopback = require('loopback');
const boot = require('../../../');
const app = module.exports = loopback();
boot(app, __dirname);

View File

@ -1,3 +1,10 @@
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = function(app) {
app.set('custom-key', 'custom-value');
};

View File

@ -0,0 +1,5 @@
{
"./components/dummy-component": {
"option": "value"
}
}

View File

@ -0,0 +1,10 @@
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = function(app, options) {
app.dummyComponentOptions = options;
};

View File

@ -0,0 +1,5 @@
{
"db": {
"connector": "remote"
}
}

View File

@ -0,0 +1,10 @@
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = function(Model, options) {
Model.timeStampsMixin = true;
};

View File

@ -0,0 +1,11 @@
{
"_meta": {
"sources": [
"./models",
"loopback/common/models"
]
},
"Customer": {
"dataSource": "db"
}
}

View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = function(Customer) {
Customer.settings._customized = 'Customer';
Customer.base.settings._customized = 'Base';
};

View File

@ -0,0 +1,5 @@
{
"name": "Customer",
"base": "User",
"mixins": {"TimeStamps": {} }
}

5
test/fixtures/coffee-app/app.coffee vendored Normal file
View File

@ -0,0 +1,5 @@
loopback = require 'loopback'
boot = require '../../../'
module.exports = client = loopback()
boot(client, __dirname)

View File

@ -0,0 +1,2 @@
module.exports = (app) ->
app.set 'custom-key', 'custom-value'

View File

@ -0,0 +1,5 @@
{
"db": {
"connector": "remote"
}
}

View File

@ -0,0 +1,5 @@
{
"Customer": {
"dataSource": "db"
}
}

View File

@ -0,0 +1,3 @@
module.exports = (Customer) ->
Customer.settings._customized = 'Customer'
Customer.base.settings._customized = 'Base'

View File

@ -0,0 +1,4 @@
{
"name": "Customer",
"base": "User"
}

2
test/fixtures/empty-app/config.json vendored Normal file
View File

@ -0,0 +1,2 @@
{
}

11
test/fixtures/env-app/boot/test/bar.js vendored Normal file
View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
process.bootFlags.push('barLoadedInTest');
module.exports = function(app, callback) {
callback();
};

View File

@ -1,4 +1,4 @@
{
"port": 3000,
"port": 0,
"host": "127.0.0.1"
}

View File

@ -0,0 +1,5 @@
{
"db": {
"connector": "memory"
}
}

View File

@ -1,5 +1,5 @@
{
"foo": {
"User": {
"dataSource": "db"
}
}

26
test/fixtures/passport.js vendored Normal file
View File

@ -0,0 +1,26 @@
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const framework = {
initialize: function(passport) {
return function(req, res, next) {
req._passport = passport;
res.setHeader('passport', 'initialized');
next();
};
},
};
const Passport = function() {
this._framework = framework;
};
Passport.prototype.initialize = function() {
return this._framework.initialize(this);
};
module.exports = new Passport();

15
test/fixtures/simple-app/boot/bar.js vendored Normal file
View File

@ -0,0 +1,15 @@
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
process.bootFlags.push('barLoaded');
module.exports = function(app, callback) {
process.bootFlags.push('barStarted');
process.nextTick(function() {
process.bootFlags.push('barFinished');
callback();
});
};

View File

@ -0,0 +1,11 @@
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
process.bootFlags.push('barSyncLoaded');
module.exports = function(app) {
process.bootFlags.push('barSyncExecuted');
};

View File

@ -0,0 +1,13 @@
// Copyright IBM Corp. 2015,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
module.exports = function(app, cb) {
if (app.booting)
process.bootingFlagSet = true;
process.nextTick(cb);
};

View File

@ -0,0 +1,6 @@
'use strict';
module.exports = function(app, callback) {
process.bootFlags.push('customjs');
callback();
};

View File

@ -0,0 +1,6 @@
'use strict';
module.exports = function(app, callback) {
process.bootFlags.push('customjs2');
callback();
};

View File

@ -1 +1,8 @@
process.loadedFooJS = true;
// Copyright IBM Corp. 2014,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
process.bootFlags.push('fooLoaded');

View File

@ -0,0 +1,15 @@
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const Promise = require('bluebird');
module.exports = function(app, callback) {
callback();
if (process.promiseAndCallback) {
return Promise.reject();
}
};

View File

@ -0,0 +1,21 @@
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const Promise = require('bluebird');
process.bootFlags.push('promiseLoaded');
module.exports = function(app) {
process.bootFlags.push('promiseStarted');
return Promise.resolve({
then: function(onFulfill, onReject) {
process.nextTick(function() {
process.bootFlags.push('promiseFinished');
onFulfill();
});
},
});
};

14
test/fixtures/simple-app/boot/reject.js vendored Normal file
View File

@ -0,0 +1,14 @@
// Copyright IBM Corp. 2017,2019. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
const Promise = require('bluebird');
module.exports = function(app) {
if (process.rejectPromise) {
return Promise.reject(new Error('reject'));
}
};

View File

@ -0,0 +1,19 @@
// Copyright IBM Corp. 2017. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
'use strict';
process.bootFlags.push('thenableLoaded');
module.exports = function(app) {
process.bootFlags.push('thenableStarted');
return {
then: function(onFulfill, onReject) {
process.nextTick(function() {
process.bootFlags.push('thenableFinished');
onFulfill();
});
},
};
};

Some files were not shown because too many files have changed in this diff Show More