Compare commits
388 Commits
Author | SHA1 | Date |
---|---|---|
|
c8241b2b35 | |
|
afe57b5b04 | |
|
56dcfaab6c | |
|
51ec554632 | |
|
f4313949c3 | |
|
98883a877f | |
|
7a6021dc77 | |
|
f9d6d147bc | |
|
298aec304a | |
|
0043b9e27d | |
|
3cbe0028a4 | |
|
e9b108f9c6 | |
|
0bbe8335a4 | |
|
c11ba18f73 | |
|
1fec4ea375 | |
|
974c63ccd5 | |
|
f6b2f24b29 | |
|
b4d1f96494 | |
|
6d8d9c92ce | |
|
14050c199f | |
|
59e46b3d91 | |
|
8bb4368ca8 | |
|
a1d2954626 | |
|
837b8185f2 | |
|
ef39abf6eb | |
|
8587dd6a19 | |
|
561cbd5bca | |
|
02da15cd3e | |
|
17ba74ed1c | |
|
e73b90ed97 | |
|
c10e8225bb | |
|
825411e6b4 | |
|
9b77d8159a | |
|
fd3a381289 | |
|
3e7031c82e | |
|
c278fe6705 | |
|
0ace54cd9e | |
|
bfa874de63 | |
|
b7a14e30e1 | |
|
2675771ec1 | |
|
14eeb4487d | |
|
e89e353ab0 | |
|
d598c3738a | |
|
c4ab39c4e0 | |
|
ded5bccb60 | |
|
473e685a69 | |
|
cdba5f0748 | |
|
47ba28898a | |
|
a035aaa3ae | |
|
c1cda7aefc | |
|
d7efabaebd | |
|
a9ae78ea54 | |
|
3277738937 | |
|
123f3da523 | |
|
e64adb92de | |
|
6e7809d3ef | |
|
a78a05f9b4 | |
|
42a7152474 | |
|
038bfeae43 | |
|
6e36f02005 | |
|
a054fb0064 | |
|
10ec2c5ed6 | |
|
3f42e66dea | |
|
2bf14c8cb4 | |
|
6e6ecb3ecc | |
|
0d216ac360 | |
|
96d59ce610 | |
|
756e3aedda | |
|
3ec9d3ab1c | |
|
92d6a1f91c | |
|
63a11502a6 | |
|
3bb519d5e9 | |
|
480380224d | |
|
d68ffc6f6f | |
|
aa321cf072 | |
|
f39fe30e47 | |
|
79d9ddb835 | |
|
ba688e0026 | |
|
f7c9cbc2c6 | |
|
ac1571ccf1 | |
|
314dff9f5f | |
|
ecc2d43957 | |
|
fbea19a002 | |
|
e96b08087d | |
|
a17c6c50e3 | |
|
6491cc8e71 | |
|
94aef17122 | |
|
295db6d873 | |
|
63cc0ecf7b | |
|
0d985bae0b | |
|
5da1420027 | |
|
57e5e64b4d | |
|
49ed10caaf | |
|
748a728a4f | |
|
58ef16993b | |
|
a6076ae8be | |
|
422fa6c11d | |
|
28d58ede8e | |
|
889a9fe275 | |
|
d2c55e79c5 | |
|
53112957d9 | |
|
09c3f8365b | |
|
e68502a4f5 | |
|
dcc575f90b | |
|
13a049412a | |
|
1121721afe | |
|
8fb9f751cd | |
|
1382e8af2f | |
|
129938bacd | |
|
eb49c7a386 | |
|
9b23e71501 | |
|
ce7fe3fb07 | |
|
c2484a6265 | |
|
296c38568c | |
|
24571328a8 | |
|
5f5e86e47f | |
|
7b226b212e | |
|
58d9322190 | |
|
5eaa909006 | |
|
defe5f4bb8 | |
|
d334425ada | |
|
51a699af01 | |
|
47974fd1d2 | |
|
1d2649eee7 | |
|
cc551b0c0f | |
|
707214cac4 | |
|
4a815deb27 | |
|
b3e5f23865 | |
|
902005ed8e | |
|
058e0e2f56 | |
|
90b9211fff | |
|
b0e5a0bc63 | |
|
e9afcaac70 | |
|
2e3b37a4f8 | |
|
c475e4af84 | |
|
d850560a18 | |
|
a5b888a719 | |
|
ab6a8ae776 | |
|
3836078985 | |
|
adeece3b30 | |
|
d8b298b53d | |
|
de6ebf65ee | |
|
4e3e1a7394 | |
|
2574c9dbb6 | |
|
088ca864c2 | |
|
5f0175afbf | |
|
a023e1f142 | |
|
c17efda0eb | |
|
3f55acf9d5 | |
|
1d27cf0e05 | |
|
24fbfbebf1 | |
|
638368844d | |
|
5238bc1027 | |
|
c8fdbd5110 | |
|
63589c03ab | |
|
fbafa2b608 | |
|
d249b15ce0 | |
|
d1b00ce5d8 | |
|
6ef37ef651 | |
|
819e1ea9c8 | |
|
a5bc1056aa | |
|
55c87796cf | |
|
7cbb8f6b40 | |
|
43042905af | |
|
f0aa0f8fdb | |
|
26a50137c7 | |
|
1b491dee85 | |
|
0b0f0ed202 | |
|
8b475a97b2 | |
|
75c945effa | |
|
f6dfbd2625 | |
|
674a8da693 | |
|
8864e2b394 | |
|
b081d828b2 | |
|
be3997de15 | |
|
32276c79d4 | |
|
7362cc489e | |
|
952c4101ef | |
|
e052da83db | |
|
450aa6dd21 | |
|
329fc59113 | |
|
02c826337c | |
|
ab723b5490 | |
|
41af081c55 | |
|
4f6e768426 | |
|
6980a250ef | |
|
ac5265f726 | |
|
66cc099d1d | |
|
0aac03ebb5 | |
|
5c8334c7c7 | |
|
a63ae8e44b | |
|
6c33a60bbc | |
|
a7d6cf695c | |
|
50d99c7e6b | |
|
bd4c2fc968 | |
|
2e63631b31 | |
|
a06aaba1cb | |
|
083a1265a4 | |
|
44f733f59f | |
|
140180f667 | |
|
53f51820f5 | |
|
2815a42d81 | |
|
43df90a4c8 | |
|
0563840006 | |
|
b6c60a297d | |
|
0e19f0a1b2 | |
|
9bb988713e | |
|
aed73a9e51 | |
|
c3bbc13fd8 | |
|
e5fd8c7975 | |
|
a2000829bc | |
|
acb3d1815f | |
|
e3763ac28e | |
|
f9d74f2c5e | |
|
6c0c8b9f8b | |
|
d9c0f12a66 | |
|
044a4df07a | |
|
80831cdaaf | |
|
1d8bd15a77 | |
|
2e9f001100 | |
|
acf1868ba4 | |
|
1b9f137062 | |
|
39d820a657 | |
|
ade8605618 | |
|
6526ffeb3d | |
|
626200a098 | |
|
07d276977a | |
|
d7c67c803a | |
|
3a7b9739d9 | |
|
416738b679 | |
|
311b892a0f | |
|
61798455f8 | |
|
187a105333 | |
|
c9f377a4c9 | |
|
4913b3ffb9 | |
|
cb7ca7d9ad | |
|
6e6eaccadd | |
|
df81d5487c | |
|
2c4b6f06a4 | |
|
bfcd3ff15e | |
|
872889423a | |
|
464c389af9 | |
|
276391811a | |
|
d041d80933 | |
|
e89a60c45c | |
|
ce58b7d65e | |
|
9b407b1ada | |
|
d8bf8687a8 | |
|
af1d0dcccb | |
|
a03e881f0a | |
|
15eb54ac85 | |
|
9dde8b0adf | |
|
6424534831 | |
|
215abf9d30 | |
|
77b83ce3fd | |
|
4f9f112d80 | |
|
548dba0c37 | |
|
a935669d9c | |
|
bb98472965 | |
|
d2c70bad3c | |
|
2a1f07aad9 | |
|
30a7b6d9b8 | |
|
fbf1e95e29 | |
|
ab33132ef7 | |
|
8bc526377f | |
|
146a3183eb | |
|
4f8514a454 | |
|
30ff50c581 | |
|
33b3729b62 | |
|
5fd79f0339 | |
|
c47bde9281 | |
|
8aa7156d3b | |
|
1f7d8e56e8 | |
|
f8247be23c | |
|
1ef2616979 | |
|
c4473951a0 | |
|
6a442f8307 | |
|
4031f6b985 | |
|
8a3e529ea3 | |
|
301031b78f | |
|
beb4f8c34c | |
|
4f65067348 | |
|
a90964ab30 | |
|
4b9e13bfc6 | |
|
e1f7c592a1 | |
|
8da44e69dd | |
|
77ded6edfe | |
|
e81ff119a3 | |
|
3dfa3bcb13 | |
|
8cc2518cb0 | |
|
81303b3093 | |
|
d3145f5745 | |
|
097fced310 | |
|
6de571f442 | |
|
2f72006c88 | |
|
d25c64523d | |
|
1114bc9227 | |
|
08fcc5faa7 | |
|
5b5071864b | |
|
83723379a2 | |
|
e2aff71bf9 | |
|
6040f66adc | |
|
9cd5d3d125 | |
|
f4c7b1ba38 | |
|
27bd48e0f6 | |
|
db917bf03b | |
|
ed59cb2483 | |
|
176f96e242 | |
|
f5ac5273a7 | |
|
84f8a51138 | |
|
d7bdbd31b1 | |
|
b480efde8a | |
|
e936deffe2 | |
|
3961f1c615 | |
|
56c74174f1 | |
|
5cea78c6aa | |
|
b5c585291b | |
|
aa4cbdd80f | |
|
26abb43ad4 | |
|
011296d825 | |
|
c04946073f | |
|
927bee82f7 | |
|
fadfaffe25 | |
|
676f347f36 | |
|
68593c8100 | |
|
e0abff007e | |
|
94cb4d6342 | |
|
38c4944e2e | |
|
0d4b5bb7c4 | |
|
d54e2b54d0 | |
|
18121a4208 | |
|
f0836719c9 | |
|
e1d870dced | |
|
abda37fee9 | |
|
ac73288cef | |
|
ed0880d00f | |
|
e974033395 | |
|
6c26e99ab9 | |
|
0169f22cd0 | |
|
34de593202 | |
|
93bfc3a63a | |
|
edb1f1fdce | |
|
20ed867d79 | |
|
1ae0167b7c | |
|
51cf0052d0 | |
|
217f0ce32f | |
|
f98d2cb89c | |
|
bc233e83d3 | |
|
4d55ab82aa | |
|
5471129f79 | |
|
e2092bba17 | |
|
c4b09c6b7a | |
|
b5989e907e | |
|
e42ee20d00 | |
|
be74e7c1ec | |
|
eac6cdf645 | |
|
6bf995c55b | |
|
cb67fc4165 | |
|
85c40c635a | |
|
9493aca34e | |
|
e7250b6eac | |
|
2bcf1f60aa | |
|
431bdc9b18 | |
|
53ca2f697a | |
|
cfe5f40808 | |
|
3129f6495c | |
|
3522f117c0 | |
|
25704cf69a | |
|
5e142c5188 | |
|
fc18561cc7 | |
|
5e23db50a5 | |
|
f222be477e | |
|
92455f569c | |
|
a3c347d073 | |
|
d5cd0a3b50 | |
|
2cc5a88699 | |
|
ac16d92a8b | |
|
0a0a6f5d01 | |
|
2eab6bf32a | |
|
57e96b0d38 | |
|
ef72efa70b | |
|
b887b33b57 | |
|
a9a401ad56 | |
|
e22ecd39ce | |
|
a204fdc1c9 | |
|
fccc6e147a | |
|
43993bf975 | |
|
47b5bb5f5c |
|
@ -1,3 +1,2 @@
|
|||
node_modules/
|
||||
coverage/
|
||||
test/sandbox/
|
|
@ -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_
|
|
@ -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.
|
|
@ -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/
|
||||
|
||||
-->
|
|
@ -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.
|
|
@ -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)
|
|
@ -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.
|
||||
|
|
@ -10,7 +10,10 @@
|
|||
*.pid
|
||||
*.swp
|
||||
*.swo
|
||||
*.iml
|
||||
node_modules
|
||||
generated-instructions*.json
|
||||
checkstyle.xml
|
||||
loopback-boot-*.tgz
|
||||
/test/sandbox/
|
||||
intl/zz/
|
||||
|
|
23
.jshintrc
23
.jshintrc
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
- "10"
|
||||
|
|
@ -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.
|
551
CHANGES.md
551
CHANGES.md
|
@ -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!
|
||||
|
|
|
@ -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
|
|
@ -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 project’s 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
299
LICENSE
|
@ -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
331
README.md
|
@ -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.
|
||||
|
|
34
browser.js
34
browser.js
|
@ -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;
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
{
|
||||
"content": [
|
||||
"README.md",
|
||||
{
|
||||
"title": "API",
|
||||
"depth": 2
|
||||
},
|
||||
"docs/header-node.md",
|
||||
"index.js",
|
||||
"docs/header-browser.md",
|
||||
"browser.js"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
```
|
|
@ -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.
|
|
@ -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);
|
||||
```
|
|
@ -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
180
index.js
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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}"
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
139
lib/bundler.js
139
lib/bundler.js
|
@ -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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
function bundleModelScripts(context, bundler) {
|
||||
bundleSourceFiles(context, 'models', bundler);
|
||||
}
|
||||
|
||||
// Add the file to the bundle.
|
||||
bundler.require(filepath, { expose: fileid });
|
||||
function bundleMixinScripts(context, bundler) {
|
||||
bundleSourceFiles(context, 'mixins', bundler);
|
||||
}
|
||||
|
||||
// Rewrite the instructions entry with the new id that will be
|
||||
// used to load the file via `require(fileid)`.
|
||||
list[ix] = fileid;
|
||||
}
|
||||
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.
|
||||
const fileid =
|
||||
'loopback-boot#' + name + '#' + path.relative(root, filepath);
|
||||
|
||||
// Add the file to the bundle.
|
||||
bundler.require(filepath, {expose: fileid});
|
||||
|
||||
// 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});
|
||||
}
|
||||
|
|
126
lib/compiler.js
126
lib/compiler.js
|
@ -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 [];
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
199
lib/executor.js
199
lib/executor.js
|
@ -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;
|
||||
}
|
|
@ -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();
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
});
|
||||
};
|
|
@ -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();
|
||||
});
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
};
|
||||
|
|
@ -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);
|
||||
});
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
48
package.json
48
package.json
|
@ -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."
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
}
|
|
@ -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/boot/configure.js
|
||||
expect(app.settings).to.have.property('custom-key', 'custom-value');
|
||||
// configured in fixtures/browser-app/component-config.json
|
||||
// and fixtures/browser-app/components/dummy-component.js
|
||||
expect(app.dummyComponentOptions).to.eql({option: 'value'});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function browserifyTestApp(appDir, next) {
|
||||
var b = browserify({
|
||||
basedir: appDir,
|
||||
});
|
||||
b.require('./app.js', { expose: 'browser-app' });
|
||||
function browserifyTestApp(options, strategy, next) {
|
||||
// set default args
|
||||
if (typeof strategy === 'function' && !next) {
|
||||
next = strategy;
|
||||
strategy = undefined;
|
||||
}
|
||||
if (!strategy) strategy = 'default';
|
||||
|
||||
boot.compileToBrowserify(appDir, b);
|
||||
const appDir = typeof options === 'object' ? options.appRootDir : options;
|
||||
const b = compileStrategies[strategy](appDir);
|
||||
|
||||
var bundlePath = sandbox.resolve('browser-app-bundle.js');
|
||||
var out = fs.createWriteStream(bundlePath);
|
||||
b.bundle({ debug: true }).pipe(out);
|
||||
|
||||
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);
|
||||
|
||||
printContextLogs(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
|
@ -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,
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"db": {
|
||||
"connector": "remote"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"_meta": {
|
||||
"sources": [
|
||||
"./models",
|
||||
"loopback/common/models"
|
||||
]
|
||||
},
|
||||
"Robot": {
|
||||
"dataSource": "db"
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
};
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "Robot",
|
||||
"base": "PersistedModel"
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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');
|
||||
};
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"./components/dummy-component": {
|
||||
"option": "value"
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"db": {
|
||||
"connector": "remote"
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"_meta": {
|
||||
"sources": [
|
||||
"./models",
|
||||
"loopback/common/models"
|
||||
]
|
||||
},
|
||||
"Customer": {
|
||||
"dataSource": "db"
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Customer",
|
||||
"base": "User",
|
||||
"mixins": {"TimeStamps": {} }
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
loopback = require 'loopback'
|
||||
boot = require '../../../'
|
||||
|
||||
module.exports = client = loopback()
|
||||
boot(client, __dirname)
|
|
@ -0,0 +1,2 @@
|
|||
module.exports = (app) ->
|
||||
app.set 'custom-key', 'custom-value'
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"db": {
|
||||
"connector": "remote"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"Customer": {
|
||||
"dataSource": "db"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = (Customer) ->
|
||||
Customer.settings._customized = 'Customer'
|
||||
Customer.base.settings._customized = 'Base'
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "Customer",
|
||||
"base": "User"
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
}
|
|
@ -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();
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"port": 3000,
|
||||
"port": 0,
|
||||
"host": "127.0.0.1"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"db": {
|
||||
"connector": "memory"
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"foo": {
|
||||
"User": {
|
||||
"dataSource": "db"
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -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();
|
||||
});
|
||||
};
|
|
@ -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');
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function(app, callback) {
|
||||
process.bootFlags.push('customjs');
|
||||
callback();
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = function(app, callback) {
|
||||
process.bootFlags.push('customjs2');
|
||||
callback();
|
||||
};
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
};
|
|
@ -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();
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
|
@ -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'));
|
||||
}
|
||||
};
|
|
@ -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
Loading…
Reference in New Issue