mirror of
https://github.com/bower/bower.git
synced 2026-04-24 03:00:19 -04:00
Compare commits
2 Commits
update-req
...
feature/de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
447c2e3c04 | ||
|
|
f535bd126a |
@@ -1,46 +0,0 @@
|
||||
# http://www.appveyor.com/docs/appveyor-yml
|
||||
|
||||
# Set build version format here instead of in the admin panel.
|
||||
version: "{build}"
|
||||
|
||||
# Fix line endings in Windows. (runs before repo cloning)
|
||||
init:
|
||||
- git config --global core.autocrlf input
|
||||
|
||||
# Test against these versions of Node.js.
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "0.12"
|
||||
- nodejs_version: "4"
|
||||
- nodejs_version: "6"
|
||||
- nodejs_version: "8"
|
||||
- nodejs_version: "9"
|
||||
|
||||
# Finish on first failed build
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
# Install node, display versions, install dependencies
|
||||
install:
|
||||
- ps: Install-Product node 8
|
||||
- node --version && npm --version
|
||||
- git --version && svn --version
|
||||
- npm install -g yarn grunt
|
||||
- yarn
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
|
||||
# Post-install test scripts.
|
||||
test_script:
|
||||
- cmd: npm run ci
|
||||
|
||||
# Make clone much faster
|
||||
shallow_clone: true
|
||||
|
||||
# Disable Visual Studio build and deploy
|
||||
build: off
|
||||
deploy: off
|
||||
|
||||
# Cache node modules, and refresh if package.json changes
|
||||
cache:
|
||||
- "%LOCALAPPDATA%\\Yarn"
|
||||
@@ -13,6 +13,3 @@ trim_trailing_whitespace = false
|
||||
|
||||
[**.std]
|
||||
insert_final_newline = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_size = 2
|
||||
|
||||
52
.eslintrc
52
.eslintrc
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"rules": {
|
||||
"no-bitwise": 0,
|
||||
"curly": 0,
|
||||
"eqeqeq": 0,
|
||||
"guard-for-in": 0,
|
||||
"no-use-before-define": 0,
|
||||
"no-caller": 2,
|
||||
"no-new": 2,
|
||||
"no-plusplus": 0,
|
||||
"no-undef": 2,
|
||||
"no-unused-vars": 0,
|
||||
"strict": 0,
|
||||
"semi": 0,
|
||||
"comma-spacing": 2,
|
||||
"quote-props": [2, "consistent", { "keywords": true }],
|
||||
"quotes": [2, "single", "avoid-escape"],
|
||||
"indent": [2, 4],
|
||||
"no-cond-assign": [ 2, "except-parens" ],
|
||||
"no-debugger": 2,
|
||||
"no-dupe-args": 2,
|
||||
"no-dupe-keys": 2,
|
||||
"no-duplicate-case": 2,
|
||||
"no-unreachable": 2,
|
||||
"valid-typeof": 2,
|
||||
"no-fallthrough": 2,
|
||||
"no-ex-assign": 2,
|
||||
"no-eq-null": 0,
|
||||
"no-eval": 0,
|
||||
"no-unused-expressions": 0,
|
||||
"block-scoped-var": 0,
|
||||
"no-iterator": 0,
|
||||
"no-loop-func": 2,
|
||||
"no-script-url": 0,
|
||||
"no-shadow": 0,
|
||||
"no-new-func": 2,
|
||||
"no-new-wrappers": 2,
|
||||
"no-invalid-this": 0,
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
|
||||
"space-infix-ops": 2,
|
||||
"keyword-spacing": 2,
|
||||
"new-parens": 2,
|
||||
"no-multiple-empty-lines": [2, { max: 2}],
|
||||
"eol-last": 2,
|
||||
"no-trailing-spaces": 2
|
||||
}
|
||||
}
|
||||
44
.github/ISSUE_TEMPLATE.md
vendored
44
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,44 +0,0 @@
|
||||
<!--
|
||||
|
||||
If you are reporting a new issue, make sure that we do not have any duplicates.
|
||||
You can ensure this by searching the issue list for this repository.
|
||||
|
||||
You are welcome to open issues to discuss important general topics concerning Bower.
|
||||
However for support questions, please consider using http://stackoverflow.com or
|
||||
asking for help in our Discord channel: https://discordapp.com/invite/0fFM7QF0KpZaDeN9
|
||||
|
||||
# BUG REPORT
|
||||
|
||||
Use the commands below to provide key information to reproduce:
|
||||
You do NOT have to include this information if this is a FEATURE REQUEST OR DISCUSSION
|
||||
|
||||
For more information about reporting bugs, see:
|
||||
https://github.com/bower/bower/wiki/Report-a-Bug
|
||||
|
||||
-->
|
||||
|
||||
**Output of `bower -v && npm -v && node -v`:**
|
||||
|
||||
```
|
||||
(paste your output here)
|
||||
```
|
||||
|
||||
**Additional environment details (proxy, private registry, etc.):**
|
||||
|
||||
|
||||
|
||||
**Steps to reproduce the issue:**
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
**Describe the results you received:**
|
||||
|
||||
|
||||
|
||||
**Describe the results you expected:**
|
||||
|
||||
|
||||
|
||||
**Additional information:**
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,14 +1,12 @@
|
||||
/node_modules
|
||||
/npm-debug.log
|
||||
|
||||
/test/assets/temp
|
||||
/test/assets/temp2
|
||||
/test/assets/temp-resolve-cache
|
||||
/test/assets/package-*/
|
||||
/test/assets/temp-*/
|
||||
/test/reports
|
||||
/test/tmp/
|
||||
|
||||
/bower.json
|
||||
/component.json
|
||||
/bower_components
|
||||
/test/sample
|
||||
!/test/sample/bower.json
|
||||
/npm-shrinkwrap.json
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"unused": "vars",
|
||||
"quotmark": "single",
|
||||
"strict": false,
|
||||
"trailing": true,
|
||||
44
.travis.yml
44
.travis.yml
@@ -1,43 +1,9 @@
|
||||
sudo: false
|
||||
|
||||
language: node_js
|
||||
|
||||
# Use node 8 for build
|
||||
node_js:
|
||||
- "8"
|
||||
|
||||
# Then test with specific node version
|
||||
env:
|
||||
- TEST_NODE_VERSION="0.10"
|
||||
- TEST_NODE_VERSION="0.12"
|
||||
- TEST_NODE_VERSION="4"
|
||||
- TEST_NODE_VERSION="6"
|
||||
- TEST_NODE_VERSION="8"
|
||||
- TEST_NODE_VERSION="9"
|
||||
|
||||
before_install:
|
||||
- node --version
|
||||
- curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.5.1
|
||||
- export PATH=$HOME/.yarn/bin:$PATH
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
|
||||
install:
|
||||
- yarn
|
||||
- nvm install $TEST_NODE_VERSION
|
||||
- npm install -g grunt
|
||||
|
||||
os:
|
||||
- osx
|
||||
- linux
|
||||
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- os: osx
|
||||
|
||||
script:
|
||||
- nvm use $TEST_NODE_VERSION
|
||||
- node --version && npm --version && git --version && svn --version | head -n 1
|
||||
- grunt travis
|
||||
- node_js: "0.11"
|
||||
before_script:
|
||||
- npm install grunt-cli -g
|
||||
|
||||
293
CHANGELOG.md
293
CHANGELOG.md
@@ -1,306 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 1.8.0 - 2016-11-07
|
||||
|
||||
- Download tar archives from GitHub when possible (#2263)
|
||||
- Change default shorthand resolver for github from `git://` to `https://`
|
||||
- Fix ssl handling by not setting GIT_SSL_NO_VERIFY=false (#2361)
|
||||
- Allow for removing components with url instead of name (#2368)
|
||||
- Show in warning message location of malformed bower.json (#2357)
|
||||
- Improve handling of non-semver versions in git resolver (#2316)
|
||||
- Fix handling of cached releases pluginResolverFactory (#2356)
|
||||
- Allow to type the entire version when conflict occured (#2243)
|
||||
- Allow `owner/reponame` shorthand for registering components (#2248)
|
||||
- Allow single-char repo names and package names (#2249)
|
||||
- Make `bower version` no longer honor `version` in bower.json (#2232)
|
||||
- Add `postinstall` hook (#2252)
|
||||
- Allow for `@` instead of `#` for `install` and `info` commands (#2322)
|
||||
- Upgrade all bundled modules
|
||||
|
||||
## 1.7.9 - 2016-04-05
|
||||
|
||||
- Show warnings for invalid bower.json fields
|
||||
- Update bower-json
|
||||
- Less strict validation on package name (allow spaces, slashes, and "@")
|
||||
|
||||
## 1.7.8 - 2016-04-04
|
||||
|
||||
- Don't ask for git credentials in non-interactive session, fixes #956 #1009
|
||||
- Prevent swallowing exceptions with programmatic api, fixes #2187
|
||||
- Update graceful-fs to 4.x in all dependences, fixes nodejs/node#5213
|
||||
- Resolve pluggable resolvers using cwd and fallback to global modules, fixes #1919
|
||||
- Upgrade handlebars to 4.0.5, closes #2195
|
||||
- Replace all % chatacters in defined scripts, instead of only first one, fixes #2174
|
||||
- Update opn package to fix issues with "bower open" command on Windows
|
||||
- Update bower-config
|
||||
- Do not interpolate environment variables in script hooks, fixes bower/config#47
|
||||
- Update bower-json
|
||||
- Validate package name more strictly and allow only latin letters, dots, dashes and underscores
|
||||
- Add support for "save" and "save-exact" in .bowerrc, #2161
|
||||
|
||||
## 1.7.7 - 2016-01-27
|
||||
|
||||
Revert locations of all files while still packaging `node_modules`.
|
||||
|
||||
It's because people are depending on internals of bower, like
|
||||
`bower/lib/renderers/StandardRenderer`. We want to preserve this
|
||||
implicit contract, but we discourage it. The only official way
|
||||
to use bower programmatically is through `require('bower')`.
|
||||
|
||||
## 1.7.6 - 2016-01-27
|
||||
|
||||
- Revert location of "bin/bower" as developers are using it directly ([#2157](https://github.com/bower/bower/issues/2157))
|
||||
Note: Correctly, you should use an alias created in `npm bin --global`.
|
||||
|
||||
## 1.7.5 - 2016-01-26
|
||||
|
||||
- Remove analytics from Bower, fixes ([#2150](https://github.com/bower/bower/pull/2150))
|
||||
- Default to ^ operator on `bower install --save` ([#2145](https://github.com/bower/bower/pull/2145))
|
||||
- Support absolute path in .bowerrc directory option ([#2130](https://github.com/bower/bower/pull/2130))
|
||||
- Display user's name upon `bower login` command ([#2133](https://github.com/bower/bower/pull/2133))
|
||||
- Decompress gzip files ([#2092](https://github.com/bower/bower/pull/2092))
|
||||
- Prevent name clashes in package extraction ([#2102](https://github.com/bower/bower/pull/2102))
|
||||
- When strictSsl is false, set GIT_SSL_NO_VERIFY=true ([#2129](https://github.com/bower/bower/issues/2129))
|
||||
- Distribute bower with npm@3 for better Windows support ([#2146](https://github.com/bower/bower/issues/2146))
|
||||
- Update request to 2.67.0 and fs-write-stream-atomic to 1.0.8
|
||||
- Documentation improvements
|
||||
|
||||
## 1.7.4 - 2016-01-21
|
||||
|
||||
Unpublished because of issue with npm distribution:
|
||||
https://github.com/npm/npm/issues/11227
|
||||
|
||||
## 1.7.3 - 2016-01-20
|
||||
|
||||
Unpublished because of issue with npm distribution:
|
||||
https://github.com/npm/npm/issues/11227
|
||||
|
||||
## 1.7.2 - 2015-12-31
|
||||
|
||||
- Lock "fs-write-stream-atomic" to 1.0.5
|
||||
|
||||
## 1.7.1 - 2015-12-11
|
||||
|
||||
- Rollback "Add `bower update --save` functionality", it causes issues and needs more testing
|
||||
- Fix backward-compatibility of `bower search --json` ([#2066](https://github.com/bower/bower/issues/2066))
|
||||
- Ignore prerelease versions from `bower info` output
|
||||
- Update update-notifier to 0.6.0
|
||||
- Better formatting of help messages (https://github.com/bower/bower/commit/de3e1089da80f47ea3667c5ab80d301cddfd8c3e)
|
||||
- Add help menu for update `--save` and `update --save-dev` (https://github.com/bower/bower/commit/612aaa88eb4d4b268b2d8665c338ac086af3a5b0)
|
||||
|
||||
## 1.7.0 - 2015-12-07
|
||||
|
||||
- Add `bower update --save` functionality ([#2035](https://github.com/bower/bower/issues/2035))
|
||||
- `bower search` shows help message when no package name is specified ([#2066](https://github.com/bower/bower/issues/2066))
|
||||
- Update only those packages that are explicitly requested by the user. Related Issues
|
||||
- [#256](https://github.com/bower/bower/issues/256)
|
||||
- [#924](https://github.com/bower/bower/issues/924)
|
||||
- [#1770](https://github.com/bower/bower/issues/1770)
|
||||
- Allow for @ in username for SVN on windows ([#1650](https://github.com/bower/bower/issues/1650))
|
||||
- Update bower config
|
||||
- Loads the .bowerrc file from the cwd specified on the command line
|
||||
- Allow the use of environment variables in .bowerrc ([#41](https://github.com/bower/config/issues/41))
|
||||
- Allow for array notation in ENV variables ([#44](https://github.com/bower/config/issues/44))
|
||||
|
||||
## 1.6.9 - 2015-12-04
|
||||
|
||||
- Change git version of fs-write-stream-atomic back to npm version ([#2079](https://github.com/bower/bower/issues/2079))
|
||||
|
||||
## 1.6.8 - 2015-11-27
|
||||
|
||||
- Use fs-write-stream-atomic for downloads
|
||||
- Improved downloader that properly cleans after itself
|
||||
- Fix shallow host detection ([#2040](https://github.com/bower/bower/pull/2040))
|
||||
- Upgrade to ([bower-config#1.2.3](https://github.com/bower/config/releases/tag/1.2.3))
|
||||
- Properly restore env variables if they are undefined at the beginning
|
||||
- Properly handle `default` setting for config.ca
|
||||
- Display proper error if .bowerrc is a directory instead of file
|
||||
|
||||
## 1.6.7 - 2015-11-26
|
||||
|
||||
- Bundless all the dependencies again
|
||||
|
||||
## 1.6.6 - 2015-11-25
|
||||
|
||||
- Fixes regression with the published npm version
|
||||
|
||||
## 1.6.5 - 2015-10-24
|
||||
|
||||
- Updates to tests and documentation
|
||||
- Fixes passing options when requesting downloads
|
||||
|
||||
## 1.6.4 - 2015-10-24
|
||||
|
||||
- Fix ignoring dependencies on multiple install run ([#1970](https://github.com/bower/bower/pull/1970))
|
||||
- Use --non-interactive when running svn client ([#1969](https://github.com/bower/bower/pull/1969))
|
||||
- Fix downloading of URLs ending with slash ([#1956](https://github.com/bower/bower/pull/1956))
|
||||
- Add user-agent field for downloads by Bower ([#1960](https://github.com/bower/bower/pull/1960))
|
||||
|
||||
## 1.6.3 - 2015-10-16
|
||||
|
||||
Fixes regression issues introduced with 1.6.2, specifically:
|
||||
|
||||
- Allow for bower_components to be a symlink
|
||||
- Allow setting custom registry in .bowerrc
|
||||
|
||||
## 1.6.2 - 2015-10-15
|
||||
|
||||
Fix dependency issues of 1.6.1. First published release of 1.6.x.
|
||||
|
||||
## 1.6.1 - 2015-10-15
|
||||
|
||||
Fix dependency issues of 1.6.0. Reverted release.
|
||||
|
||||
## 1.6.0 - 2015-10-15
|
||||
|
||||
- Shrinkwrap all dependencies and add them to bundledDependencies ([#1948](https://github.com/bower/bower/pull/1948))
|
||||
- Allow for ignoring of child dependencies ([#1394](https://github.com/bower/bower/pull/1394))
|
||||
- Allow passing `--config.resolvers` through CLI ([#1922](https://github.com/bower/bower/pull/1922))
|
||||
- Use defaults values from package.json if it exists (bower init) ([#1731](https://github.com/bower/bower/issues/1731))
|
||||
- Properly use cerificates set in .bowerrc ([#1869](https://github.com/bower/bower/pull/1869))
|
||||
- Include package name when version conflict occurs ([#1917](https://github.com/bower/bower/pull/1917))
|
||||
- Add timeout for permission check ([yeoman/insight#35](https://github.com/yeoman/insight/pull/35))
|
||||
- Close file-handles when possible. Prevents all sorts of permission issues on Windows ([0bb1536](https://github.com/bower/bower/commit/0bb1536c9972e13f3be06bea9a8619632966c664))
|
||||
- Prevent ENOENT error on Windows when in VM environment ([isaacs/chmodr#8](https://github.com/isaacs/chmodr/pull/8))
|
||||
|
||||
Reverted release.
|
||||
|
||||
## 1.5.4 - 2015-11-24
|
||||
|
||||
- [fix] Lock lru-cache dependency to 2.7.0
|
||||
|
||||
## 1.5.3 - 2015-09-24
|
||||
|
||||
- Revert auto sorting of bower dependencies, fixes ([#1897](https://github.com/bower/bower/issues/1897))
|
||||
- Fix --save-exact feature for github endpoints, fixes ([#1925](https://github.com/bower/bower/issues/1925))
|
||||
- Fix `bower init` to support private flag again ([#1819](https://github.com/bower/bower/pull/1819))
|
||||
- Bump insight dependency to support prompt timeout ([#1102](https://github.com/bower/bower/issues/1102))
|
||||
|
||||
## 1.5.2 - 2015-08-25
|
||||
|
||||
- Revert update semver version from 2.x to 5.x, fixes ([#1896](https://github.com/bower/bower/issues/1896))
|
||||
- Make bower commands work from subdirectories, fixes ([#1893](https://github.com/bower/bower/issues/1893))
|
||||
- Put auto shallow cloning for git behind a flag, fixes ([#1764](https://github.com/bower/bower/issues/1764))
|
||||
|
||||
## 1.5.1 - 2015-08-24
|
||||
|
||||
- If cwd provided explicitly, force using it, fixes #1866
|
||||
|
||||
## 1.5.0 - 2015-08-24
|
||||
|
||||
- Pluggable Resolvers! http://bower.io/docs/pluggable-resolvers/
|
||||
- Update semver version from 2.x to 5.x ([#1852](https://github.com/bower/bower/issues/1852))
|
||||
- Auto-sort dependencies alphabetically ([#1381](https://github.com/bower/bower/issues/1381))
|
||||
- Make bower commands work from subdirectories ([#1866](https://github.com/bower/bower/issues/1866))
|
||||
- No longer prefer installing bower as global module ([#1865](https://github.com/bower/bower/issues/1865))
|
||||
|
||||
## 1.4.2 - 2015-11-24
|
||||
|
||||
- [fix] Lock lru-cache dependency to 2.7.0
|
||||
|
||||
## 1.4.1 - 2015-04-01
|
||||
|
||||
- [fix] Reading .bowerrc upwards directory tree ([#1763](https://github.com/bower/bower/issues/1763))
|
||||
- [fix] Update bower-registry-client so it uses the same bower-config as bower
|
||||
|
||||
## 1.4.0 - 2015-03-30
|
||||
|
||||
- Add login and unregister commands ([#1719](https://github.com/bower/bower/issues/1719))
|
||||
- Automatically detecting smart Git hosts ([#1628](https://github.com/bower/bower/issues/1628))
|
||||
- [bower/config#23] Allow npm config variables ([#1711](https://github.com/bower/bower/issues/1711))
|
||||
- [bower/config#24] Merge .bowerrc files upwards directory tree ([#1689](https://github.com/bower/bower/issues/1689))
|
||||
- Better homedir detection (514eb8f)
|
||||
- Add --save-exact flag ([#1654](https://github.com/bower/bower/issues/1654))
|
||||
- Ensure extracted files are readable (tar-fs) ([#1548](https://github.com/bower/bower/issues/1548))
|
||||
- The version command in the programmatic API now returns the new version ([#1755](https://github.com/bower/bower/issues/1755))
|
||||
- Some minor fixes: #1639, #1620, #1576, #1557, 962a565, a464f5a
|
||||
- Improved Windows support (AppVeyor CI, tests actually passing on Windows)
|
||||
- OSX testing enabled on TravisCI
|
||||
|
||||
It also includes improved test coverage (~60% -> ~85%) and many refactors.
|
||||
|
||||
## 1.3.12 - 2014-09-28
|
||||
|
||||
- [stability] Fix versions for unstable dependencies ([#1532](https://github.com/bower/bower/pull/1532))
|
||||
- [fix] Update tar-fs to support old tar format ([#1537](https://github.com/bower/bower/issues/1537))
|
||||
- [fix] Make analytics work again ([#1529](https://github.com/bower/bower/pull/1529))
|
||||
- [fix] Always disable analytics for non-interactive mode ([#1529](https://github.com/bower/bower/pull/1529))
|
||||
- [fix] Bower init can create private packages again ([#1522](https://github.com/bower/bower/issues/1522))
|
||||
- [fix] Show again missing newline for bower search output ([#1538](https://github.com/bower/bower/issues/1538))
|
||||
|
||||
## 1.3.11 - 2014-09-17
|
||||
|
||||
- [fix] Restore install missing dependencies on update ([1519](https://github.com/bower/bower/pull/1519))
|
||||
|
||||
## 1.3.10 - 2014-09-13
|
||||
|
||||
- [fix] Back down concurrency from 50 to 5 ([#1483](https://github.com/bower/bower/pull/1483))
|
||||
- [fix] Read .bowerrc from specified cwd ([#1301](https://github.com/bower/bower/pull/1301))
|
||||
- [fix] Disable shallow clones except those from GitHub ([#1393](https://github.com/bower/bower/pull/1393))
|
||||
- [fix] Expose bower version ([#1478](https://github.com/bower/bower/pull/1478))
|
||||
- [fix] Bump dependencies, including "request" ([#1467](https://github.com/bower/bower/pull/1467))
|
||||
- [fix] Prevent an error when piping bower output to head ([#1508](https://github.com/bower/bower/pull/1508))
|
||||
- [fix] Disable removing unnecessary resolutions ([#1061](https://github.com/bower/bower/pull/1061))
|
||||
- [fix] Display the output of hooks again ([#1484](https://github.com/bower/bower/issues/1484))
|
||||
- [fix] analytics: true in .bowerrc prevents user prompt ([#1470](https://github.com/bower/bower/pull/1470))
|
||||
- [perf] Use `tar-fs` instead of `tar` for faster TAR extraction ([#1490](https://github.com/bower/bower/pull/1490))
|
||||
|
||||
## 1.3.9 - 2014-08-06
|
||||
|
||||
- [fix] Handle `tmp` sometimes returning an array ([#1434](https://github.com/bower/bower/pull/1434))
|
||||
|
||||
## 1.3.8 - 2014-7-11
|
||||
|
||||
- [fix] Lock down `tmp` package dep ([#1403](https://github.com/bower/bower/pull/1403), [#1407](https://github.com/bower/bower/pull/1407))
|
||||
|
||||
## 1.3.7 - 2014-07-04
|
||||
|
||||
- [fix] callstack error when processing installed packages with circular dependencies ([#1349](https://github.com/bower/bower/issues/1349))
|
||||
- [fix] Prevent bower list --paths` failing with TypeError ([#1383](https://github.com/bower/bower/issues/1383))
|
||||
- "bower install" fails if there's no bower.json in current directory ([#922](https://github.com/bower/bower/issues/922))
|
||||
|
||||
## 1.3.6 - 2014-07-02
|
||||
|
||||
- [fix] Make --force always re-run installation ([#931](https://github.com/bower/bower/issues/931))
|
||||
- [fix] Disable caching for local resources ([#1356](https://github.com/bower/bower/issues/1356))
|
||||
- [fix] Emit errors instead throwing them when using bower.commands API ([#1297](https://github.com/bower/bower/issues/1297))
|
||||
- [fix] Main files and bower.json are never ignored ([#547](https://github.com/bower/bower/issues/547))
|
||||
- [fix] Check if pkgMeta is undefined during uninstall command ([#1329](https://github.com/bower/bower/issues/1329))
|
||||
- [fix] Make custom tmp dir and ignores play well with each other ([#1299](https://github.com/bower/bower/issues/1299))
|
||||
- Warn users when installing package with missing properties ([#694](https://github.com/bower/bower/issues/694))
|
||||
|
||||
|
||||
## 1.3.5 - 2014-06-06
|
||||
- Search compatible versions in fetching packages ([#1147](https://github.com/bower/bower/issues/1147))
|
||||
|
||||
## 1.3.4 - 2014-06-02
|
||||
|
||||
- Resolve a situation in which the install process gets into an infinite loop ([#1169](https://github.com/bower/bower/issues/1169))
|
||||
- Improved CLI output for conflicts ([#1284](https://github.com/bower/bower/issues/1284))
|
||||
- Changed `bower version` to mirror the tag format of `npm version` ([#1278](https://github.com/bower/bower/issues/1278))
|
||||
- Allow short commit SHAs to be used ([#990](https://github.com/bower/bower/issues/990))
|
||||
|
||||
## 1.3.3 - 2014-04-24
|
||||
|
||||
- Do not cache moving targets like branches ([#1242](https://github.com/bower/bower/issues/1242))
|
||||
- Suppress output if --quiet option is specified ([#1124](https://github.com/bower/bower/pull/1124))
|
||||
- Use "svn export" for efficiency ([#1224](https://github.com/bower/bower/pull/1224))
|
||||
- Prevent loading insights and analytics on CI ([#1221](https://github.com/bower/bower/issues/1221))
|
||||
- Make "bower list" respect custom components directory ([#1237](https://github.com/bower/bower/issues/1237))
|
||||
- Improve non-interactive loading performance 2x ([#1238](https://github.com/bower/bower/issues/1238))
|
||||
- Load commands only on demand, improving performance ([#1232](https://github.com/bower/bower/pull/1232))
|
||||
|
||||
## 1.3.2 - 2014-04-05
|
||||
|
||||
- Added yui moduleType [PR #1129](https://github.com/bower/bower/pull/1129)
|
||||
- Fixes for concurrency issues [PR #1211](https://github.com/bower/bower/pull/1211)
|
||||
- `link` now installs package dependencies [PR #891](https://github.com/bower/bower/pull/891)
|
||||
- Improved conflict installation message [Commit](https://github.com/bower/bower/commit/bea533acf87903d4b411bfbaa7df93f852ef46a3)
|
||||
- Add --production switch to "prune" command [PR #1168](https://github.com/bower/bower/pull/1168)
|
||||
|
||||
|
||||
## 1.3.1 - 2014-03-10
|
||||
|
||||
- No longer ask for permission to gather analytics when running on in a CI environment.
|
||||
|
||||
|
||||
|
||||
@@ -1,43 +1,96 @@
|
||||
# Contributing to Bower
|
||||
|
||||
Bower is a large community project with many different developers contributing at all levels to the project. We're **actively** looking for more contributors right now. If you're interested in becoming a Bower maintainer or supporting in any way, please fill the following form: http://goo.gl/forms/P1ndzCNoiG. There is more information about [contributing](https://github.com/bower/bower/wiki/Contributor-Guidelines) in the Wiki.
|
||||
|
||||
<a name="bugs"></a>
|
||||
## 🐛 [Bug reports](https://github.com/bower/bower/wiki/Report-a-Bug)
|
||||
Bower is a large community project with many different developers contributing at all levels to the project. We're **actively** looking for more contributors right now. (Jan 2014)
|
||||
|
||||
## Casual Involvement
|
||||
|
||||
* Improve the bower.io site ([tickets](https://github.com/bower/bower.github.io/issues))
|
||||
* Move forward [bower.io redesign](https://github.com/bower/bower.github.io/issues/7)
|
||||
* Attend team meetings
|
||||
* Comment on issues and drive to resolution
|
||||
|
||||
## High-impact Involvement
|
||||
|
||||
* Maintaining the bower client.
|
||||
* Maintaining the bower client.
|
||||
* [Authoring client tests](https://github.com/bower/bower/issues/801)
|
||||
* Read [Architecture doc](https://github.com/bower/bower/wiki/Rewrite-architecture)
|
||||
* Triage, close, fix and resolve [issues](https://github.com/bower/bower/issues)
|
||||
* Developing the [new registry server](https://github.com/bower/registry/tree/node_rewrite)
|
||||
* Hooking in to Elastic Search rather than the in-memory search
|
||||
* Getting bower/registry-client to talk to the new server without breaking backwards compatibility
|
||||
* DevOps for the server
|
||||
|
||||
## Team Meetings
|
||||
|
||||
We communicate through a channel on Discord https://discord.gg/0fFM7QF0KpZRh2cY
|
||||
We meet on Monday at 1:00pm PST, 9:00pm UTC in #bower on Freenode. [The meeting notes](http://goo.gl/NJZ1o2).
|
||||
|
||||
<hr>
|
||||
|
||||
Following these guidelines helps to communicate that you respect the time of
|
||||
the developers managing and developing this open source project. In return,
|
||||
they should reciprocate that respect in addressing your issue, assessing
|
||||
changes, and helping you finalize your pull requests.
|
||||
|
||||
If you'd like to attend the meetings, please fill the [support form](http://goo.gl/forms/P1ndzCNoiG), and you'll get an invite.
|
||||
|
||||
## Using the issue tracker
|
||||
|
||||
*
|
||||
|
||||
The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
[features requests](#features) and [submitting pull
|
||||
requests](#pull-requests), but please respect the following restrictions:
|
||||
|
||||
* Please **do not** use the issue tracker for personal support requests. Use
|
||||
[Stack Overflow](http://stackoverflow.com/questions/tagged/bower),
|
||||
[Discord Channel](https://discordapp.com/channels/119103197720739842/123728452816732160),
|
||||
[Mailing List](http://groups.google.com/group/twitter-bower),
|
||||
[Stack Overflow](http://stackoverflow.com/questions/tagged/bower), our
|
||||
[Mailing List](http://groups.google.com/group/twitter-bower)
|
||||
(twitter-bower@googlegroups.com), or
|
||||
[#bower](http://webchat.freenode.net/?channels=bower) on Freenode.
|
||||
|
||||
* Please **do not** derail or troll issues. Keep the discussion on topic and
|
||||
respect the opinions of others.
|
||||
|
||||
|
||||
<a name="bugs"></a>
|
||||
## Bug reports
|
||||
|
||||
A bug is a _demonstrable problem_ that is caused by the code in the repository.
|
||||
Good bug reports are extremely helpful - thank you!
|
||||
|
||||
Guidelines for bug reports:
|
||||
|
||||
1. **Use the GitHub issue search** — check if the issue has already been
|
||||
reported.
|
||||
|
||||
2. **Check if the issue has been fixed** — try to reproduce it using the
|
||||
latest `master` or development branch in the repository.
|
||||
|
||||
3. **Isolate the problem** — ideally create a [reduced test
|
||||
case](http://css-tricks.com/6263-reduced-test-cases/).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report. What is
|
||||
your environment? What steps will reproduce the issue? What OS experiences the
|
||||
problem? What would you expect to be the outcome? All these details will help
|
||||
people to fix any potential bugs.
|
||||
|
||||
Example:
|
||||
|
||||
> Short and descriptive example bug report title
|
||||
>
|
||||
> A summary of the issue and the browser/OS environment in which it occurs. If
|
||||
> suitable, include the steps required to reproduce the bug.
|
||||
>
|
||||
> 1. This is the first step
|
||||
> 2. This is the second step
|
||||
> 3. Further steps, etc.
|
||||
>
|
||||
> `<url>` - a link to the reduced test case
|
||||
>
|
||||
> Any other information you want to share that is relevant to the issue being
|
||||
> reported. This might include the lines of code that you have identified as
|
||||
> causing the bug, and potential solutions (and your opinions on their
|
||||
> merits).
|
||||
|
||||
|
||||
<a name="features"></a>
|
||||
## Feature requests
|
||||
|
||||
@@ -121,8 +174,6 @@ included in the project:
|
||||
force push to your remote feature branch. You may also be asked to squash
|
||||
commits.
|
||||
|
||||
10. If you are asked to squash your commits, then please use `git rebase -i master`. It will ask you to pick your commits - pick the major commits and squash the rest.
|
||||
|
||||
**IMPORTANT**: By submitting a patch, you agree to license your work under the
|
||||
same license as that used by the project.
|
||||
|
||||
|
||||
192
Gruntfile.js
192
Gruntfile.js
@@ -1,40 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
var tmp = require('tmp');
|
||||
var childProcess = require('child_process');
|
||||
var arraydiff = require('arr-diff');
|
||||
var fs = require('fs');
|
||||
var wrench = require('wrench');
|
||||
var inquirer = require('inquirer');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.initConfig({
|
||||
eslint: {
|
||||
jshint: {
|
||||
options: {
|
||||
fix: true
|
||||
jshintrc: '.jshintrc'
|
||||
},
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
'bin/*',
|
||||
'lib/**/*.js',
|
||||
'test/**/*.js',
|
||||
'!test/assets/**/*',
|
||||
'!test/reports/**/*',
|
||||
'!test/sample/**/*',
|
||||
'!test/tmp/**/*'
|
||||
]
|
||||
files: ['Gruntfile.js', 'bin/*', 'lib/**/*.js', 'test/**/*.js', '!test/assets/**/*', '!test/reports/**/*']
|
||||
},
|
||||
simplemocha: {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
timeout: '15000'
|
||||
},
|
||||
full: {
|
||||
src: ['test/test.js']
|
||||
timeout: '5000'
|
||||
},
|
||||
full: { src: ['test/test.js'] },
|
||||
short: {
|
||||
options: {
|
||||
reporter: 'dot'
|
||||
@@ -43,171 +23,25 @@ module.exports = function (grunt) {
|
||||
}
|
||||
},
|
||||
exec: {
|
||||
'assets': {
|
||||
assets: {
|
||||
command: 'node test/packages.js && node test/packages-svn.js'
|
||||
},
|
||||
'assets-force': {
|
||||
command: 'node test/packages.js --force && node test/packages-svn.js --force'
|
||||
},
|
||||
'cover': {
|
||||
command: 'node node_modules/istanbul/lib/cli.js cover --dir ./test/reports node_modules/mocha/bin/_mocha -- --timeout 30000 -R dot test/test.js'
|
||||
},
|
||||
'coveralls': {
|
||||
command: 'npm run coveralls < test/reports/lcov.info',
|
||||
exitCodes: [0, 1, 2, 3] // Alow for failure for coverage report
|
||||
cover: {
|
||||
command: 'node node_modules/istanbul/lib/cli.js cover --dir ./test/reports node_modules/mocha/bin/_mocha -- -R dot test/test.js'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: ['<%= eslint.files %>'],
|
||||
tasks: ['eslint', 'simplemocha:short']
|
||||
files: ['<%= jshint.files %>'],
|
||||
tasks: ['jshint', 'simplemocha:short']
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
grunt.registerTask('assets', ['exec:assets-force']);
|
||||
grunt.registerTask('test', ['eslint', 'exec:assets', 'simplemocha:full']);
|
||||
grunt.registerTask('test', ['jshint', 'exec:assets', 'simplemocha:full']);
|
||||
grunt.registerTask('cover', 'exec:cover');
|
||||
grunt.registerTask('travis', ['eslint', 'exec:assets', 'exec:cover', 'exec:coveralls']);
|
||||
grunt.registerTask('default', 'test');
|
||||
|
||||
grunt.task.registerTask('publish', 'Perform final checks and publish Bower', function () {
|
||||
var npmVersion = JSON.parse(childProcess.execSync('npm version --json').toString()).npm.split('.');
|
||||
var npmMajor = parseInt(npmVersion[0], 10);
|
||||
var npmMinor = parseInt(npmVersion[1], 10);
|
||||
|
||||
var jsonPackage = require('./package');
|
||||
|
||||
if (npmMajor !== 3 || npmMinor < 5) {
|
||||
grunt.log.writeln('You need to use at least npm@3.5 to publish bower.');
|
||||
grunt.log.writeln('It is because npm 2.x produces too long paths that Windows does not handle.');
|
||||
grunt.log.writeln('Please upgrade it: npm install -g npm');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var version = jsonPackage.version;
|
||||
var changelog = fs.readFileSync('./CHANGELOG.md');
|
||||
|
||||
if (changelog.indexOf('## ' + version) === -1) {
|
||||
grunt.log.writeln('Please add changelog.md entry for this bower version (' + version + ')');
|
||||
|
||||
var lastRelease = childProcess.execSync('git tag | tail -1').toString().trim();
|
||||
|
||||
grunt.log.writeln('Commits since last release (' + lastRelease + '): \n');
|
||||
|
||||
grunt.log.writeln(childProcess.execSync('git log --oneline ' + lastRelease + '..').toString());
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (childProcess.execSync('git rev-parse --abbrev-ref HEAD').toString().trim() !== 'master') {
|
||||
grunt.log.writeln('You need to release bower from the "master" branch');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (process.env.SKIP_TESTS !== '1') {
|
||||
grunt.log.writeln('Reinstalling dependencies...');
|
||||
childProcess.execSync('rm -rf node_modules && npm install', { stdio: [0, 1, 2] });
|
||||
|
||||
grunt.log.writeln('Running test suite...');
|
||||
childProcess.execSync('grunt test', { stdio: [0, 1, 2] });
|
||||
}
|
||||
|
||||
var dir = tmp.dirSync().name;
|
||||
|
||||
wrench.copyDirSyncRecursive(__dirname, dir, {
|
||||
forceDelete: true,
|
||||
include: function (path) {
|
||||
return !path.match(/node_modules|\.git|test/);
|
||||
}
|
||||
});
|
||||
|
||||
grunt.log.writeln('Installing production dependencies...');
|
||||
childProcess.execSync('npm install --production --silent', { cwd: dir, stdio: [0, 1, 2] });
|
||||
|
||||
delete jsonPackage.dependencies;
|
||||
delete jsonPackage.devDependencies;
|
||||
delete jsonPackage.scripts;
|
||||
|
||||
fs.writeFileSync(path.resolve(dir, 'package.json'), JSON.stringify(jsonPackage, null, ' ') + '\n');
|
||||
|
||||
|
||||
grunt.log.writeln('Moving node_modules to lib directory...');
|
||||
|
||||
wrench.copyDirSyncRecursive(path.resolve(dir, 'node_modules'), path.resolve(dir, 'lib', 'node_modules'));
|
||||
wrench.rmdirSyncRecursive(path.resolve(dir, 'node_modules'));
|
||||
|
||||
grunt.log.writeln('Testing bower on sample project...');
|
||||
|
||||
childProcess.execSync(
|
||||
'cd test/sample && rm -rf bower_components && ' + dir + '/bin/bower install --force', { stdio: [0, 1, 2] }
|
||||
);
|
||||
|
||||
var expectedPackages = (
|
||||
'SHA-1 ace-builds almond angular angular-animate angular-bootstrap angular-charts angular-contenteditable ' +
|
||||
'angular-deckgrid angular-fullscreen angular-gravatar angular-hotkeys angular-local-storage angular-marked ' +
|
||||
'angular-moment angular-sanitize angular-touch angular-ui-router angular-ui-sortable ' +
|
||||
'angulartics asEvented bootstrap coffee-script d3 es6-shim font-awesome howler jquery ' +
|
||||
'jquery-ui jquery-waypoints js-beautify lodash lz-string marked moment ng-file-upload peerjs ' +
|
||||
'requirejs restangular slimScroll slimScrollHorizontal venturocket-angular-slider'
|
||||
).split(' ');
|
||||
|
||||
var installedPackages = fs.readdirSync('./test/sample/bower_components');
|
||||
|
||||
var installedDiff = arraydiff(expectedPackages, installedPackages);
|
||||
|
||||
if (installedDiff.length > 0) {
|
||||
grunt.log.writeln('ERROR. Some packages were not installed by bower: ');
|
||||
grunt.log.writeln(installedDiff.join(', '));
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
grunt.log.writeln('\nBower production bundle installed in:');
|
||||
grunt.log.writeln(dir + '\n');
|
||||
|
||||
var questions = [
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'review',
|
||||
message: 'Did you review all the changes with "git diff"?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'changelog',
|
||||
message: 'Are you sure the CHANGELOG.md contains all changes?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'tests',
|
||||
message: 'Are you sure all tests are passing on Travis and Appveyor?',
|
||||
default: false
|
||||
},
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'publish',
|
||||
message: 'Are you SURE you want to publish ' + jsonPackage.name + '@' + jsonPackage.version + '?',
|
||||
default: false
|
||||
}
|
||||
];
|
||||
|
||||
var done = this.async();
|
||||
|
||||
inquirer.prompt(questions, function (answers) {
|
||||
if (!answers.review || !answers.changelog || !answers.tests || !answers.publish) {
|
||||
grunt.log.writeln('Please publish bower after you fix this issue');
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
grunt.log.writeln('\nPlease remember to tag this relese, and add a release on Github!');
|
||||
grunt.log.writeln('\nAlso, please remember to test published Bower one more time!');
|
||||
grunt.log.writeln('\nPublishing Bower...');
|
||||
|
||||
childProcess.execSync('npm publish --tag beta', { cwd: dir, stdio: [0, 1, 2] });
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
21
HOOKS.md
Normal file
21
HOOKS.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Install and Uninstall Hooks
|
||||
|
||||
Bower provides 3 separate hooks that can be used to trigger other automated tools during Bower usage. Importantly, these hooks are intended to allow external tools to help wire up the newly installed components into the parent project and other similar tasks. These hooks are not intended to provide a post-installation build step for component authors. As such, the configuration for these hooks is provided in the `.bowerrc` file in the parent project's directory.
|
||||
|
||||
## Configuring
|
||||
|
||||
In `.bowerrc` do:
|
||||
|
||||
```js
|
||||
{
|
||||
"scripts": {
|
||||
"preinstall": "<your command here>",
|
||||
"postinstall": "<your command here>",
|
||||
"preuninstall": "<your command here>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The value of each script hook may contain a % character. When your script is called, the % will be replaced with a space-separated list of components being installed or uninstalled.
|
||||
|
||||
Your script will also include an environment variable `BOWER_PID` containing the PID of the parent Bower process that triggered the script. This can be used to verify that a `preinstall` and `postinstall` steps are part of the same Bower process.
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2013-present Twitter and other contributors
|
||||
Copyright (c) 2014 Twitter and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
|
||||
472
README.md
472
README.md
@@ -1,200 +1,424 @@
|
||||
# Bower - A package manager for the web
|
||||
# Bower
|
||||
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
|
||||
> ..psst! While Bower is maintained, we recommend [yarn](https://yarnpkg.com/) and [webpack](https://webpack.js.org/) for new front-end projects!
|
||||
|
||||
[](https://travis-ci.org/bower/bower)
|
||||
[](https://ci.appveyor.com/project/bower/bower)
|
||||
[](https://coveralls.io/r/bower/bower?branch=master)
|
||||
[](https://discord.gg/0fFM7QF0KpZRh2cY)
|
||||
[](http://travis-ci.org/bower/bower)
|
||||
|
||||
<img align="right" height="300" src="http://bower.io/img/bower-logo.png">
|
||||
|
||||
---
|
||||
Bower is a package manager for the web. It offers a generic, unopinionated
|
||||
solution to the problem of **front-end package management**, while exposing the
|
||||
package dependency model via an API that can be consumed by a more opinionated
|
||||
build stack. There are no system wide dependencies, no dependencies are shared
|
||||
between different apps, and the dependency tree is flat.
|
||||
|
||||
Bower offers a generic, unopinionated solution to the problem of **front-end package management**, while exposing the package dependency model via an API that can be consumed by a more opinionated build stack. There are no system wide dependencies, no dependencies are shared between different apps, and the dependency tree is flat.
|
||||
|
||||
Bower runs over Git, and is package-agnostic. A packaged component can be made up of any type of asset, and use any type of transport (e.g., AMD, CommonJS, etc.).
|
||||
|
||||
**View complete docs on [bower.io](http://bower.io)**
|
||||
Bower runs over Git, and is package-agnostic. A packaged component can be made
|
||||
up of any type of asset, and use any type of transport (e.g., AMD, CommonJS,
|
||||
etc.).
|
||||
|
||||
[View all packages available through Bower's registry](http://bower.io/search/).
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
$ npm install -g bower
|
||||
## Installing Bower
|
||||
|
||||
Bower depends on [Node](http://nodejs.org/) and [npm](http://npmjs.org/). It's
|
||||
installed globally using npm:
|
||||
|
||||
```
|
||||
npm install -g bower
|
||||
```
|
||||
|
||||
Bower depends on [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/). Also make sure that [git](http://git-scm.com/) is installed as some bower
|
||||
Also make sure that [git](http://git-scm.com/) is installed as some bower
|
||||
packages require it to be fetched and installed.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
See complete command line reference at [bower.io/docs/api/](http://bower.io/docs/api/)
|
||||
Much more information is available via `bower help` once it's installed. This
|
||||
is just enough to get you started.
|
||||
|
||||
### Installing packages and dependencies
|
||||
|
||||
```sh
|
||||
# install dependencies listed in bower.json
|
||||
$ bower install
|
||||
Bower offers several ways to install packages:
|
||||
|
||||
# install a package and add it to bower.json
|
||||
$ bower install <package> --save
|
||||
|
||||
# install specific version of a package and add it to bower.json
|
||||
$ bower install <package>#<version> --save
|
||||
#####Using the dependencies listed in the current directory's bower.json
|
||||
```
|
||||
bower install
|
||||
```
|
||||
##### Using a local or remote package
|
||||
```
|
||||
bower install <package>
|
||||
```
|
||||
##### Using a specific version of a package
|
||||
```
|
||||
bower install <package>#<version>
|
||||
```
|
||||
##### Using a different name and a specific version of a package
|
||||
```
|
||||
bower install <name>=<package>#<version>
|
||||
```
|
||||
|
||||
Where `<package>` can be any one of the following:
|
||||
|
||||
* A name that maps to a package registered with Bower, e.g, `jquery`. ‡
|
||||
* A public remote Git endpoint, e.g., ```git://github.com/someone/some-package.git```. ‡
|
||||
* A private Git repository, e.g., ```https://github.com/someone/some-package.git```. If the protocol is https, a prompt will ask for the credentials. ssh can also be used, e.g., ```git@github.com:someone/some-package.git``` and can authenticate with the user's ssh public/private keys. ‡
|
||||
* A local endpoint, i.e., a folder that's a Git repository. ‡
|
||||
* A public remote Subversion endpoint, e.g., ```svn+http://package.googlecode.com/svn/```. ‡
|
||||
* A private Subversion repository, e.g., ```svn+ssh://package.googlecode.com/svn/``` or ```svn+https://package.googlecode.com/svn/```. ‡
|
||||
* A local endpoint, i.e., a folder that's an Subversion repository, e.g., ```svn+file:///path/to/svn/```. ‡
|
||||
* A shorthand endpoint, e.g., `someone/some-package` (defaults to GitHub). ‡
|
||||
* A URL to a file, including `zip` and `tar` files. Its contents will be
|
||||
extracted.
|
||||
|
||||
‡ These types of `<package>` might have versions available. You can specify a
|
||||
[semver](http://semver.org/) compatible version to fetch a specific release, and lock the
|
||||
package to that version. You can also specify a [range](https://github.com/isaacs/node-semver#ranges) of versions.
|
||||
|
||||
If you are using a package that is a git endpoint, you may use any tag, commit SHA,
|
||||
or branch name as a version. For example: `<package>#<sha>`. Using branches is not
|
||||
recommended because the HEAD does not reference a fixed commit SHA.
|
||||
|
||||
If you are using a package that is a subversion endpoint, you may use any tag, revision number,
|
||||
or branch name as a version. For example: `<package>#<revision>`.
|
||||
|
||||
All package contents are installed in the `bower_components` directory by default.
|
||||
You should **never** directly modify the contents of this directory.
|
||||
|
||||
Using `bower list` will show all the packages that are installed locally.
|
||||
|
||||
**N.B.** If you aren't authoring a package that is intended to be consumed by
|
||||
others (e.g., you're building a web app), you should always [check installed
|
||||
packages into source control](http://addyosmani.com/blog/checking-in-front-end-dependencies/).
|
||||
|
||||
|
||||
### Custom install directory
|
||||
|
||||
A custom install location can be set in a `.bowerrc` file using the `directory` property. The .bowerrc file should be a sibling of your project's bower.json.
|
||||
|
||||
```json
|
||||
{
|
||||
"directory": "public/bower_components"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Finding packages
|
||||
|
||||
To search for packages registered with Bower:
|
||||
|
||||
```
|
||||
bower search [<name>]
|
||||
```
|
||||
|
||||
Using just `bower search` will list all packages in the registry.
|
||||
|
||||
### Using packages
|
||||
|
||||
We discourage using bower components statically for performance and security reasons (if component has an `upload.php` file that is not ignored, that can be easily exploited to do malicious stuff).
|
||||
The easiest approach is to use Bower statically, just reference the package's
|
||||
installed components manually using a `script` tag:
|
||||
|
||||
The best approach is to process components installed by bower with build tool (like [Grunt](http://gruntjs.com/) or [gulp](http://gulpjs.com/)), and serve them concatenated or using a module loader (like [RequireJS](http://requirejs.org/)).
|
||||
```html
|
||||
<script src="/bower_components/jquery/jquery.js"></script>
|
||||
```
|
||||
|
||||
For more complex projects, you'll probably want to concatenate your scripts or
|
||||
use a module loader. Bower is just a package manager, but there are plenty of
|
||||
other tools -- such as [Sprockets](https://github.com/sstephenson/sprockets)
|
||||
and [RequireJS](http://requirejs.org/) -- that will help you do this.
|
||||
|
||||
### Uninstalling packages
|
||||
|
||||
To uninstall a locally installed package:
|
||||
|
||||
```sh
|
||||
$ bower uninstall <package-name>
|
||||
```
|
||||
bower uninstall <package-name>
|
||||
```
|
||||
|
||||
### prezto and oh-my-zsh users
|
||||
|
||||
#### Warning
|
||||
|
||||
On `prezto` or `oh-my-zsh`, do not forget to `alias bower='noglob bower'` or `bower install jquery\#1.9.1`
|
||||
|
||||
### Never run Bower with sudo
|
||||
#### Running commands with sudo
|
||||
|
||||
Bower is a user command; there is no need to execute it with superuser permissions.
|
||||
Bower is a user command, there is no need to execute it with superuser permissions.
|
||||
However, if you still want to run commands with sudo, use `--allow-root` option.
|
||||
|
||||
### Windows users
|
||||
#### A note for Windows users
|
||||
|
||||
To use Bower on Windows, you must install
|
||||
[Git for Windows](http://git-for-windows.github.io/) correctly. Be sure to check the
|
||||
options shown below:
|
||||
[msysgit](http://msysgit.github.io/) correctly. Be sure to check the
|
||||
option shown below:
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/10702007/10532690/d2e8991a-7386-11e5-9a57-613c7f92e84e.png" width="534" height="418" alt="Git for Windows" />
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/10702007/10532694/dbe8857a-7386-11e5-9bd0-367e97644403.png" width="534" height="418" alt="Git for Windows" />
|
||||

|
||||
|
||||
Note that if you use TortoiseGit and if Bower keeps asking for your SSH
|
||||
password, you should add the following environment variable: `GIT_SSH -
|
||||
C:\Program Files\TortoiseGit\bin\TortoisePlink.exe`. Adjust the `TortoisePlink`
|
||||
path if needed.
|
||||
|
||||
### Ubuntu users
|
||||
|
||||
To use Bower on Ubuntu, you might need to link `nodejs` executable to `node`:
|
||||
### Using bower's cache
|
||||
|
||||
Bower supports installing packages from its local cache (without internet connection), if the packages were installed before.
|
||||
```
|
||||
sudo ln -s /usr/bin/nodejs /usr/bin/node
|
||||
bower install <package-name> --offline
|
||||
```
|
||||
The content of the cache can be listed with:
|
||||
```
|
||||
bower cache list
|
||||
```
|
||||
The cache can be cleaned with:
|
||||
```
|
||||
bower cache clean
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Bower can be configured using JSON in a `.bowerrc` file. Read over available options at [bower.io/docs/config](http://bower.io/docs/config).
|
||||
Bower can be configured using JSON in a `.bowerrc` file.
|
||||
|
||||
The current spec can be read
|
||||
[here](https://docs.google.com/document/d/1APq7oA9tNao1UYWyOm8dKqlRP2blVkROYLZ2fLIjtWc/edit#heading=h.4pzytc1f9j8k)
|
||||
in the `Configuration` section.
|
||||
|
||||
## Running on a continuous integration server
|
||||
|
||||
Bower will skip some interactive and analytics operations if it finds a `CI` environmental variable set to `true`. You will find that the `CI` variable is already set for you on many continuous integration servers, e.g., [CircleCI](https://circleci.com/docs/environment-variables#basics) and [Travis-CI](http://docs.travis-ci.com/user/ci-environment/#Environment-variables).
|
||||
|
||||
You may try to set manually set `CI` variable manually before running your Bower commands. On Mac or Linux, `export CI=true` and on Windows `set CI=true`
|
||||
|
||||
### Interactive configuration
|
||||
|
||||
If for some reason you are unable to set the `CI` environment variable, you can alternately use the `--config.interactive=false` flag. (`bower install --config.interactive=false`)
|
||||
|
||||
## Defining a package
|
||||
|
||||
You must create a `bower.json` in your project's root, and specify all of its
|
||||
dependencies. This is similar to Node's `package.json`, or Ruby's `Gemfile`,
|
||||
and is useful for locking down a project's dependencies.
|
||||
|
||||
*NOTE:* In versions of Bower before 0.9.0 the package metadata file was called
|
||||
`component.json` rather than `bower.json`. This has changed to avoid a name
|
||||
clash with another tool. You can still use `component.json` for now but it is
|
||||
deprecated and the automatic fallback is likely to be removed in an upcoming
|
||||
release.
|
||||
|
||||
You can interactively create a `bower.json` with the following command:
|
||||
|
||||
```
|
||||
bower init
|
||||
```
|
||||
|
||||
The `bower.json` ([spec](https://github.com/bower/bower.json-spec)) defines several options, including:
|
||||
|
||||
* `name` (required): The name of your package.
|
||||
* `version`: A semantic version number (see [semver](http://semver.org/)).
|
||||
* `main` [string|array]: The primary endpoints of your package.
|
||||
* `ignore` [array]: An array of paths not needed in production that you want
|
||||
Bower to ignore when installing your package.
|
||||
* `dependencies` [hash]: Packages your package depends upon in production.
|
||||
Note that you can specify [ranges](https://github.com/isaacs/node-semver#ranges)
|
||||
of versions for your dependencies.
|
||||
* `devDependencies` [hash]: Development dependencies.
|
||||
* `private` [boolean]: Set to true if you want to keep the package private and
|
||||
do not want to register the package in future.
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "my-project",
|
||||
"description": "My project does XYZ...",
|
||||
"version": "1.0.0",
|
||||
"main": "path/to/main.css",
|
||||
"ignore": [
|
||||
".jshintrc",
|
||||
"**/*.txt"
|
||||
],
|
||||
"dependencies": {
|
||||
"<name>": "<version>",
|
||||
"<name>": "<folder>",
|
||||
"<name>": "<package>"
|
||||
},
|
||||
"devDependencies": {
|
||||
"<test-framework-name>": "<version>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Registering packages
|
||||
|
||||
To register a new package:
|
||||
|
||||
* There **must** be a valid manifest JSON in the current working directory.
|
||||
* Your package should use [semver](http://semver.org/) Git tags.
|
||||
* Your package **must** be available at a Git endpoint (e.g., GitHub); remember
|
||||
to push your Git tags!
|
||||
|
||||
Then use the following command:
|
||||
|
||||
```
|
||||
bower register <my-package-name> <git-endpoint>
|
||||
```
|
||||
|
||||
The Bower registry does not have authentication or user management at this point
|
||||
in time. It's on a first come, first served basis. Think of it like a URL
|
||||
shortener. Now anyone can run `bower install <my-package-name>`, and get your
|
||||
library installed.
|
||||
|
||||
There is no direct way to unregister a package yet. For now, you can [request a
|
||||
package be unregistered](https://github.com/bower/bower/issues/120).
|
||||
|
||||
|
||||
## Support
|
||||
## Consuming a package
|
||||
|
||||
Bower also makes available a source mapping. This can be used by build tools to
|
||||
easily consume Bower packages.
|
||||
|
||||
If you pass the `--paths` option to Bower's `list` command, you will get a
|
||||
simple name-to-path mapping:
|
||||
|
||||
```json
|
||||
{
|
||||
"backbone": "bower_components/backbone/index.js",
|
||||
"jquery": "bower_components/jquery/index.js",
|
||||
"underscore": "bower_components/underscore/index.js"
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, every command supports the `--json` option that makes bower
|
||||
output JSON. Command result is outputted to `stdout` and error/logs to
|
||||
`stderr`.
|
||||
|
||||
|
||||
## Programmatic API
|
||||
|
||||
Bower provides a powerful, programmatic API. All commands can be accessed
|
||||
through the `bower.commands` object.
|
||||
|
||||
```js
|
||||
var bower = require('bower');
|
||||
|
||||
bower.commands
|
||||
.install(['jquery'], { save: true }, { /* custom config */ })
|
||||
.on('end', function (installed) {
|
||||
console.log(installed);
|
||||
});
|
||||
|
||||
bower.commands
|
||||
.search('jquery', {})
|
||||
.on('end', function (results) {
|
||||
console.log(results);
|
||||
});
|
||||
```
|
||||
|
||||
Commands emit four types of events: `log`, `prompt`, `end`, `error`.
|
||||
|
||||
* `log` is emitted to report the state/progress of the command.
|
||||
* `prompt` is emitted whenever the user needs to be prompted.
|
||||
* `error` will only be emitted if something goes wrong.
|
||||
* `end` is emitted when the command successfully ends.
|
||||
|
||||
For a better of idea how this works, you may want to check out [our bin
|
||||
file](https://github.com/bower/bower/blob/master/bin/bower).
|
||||
|
||||
When using bower programmatically, prompting is disabled by default. Though you can enable it when calling commands with `interactive: true` in the config.
|
||||
This requires you to listen for the `prompt` event and handle the prompting yourself. The easiest way is to use the [inquirer](https://npmjs.org/package/inquirer) npm module like so:
|
||||
|
||||
```js
|
||||
var inquirer = require('inquirer');
|
||||
|
||||
bower.commands
|
||||
.install(['jquery'], { save: true }, { interactive: true })
|
||||
// ..
|
||||
.on('prompt', function (prompts, callback) {
|
||||
inquirer.prompt(prompts, callback);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
## Completion (experimental)
|
||||
|
||||
_NOTE_: Completion is still not implemented for the 1.0.0 release
|
||||
|
||||
Bower now has an experimental `completion` command that is based on, and works
|
||||
similarly to the [npm completion](https://npmjs.org/doc/completion.html). It is
|
||||
not available for Windows users.
|
||||
|
||||
This command will output a Bash / ZSH script to put into your `~/.bashrc`,
|
||||
`~/.bash_profile`, or `~/.zshrc` file.
|
||||
|
||||
```
|
||||
bower completion >> ~/.bash_profile
|
||||
```
|
||||
|
||||
|
||||
## Contact
|
||||
|
||||
Have a question?
|
||||
|
||||
* [Discord chat](https://discord.gg/0fFM7QF0KpZRh2cY)
|
||||
* [StackOverflow](http://stackoverflow.com/questions/tagged/bower)
|
||||
* [Mailinglist](http://groups.google.com/group/twitter-bower) - twitter-bower@googlegroups.com
|
||||
* [\#bower](http://webchat.freenode.net/?channels=bower) on Freenode
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome [contributions](https://github.com/bower/bower/graphs/contributors) of all kinds from anyone. Please take a moment to review the [guidelines for contributing](CONTRIBUTING.md).
|
||||
## Contributing to this project
|
||||
|
||||
* [Bug reports](https://github.com/bower/bower/wiki/Report-a-Bug)
|
||||
Anyone and everyone is welcome to contribute. Please take a moment to
|
||||
review the [guidelines for contributing](CONTRIBUTING.md).
|
||||
|
||||
* [Bug reports](CONTRIBUTING.md#bugs)
|
||||
* [Feature requests](CONTRIBUTING.md#features)
|
||||
* [Pull requests](CONTRIBUTING.md#pull-requests)
|
||||
|
||||
|
||||
Note that on Windows for tests to pass you need to configure Git before cloning:
|
||||
## Bower Team
|
||||
|
||||
```
|
||||
git config --global core.autocrlf input
|
||||
```
|
||||
### Core team
|
||||
|
||||
* [@satazor](https://github.com/satazor)
|
||||
* [@wibblymat](https://github.com/wibblymat)
|
||||
* [@paulirish](https://github.com/paulirish)
|
||||
* [@benschwarz](https://github.com/benschwarz)
|
||||
* [@sindresorhus](https://github.com/sindresorhus)
|
||||
* [@svnlto](https://github.com/svnlto)
|
||||
|
||||
## Backers
|
||||
Thanks for assistance and contributions:
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/bower#backer)]
|
||||
[@addyosmani](https://github.com/addyosmani),
|
||||
[@ahmadnassri](https://github.com/ahmadnassri),
|
||||
[@angus-c](https://github.com/angus-c),
|
||||
[@borismus](https://github.com/borismus),
|
||||
[@carsonmcdonald](https://github.com/carsonmcdonald),
|
||||
[@chriseppstein](https://github.com/chriseppstein),
|
||||
[@danwrong](https://github.com/danwrong),
|
||||
[@davidmaxwaterman](https://github.com/davidmaxwaterman),
|
||||
[@desandro](https://github.com/desandro),
|
||||
[@hemanth](https://github.com/hemanth),
|
||||
[@isaacs](https://github.com/isaacs),
|
||||
[@josh](https://github.com/josh),
|
||||
[@jrburke](https://github.com/jrburke),
|
||||
[@kennethklee](https://github.com/kennethklee),
|
||||
[@marcelombc](https://github.com/marcelombc),
|
||||
[@marcooliveira](https://github.com/marcooliveira),
|
||||
[@mklabs](https://github.com/mklabs),
|
||||
[@MrDHat](https://github.com/MrDHat),
|
||||
[@necolas](https://github.com/necolas),
|
||||
[@richo](https://github.com/richo),
|
||||
[@rvagg](https://github.com/rvagg),
|
||||
[@ryanflorence](https://github.com/ryanflorence),
|
||||
[@SlexAxton](https://github.com/SlexAxton),
|
||||
[@sstephenson](https://github.com/sstephenson),
|
||||
[@tomdale](https://github.com/tomdale),
|
||||
[@uzquiano](https://github.com/uzquiano),
|
||||
[@visionmedia](https://github.com/visionmedia),
|
||||
[@wagenet](https://github.com/wagenet),
|
||||
[@wycats](https://github.com/wycats)
|
||||
|
||||
<a href="https://opencollective.com/bower/backer/0/website" target="_blank"><img src="https://opencollective.com/bower/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/1/website" target="_blank"><img src="https://opencollective.com/bower/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/2/website" target="_blank"><img src="https://opencollective.com/bower/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/3/website" target="_blank"><img src="https://opencollective.com/bower/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/4/website" target="_blank"><img src="https://opencollective.com/bower/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/5/website" target="_blank"><img src="https://opencollective.com/bower/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/6/website" target="_blank"><img src="https://opencollective.com/bower/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/7/website" target="_blank"><img src="https://opencollective.com/bower/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/8/website" target="_blank"><img src="https://opencollective.com/bower/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/9/website" target="_blank"><img src="https://opencollective.com/bower/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/10/website" target="_blank"><img src="https://opencollective.com/bower/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/11/website" target="_blank"><img src="https://opencollective.com/bower/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/12/website" target="_blank"><img src="https://opencollective.com/bower/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/13/website" target="_blank"><img src="https://opencollective.com/bower/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/14/website" target="_blank"><img src="https://opencollective.com/bower/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/15/website" target="_blank"><img src="https://opencollective.com/bower/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/16/website" target="_blank"><img src="https://opencollective.com/bower/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/17/website" target="_blank"><img src="https://opencollective.com/bower/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/18/website" target="_blank"><img src="https://opencollective.com/bower/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/19/website" target="_blank"><img src="https://opencollective.com/bower/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/20/website" target="_blank"><img src="https://opencollective.com/bower/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/21/website" target="_blank"><img src="https://opencollective.com/bower/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/22/website" target="_blank"><img src="https://opencollective.com/bower/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/23/website" target="_blank"><img src="https://opencollective.com/bower/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/24/website" target="_blank"><img src="https://opencollective.com/bower/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/25/website" target="_blank"><img src="https://opencollective.com/bower/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/26/website" target="_blank"><img src="https://opencollective.com/bower/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/27/website" target="_blank"><img src="https://opencollective.com/bower/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/28/website" target="_blank"><img src="https://opencollective.com/bower/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/29/website" target="_blank"><img src="https://opencollective.com/bower/backer/29/avatar.svg"></a>
|
||||
### Bower Alumni
|
||||
|
||||
|
||||
## Sponsors
|
||||
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/bower#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/bower/sponsor/0/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/1/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/2/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/3/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/4/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/5/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/6/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/7/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/8/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/9/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/10/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/11/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/12/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/13/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/14/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/15/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/16/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/17/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/18/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/19/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/20/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/21/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/22/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/23/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/24/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/25/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/26/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/27/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/28/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/sponsor/29/website" target="_blank"><img src="https://opencollective.com/bower/sponsor/29/avatar.svg"></a>
|
||||
* [@fat](https://github.com/fat)
|
||||
* [@maccman](https://github.com/maccman)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2012-present Twitter and [other contributors](https://github.com/bower/bower/graphs/contributors)
|
||||
Copyright (c) 2014 Twitter and other contributors
|
||||
|
||||
Licensed under the MIT License
|
||||
|
||||
144
bin/bower
144
bin/bower
@@ -1,3 +1,145 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
require('../lib/bin/bower');
|
||||
process.bin = process.title = 'bower';
|
||||
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var updateNotifier = require('update-notifier');
|
||||
var Logger = require('bower-logger');
|
||||
var osenv = require('osenv');
|
||||
var bower = require('../lib');
|
||||
var pkg = require(path.join(__dirname, '..', 'package.json'));
|
||||
var cli = require('../lib/util/cli');
|
||||
var rootCheck = require('../lib/util/rootCheck');
|
||||
var analytics = require('../lib/util/analytics');
|
||||
|
||||
// --------
|
||||
|
||||
var options;
|
||||
var renderer;
|
||||
var loglevel;
|
||||
var command;
|
||||
var commandFunc;
|
||||
var logger;
|
||||
var notifier;
|
||||
var levels = Logger.LEVELS;
|
||||
|
||||
options = cli.readOptions({
|
||||
version: { type: Boolean, shorthand: 'v' },
|
||||
help: { type: Boolean, shorthand: 'h' },
|
||||
'allow-root': { type: Boolean }
|
||||
});
|
||||
|
||||
// Handle print of version
|
||||
if (options.version) {
|
||||
process.stdout.write(pkg.version + '\n');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
// Root check
|
||||
rootCheck(options, bower.config);
|
||||
|
||||
// Set loglevel
|
||||
if (bower.config.silent) {
|
||||
loglevel = levels.error;
|
||||
} else if (bower.config.verbose) {
|
||||
loglevel = -Infinity;
|
||||
Q.longStackSupport = true;
|
||||
} else if (bower.config.quiet) {
|
||||
loglevel = levels.warn;
|
||||
} else {
|
||||
loglevel = levels[bower.config.loglevel] || levels.info;
|
||||
}
|
||||
|
||||
// Get the command to execute
|
||||
while (options.argv.remain.length) {
|
||||
command = options.argv.remain.join(' ');
|
||||
|
||||
// Alias lookup
|
||||
if (bower.abbreviations[command]) {
|
||||
command = bower.abbreviations[command].replace(/\s/g, '.');
|
||||
break;
|
||||
}
|
||||
|
||||
command = command.replace(/\s/g, '.');
|
||||
|
||||
// Direct lookup
|
||||
if (mout.object.has(bower.commands, command)) {
|
||||
break;
|
||||
}
|
||||
|
||||
options.argv.remain.pop();
|
||||
}
|
||||
|
||||
// Ask for Insights on first run.
|
||||
analytics.setup().then(function () {
|
||||
// Execute the command
|
||||
commandFunc = command && mout.object.get(bower.commands, command);
|
||||
command = command && command.replace(/\./g, ' ');
|
||||
|
||||
// If no command was specified, show bower help
|
||||
// Do the same if the command is unknown
|
||||
if (!commandFunc) {
|
||||
logger = bower.commands.help();
|
||||
command = 'help';
|
||||
// If the user requested help, show the command's help
|
||||
// Do the same if the actual command is a group of other commands (e.g.: cache)
|
||||
} else if (options.help || !commandFunc.line) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
// Call the line method
|
||||
} else {
|
||||
logger = commandFunc.line(process.argv);
|
||||
|
||||
// If the method failed to interpret the process arguments
|
||||
// show the command help
|
||||
if (!logger) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
}
|
||||
}
|
||||
|
||||
// Get the renderer and configure it with the executed command
|
||||
renderer = cli.getRenderer(command, logger.json, bower.config);
|
||||
|
||||
logger
|
||||
.on('end', function (data) {
|
||||
if (!bower.config.silent && !bower.config.quiet) {
|
||||
renderer.end(data);
|
||||
}
|
||||
})
|
||||
.on('error', function (err) {
|
||||
if (levels.error >= loglevel) {
|
||||
renderer.error(err);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
})
|
||||
.on('log', function (log) {
|
||||
if (levels[log.level] >= loglevel) {
|
||||
renderer.log(log);
|
||||
}
|
||||
})
|
||||
.on('prompt', function (prompt, callback) {
|
||||
renderer.prompt(prompt)
|
||||
.then(function (answer) {
|
||||
callback(answer);
|
||||
});
|
||||
});
|
||||
|
||||
// Warn if HOME is not SET
|
||||
if (!osenv.home()) {
|
||||
logger.warn('no-home', 'HOME not set, user configuration will not be loaded');
|
||||
}
|
||||
|
||||
// Check for newer version of Bower
|
||||
notifier = updateNotifier({
|
||||
packageName: pkg.name,
|
||||
packageVersion: pkg.version
|
||||
});
|
||||
|
||||
if (notifier.update && levels.info >= loglevel) {
|
||||
renderer.updateNotice(notifier.update);
|
||||
}
|
||||
});
|
||||
|
||||
145
lib/bin/bower.js
145
lib/bin/bower.js
@@ -1,145 +0,0 @@
|
||||
process.bin = process.title = 'bower';
|
||||
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var userHome = require('user-home');
|
||||
var bower = require('../');
|
||||
var version = require('../version');
|
||||
var cli = require('../util/cli');
|
||||
var rootCheck = require('../util/rootCheck');
|
||||
|
||||
var options;
|
||||
var renderer;
|
||||
var loglevel;
|
||||
var command;
|
||||
var commandFunc;
|
||||
var logger;
|
||||
var levels = Logger.LEVELS;
|
||||
|
||||
options = cli.readOptions({
|
||||
'version': { type: Boolean, shorthand: 'v' },
|
||||
'help': { type: Boolean, shorthand: 'h' },
|
||||
'allow-root': { type: Boolean }
|
||||
});
|
||||
|
||||
// Handle print of version
|
||||
if (options.version) {
|
||||
process.stdout.write(version + '\n');
|
||||
process.exit();
|
||||
}
|
||||
|
||||
// Root check
|
||||
rootCheck(options, bower.config);
|
||||
|
||||
// Set loglevel
|
||||
if (bower.config.silent) {
|
||||
loglevel = levels.error;
|
||||
} else if (bower.config.verbose) {
|
||||
loglevel = -Infinity;
|
||||
Q.longStackSupport = true;
|
||||
} else if (bower.config.quiet) {
|
||||
loglevel = levels.warn;
|
||||
} else {
|
||||
loglevel = levels[bower.config.loglevel] || levels.info;
|
||||
}
|
||||
|
||||
// Get the command to execute
|
||||
while (options.argv.remain.length) {
|
||||
command = options.argv.remain.join(' ');
|
||||
|
||||
// Alias lookup
|
||||
if (bower.abbreviations[command]) {
|
||||
command = bower.abbreviations[command].replace(/\s/g, '.');
|
||||
break;
|
||||
}
|
||||
|
||||
command = command.replace(/\s/g, '.');
|
||||
|
||||
// Direct lookup
|
||||
if (mout.object.has(bower.commands, command)) {
|
||||
break;
|
||||
}
|
||||
|
||||
options.argv.remain.pop();
|
||||
}
|
||||
|
||||
// Execute the command
|
||||
commandFunc = command && mout.object.get(bower.commands, command);
|
||||
command = command && command.replace(/\./g, ' ');
|
||||
|
||||
// If no command was specified, show bower help
|
||||
// Do the same if the command is unknown
|
||||
if (!commandFunc) {
|
||||
logger = bower.commands.help();
|
||||
command = 'help';
|
||||
// If the user requested help, show the command's help
|
||||
// Do the same if the actual command is a group of other commands (e.g.: cache)
|
||||
} else if (options.help || !commandFunc.line) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
// Call the line method
|
||||
} else {
|
||||
logger = commandFunc.line(process.argv);
|
||||
|
||||
// If the method failed to interpret the process arguments
|
||||
// show the command help
|
||||
if (!logger) {
|
||||
logger = bower.commands.help(command);
|
||||
command = 'help';
|
||||
}
|
||||
}
|
||||
|
||||
// Get the renderer and configure it with the executed command
|
||||
renderer = cli.getRenderer(command, logger.json, bower.config);
|
||||
|
||||
function handleLogger(logger, renderer) {
|
||||
logger
|
||||
.on('end', function (data) {
|
||||
if (!bower.config.silent && !bower.config.quiet) {
|
||||
renderer.end(data);
|
||||
}
|
||||
})
|
||||
.on('error', function (err) {
|
||||
if (command !== 'help' && (err.code === 'EREADOPTIONS' || err.code === 'EINVFORMAT')) {
|
||||
logger = bower.commands.help(command);
|
||||
renderer = cli.getRenderer('help', logger.json, bower.config);
|
||||
handleLogger(logger, renderer);
|
||||
} else {
|
||||
if (levels.error >= loglevel) {
|
||||
renderer.error(err);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
})
|
||||
.on('log', function (log) {
|
||||
if (levels[log.level] >= loglevel) {
|
||||
renderer.log(log);
|
||||
}
|
||||
})
|
||||
.on('prompt', function (prompt, callback) {
|
||||
renderer.prompt(prompt)
|
||||
.then(function (answer) {
|
||||
callback(answer);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleLogger(logger, renderer);
|
||||
|
||||
// Warn if HOME is not SET
|
||||
if (!userHome) {
|
||||
logger.warn('no-home', 'HOME environment variable not set. User config will not be loaded.');
|
||||
}
|
||||
|
||||
if (bower.config.interactive) {
|
||||
var updateNotifier = require('update-notifier');
|
||||
|
||||
// Check for newer version of Bower
|
||||
var notifier = updateNotifier({ pkg: { name: 'bower', version: version } });
|
||||
|
||||
if (notifier.update && levels.info >= loglevel) {
|
||||
notifier.notify();
|
||||
}
|
||||
}
|
||||
56
lib/commands/cache/clean.js
vendored
56
lib/commands/cache/clean.js
vendored
@@ -1,19 +1,22 @@
|
||||
var fs = require('../../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var rimraf = require('../../util/rimraf');
|
||||
var rimraf = require('rimraf');
|
||||
var Logger = require('bower-logger');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var PackageRepository = require('../../core/PackageRepository');
|
||||
var semver = require('../../util/semver');
|
||||
var cli = require('../../util/cli');
|
||||
var defaultConfig = require('../../config');
|
||||
|
||||
function clean(logger, endpoints, options, config) {
|
||||
function clean(endpoints, options, config) {
|
||||
var logger = new Logger();
|
||||
var decEndpoints;
|
||||
var names;
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
|
||||
// If endpoints is an empty array, null them
|
||||
if (endpoints && !endpoints.length) {
|
||||
@@ -30,13 +33,21 @@ function clean(logger, endpoints, options, config) {
|
||||
});
|
||||
}
|
||||
|
||||
return Q.all([
|
||||
Q.all([
|
||||
clearPackages(decEndpoints, config, logger),
|
||||
clearLinks(names, config, logger)
|
||||
clearLinks(names, config, logger),
|
||||
!names ? clearCompletion(config, logger) : null
|
||||
])
|
||||
.spread(function (entries) {
|
||||
return entries;
|
||||
})
|
||||
.done(function (entries) {
|
||||
logger.emit('end', entries);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function clearPackages(decEndpoints, config, logger) {
|
||||
@@ -170,16 +181,37 @@ function clearLinks(names, config, logger) {
|
||||
});
|
||||
}
|
||||
|
||||
function clearCompletion(config, logger) {
|
||||
var dir = config.storage.completion;
|
||||
|
||||
return Q.nfcall(fs.stat, dir)
|
||||
.then(function () {
|
||||
return Q.nfcall(rimraf, dir)
|
||||
.then(function () {
|
||||
logger.info('deleted', 'Completion cache', {
|
||||
file: dir
|
||||
});
|
||||
});
|
||||
}, function (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
clean.readOptions = function (argv) {
|
||||
var cli = require('../../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var endpoints = options.argv.remain.slice(2);
|
||||
clean.line = function (argv) {
|
||||
var options = clean.options(argv);
|
||||
return clean(options.argv.remain.slice(2), options);
|
||||
};
|
||||
|
||||
delete options.argv;
|
||||
clean.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
return [endpoints, options];
|
||||
clean.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = clean;
|
||||
|
||||
4
lib/commands/cache/index.js
vendored
Normal file
4
lib/commands/cache/index.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
clean: require('./clean'),
|
||||
list: require('./list')
|
||||
};
|
||||
31
lib/commands/cache/list.js
vendored
31
lib/commands/cache/list.js
vendored
@@ -1,11 +1,14 @@
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var PackageRepository = require('../../core/PackageRepository');
|
||||
var cli = require('../../util/cli');
|
||||
var defaultConfig = require('../../config');
|
||||
|
||||
function list(logger, packages, options, config) {
|
||||
function list(packages, options, config) {
|
||||
var repository;
|
||||
var logger = new Logger();
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
repository = new PackageRepository(config, logger);
|
||||
|
||||
// If packages is an empty array, null them
|
||||
@@ -13,7 +16,7 @@ function list(logger, packages, options, config) {
|
||||
packages = null;
|
||||
}
|
||||
|
||||
return repository.list()
|
||||
repository.list()
|
||||
.then(function (entries) {
|
||||
if (packages) {
|
||||
// Filter entries according to the specified packages
|
||||
@@ -25,19 +28,29 @@ function list(logger, packages, options, config) {
|
||||
}
|
||||
|
||||
return entries;
|
||||
})
|
||||
.done(function (entries) {
|
||||
logger.emit('end', entries);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
list.readOptions = function (argv) {
|
||||
var cli = require('../../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var packages = options.argv.remain.slice(2);
|
||||
list.line = function (argv) {
|
||||
var options = list.options(argv);
|
||||
return list(options.argv.remain.slice(2), options);
|
||||
};
|
||||
|
||||
delete options.argv;
|
||||
list.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
return [packages, options];
|
||||
list.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = list;
|
||||
|
||||
31
lib/commands/completion.js
Normal file
31
lib/commands/completion.js
Normal file
@@ -0,0 +1,31 @@
|
||||
var Logger = require('bower-logger');
|
||||
var cli = require('../util/cli');
|
||||
|
||||
function completion(config) {
|
||||
var logger = new Logger();
|
||||
|
||||
process.nextTick(function () {
|
||||
logger.emit('end');
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
completion.line = function (argv) {
|
||||
var options = completion.options(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
return completion(name);
|
||||
};
|
||||
|
||||
completion.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
completion.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = completion;
|
||||
@@ -1,39 +1,52 @@
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var fs = require('../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var Logger = require('bower-logger');
|
||||
var cli = require('../util/cli');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function help(logger, name, config) {
|
||||
function help(name) {
|
||||
var json;
|
||||
var logger = new Logger();
|
||||
|
||||
if (name) {
|
||||
json = path.resolve(__dirname, '../templates/json/help-' + name.replace(/\s+/g, '/') + '.json');
|
||||
json = path.resolve(__dirname, '../../templates/json/help-' + name.replace(/\s+/g, '/') + '.json');
|
||||
} else {
|
||||
json = path.resolve(__dirname, '../templates/json/help.json');
|
||||
json = path.resolve(__dirname, '../../templates/json/help.json');
|
||||
}
|
||||
|
||||
return Q.promise(function (resolve) {
|
||||
fs.exists(json, resolve);
|
||||
})
|
||||
.then(function (exists) {
|
||||
fs.exists(json, function (exists) {
|
||||
if (!exists) {
|
||||
throw createError('Unknown command: ' + name, 'EUNKNOWNCMD', {
|
||||
return logger.emit('error', createError('Unknown command: ' + name, 'EUNKOWNCMD', {
|
||||
command: name
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
return require(json);
|
||||
try {
|
||||
json = require(json);
|
||||
} catch (error) {
|
||||
return logger.emit('error', error);
|
||||
}
|
||||
|
||||
logger.emit('end', json);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
help.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain.slice(1).join(' ');
|
||||
help.line = function (argv) {
|
||||
var options = help.options(argv);
|
||||
|
||||
return [name];
|
||||
return help(options.argv.remain.slice(1).join(' '));
|
||||
};
|
||||
|
||||
help.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
help.completion = function () {
|
||||
// TODO
|
||||
};
|
||||
|
||||
module.exports = help;
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var Project = require('../core/Project');
|
||||
var open = require('opn');
|
||||
var open = require('open');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var cli = require('../util/cli');
|
||||
var createError = require('../util/createError');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function home(logger, name, config) {
|
||||
function home(name, config) {
|
||||
var project;
|
||||
var promise;
|
||||
var decEndpoint;
|
||||
var logger = new Logger();
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
project = new Project(config, logger);
|
||||
|
||||
// Get the package meta
|
||||
@@ -33,26 +37,40 @@ function home(logger, name, config) {
|
||||
}
|
||||
|
||||
// Get homepage and open it
|
||||
return promise.then(function (pkgMeta) {
|
||||
promise.then(function (pkgMeta) {
|
||||
var homepage = pkgMeta.homepage;
|
||||
|
||||
if (!homepage) {
|
||||
throw createError('No homepage set for ' + pkgMeta.name, 'ENOHOME');
|
||||
}
|
||||
|
||||
open(homepage, { wait: false });
|
||||
open(homepage);
|
||||
return homepage;
|
||||
})
|
||||
.done(function (homepage) {
|
||||
logger.emit('end', homepage);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
home.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
home.line = function (argv) {
|
||||
var options = home.options(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
return [name];
|
||||
return home(name);
|
||||
};
|
||||
|
||||
home.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
home.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = home;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
var Q = require('q');
|
||||
var Logger = require('bower-logger');
|
||||
var config = require('../config');
|
||||
|
||||
/**
|
||||
* Require commands only when called.
|
||||
@@ -9,73 +8,50 @@ var config = require('../config');
|
||||
* a command function. The difference is that `cmd = commandFactory()` and `cmd()`
|
||||
* return as soon as possible and load and execute the command asynchronously.
|
||||
*/
|
||||
function commandFactory(id) {
|
||||
function runApi() {
|
||||
var command = require(id);
|
||||
var commandArgs = [].slice.call(arguments);
|
||||
|
||||
return withLogger(function (logger) {
|
||||
commandArgs.unshift(logger);
|
||||
|
||||
return command.apply(undefined, commandArgs);
|
||||
});
|
||||
}
|
||||
|
||||
function runFromArgv(argv) {
|
||||
var commandArgs;
|
||||
var command = require(id);
|
||||
|
||||
commandArgs = command.readOptions(argv);
|
||||
|
||||
return withLogger(function (logger) {
|
||||
commandArgs.unshift(logger);
|
||||
|
||||
return command.apply(undefined, commandArgs);
|
||||
});
|
||||
}
|
||||
|
||||
function withLogger(func) {
|
||||
function lazyRequire(id) {
|
||||
function command() {
|
||||
var logger = new Logger();
|
||||
var commandArgs = arguments;
|
||||
|
||||
Q.try(func, logger)
|
||||
.done(function () {
|
||||
config.restore();
|
||||
var args = [].slice.call(arguments);
|
||||
args.unshift('end');
|
||||
logger.emit.apply(logger, args);
|
||||
Q.try(function () {
|
||||
// call require asynchronously
|
||||
return require(id).apply(undefined, commandArgs);
|
||||
})
|
||||
.done(function (commandLogger) {
|
||||
// forward to exposed logger
|
||||
commandLogger.on('end', logger.emit.bind(logger, 'end'));
|
||||
commandLogger.on('error', logger.emit.bind(logger, 'error'));
|
||||
}, function (error) {
|
||||
config.restore();
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
runApi.line = runFromArgv;
|
||||
function runFromArgv() {
|
||||
return require(id).line.apply(undefined, arguments);
|
||||
}
|
||||
|
||||
return runApi;
|
||||
command.line = runFromArgv;
|
||||
return command;
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
cache: {
|
||||
clean: commandFactory('./cache/clean'),
|
||||
list: commandFactory('./cache/list'),
|
||||
},
|
||||
help: commandFactory('./help'),
|
||||
home: commandFactory('./home'),
|
||||
info: commandFactory('./info'),
|
||||
init: commandFactory('./init'),
|
||||
install: commandFactory('./install'),
|
||||
link: commandFactory('./link'),
|
||||
list: commandFactory('./list'),
|
||||
login: commandFactory('./login'),
|
||||
lookup: commandFactory('./lookup'),
|
||||
prune: commandFactory('./prune'),
|
||||
register: commandFactory('./register'),
|
||||
search: commandFactory('./search'),
|
||||
update: commandFactory('./update'),
|
||||
uninstall: commandFactory('./uninstall'),
|
||||
unregister: commandFactory('./unregister'),
|
||||
version: commandFactory('./version')
|
||||
cache: lazyRequire('./cache'),
|
||||
completion: lazyRequire('./completion'),
|
||||
help: lazyRequire('./help'),
|
||||
home: lazyRequire('./home'),
|
||||
info: lazyRequire('./info'),
|
||||
init: lazyRequire('./init'),
|
||||
install: lazyRequire('./install'),
|
||||
link: lazyRequire('./link'),
|
||||
list: lazyRequire('./list'),
|
||||
lookup: lazyRequire('./lookup'),
|
||||
prune: lazyRequire('./prune'),
|
||||
register: lazyRequire('./register'),
|
||||
search: lazyRequire('./search'),
|
||||
update: lazyRequire('./update'),
|
||||
uninstall: lazyRequire('./uninstall'),
|
||||
version: lazyRequire('./version')
|
||||
};
|
||||
|
||||
@@ -1,70 +1,85 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var Logger = require('bower-logger');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var cli = require('../util/cli');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function info(logger, endpoint, property, config) {
|
||||
if (!endpoint) {
|
||||
return;
|
||||
}
|
||||
|
||||
// handle @ as version divider
|
||||
var splitParts = endpoint.split('/');
|
||||
splitParts[splitParts.length - 1] = splitParts[splitParts.length - 1].replace('@', '#');
|
||||
endpoint = splitParts.join('/');
|
||||
|
||||
|
||||
function info(endpoint, property, config) {
|
||||
var repository;
|
||||
var decEndpoint;
|
||||
var tracker;
|
||||
var logger = new Logger();
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
repository = new PackageRepository(config, logger);
|
||||
tracker = new Tracker(config);
|
||||
|
||||
decEndpoint = endpointParser.decompose(endpoint);
|
||||
tracker.trackDecomposedEndpoints('info', [decEndpoint]);
|
||||
|
||||
return Q.all([
|
||||
Q.all([
|
||||
getPkgMeta(repository, decEndpoint, property),
|
||||
decEndpoint.target === '*' && !property ? repository.versions(decEndpoint.source) : null
|
||||
])
|
||||
.spread(function (pkgMeta, versions) {
|
||||
if (versions) {
|
||||
return {
|
||||
name: decEndpoint.source,
|
||||
versions: versions,
|
||||
latest: pkgMeta
|
||||
};
|
||||
}
|
||||
.spread(function (pkgMeta, versions) {
|
||||
if (versions) {
|
||||
return {
|
||||
name: decEndpoint.source,
|
||||
versions: versions,
|
||||
latest: pkgMeta
|
||||
};
|
||||
}
|
||||
|
||||
return pkgMeta;
|
||||
});
|
||||
return pkgMeta;
|
||||
})
|
||||
.done(function (result) {
|
||||
logger.emit('end', result);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function getPkgMeta(repository, decEndpoint, property) {
|
||||
return repository.fetch(decEndpoint)
|
||||
.spread(function (canonicalDir, pkgMeta) {
|
||||
pkgMeta = mout.object.filter(pkgMeta, function (value, key) {
|
||||
return key.charAt(0) !== '_';
|
||||
});
|
||||
|
||||
// Retrieve specific property
|
||||
if (property) {
|
||||
pkgMeta = mout.object.get(pkgMeta, property);
|
||||
}
|
||||
|
||||
return pkgMeta;
|
||||
.spread(function (canonicalDir, pkgMeta) {
|
||||
pkgMeta = mout.object.filter(pkgMeta, function (value, key) {
|
||||
return key.charAt(0) !== '_';
|
||||
});
|
||||
|
||||
// Retrieve specific property
|
||||
if (property) {
|
||||
pkgMeta = mout.object.get(pkgMeta, property);
|
||||
}
|
||||
|
||||
return pkgMeta;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
info.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
info.line = function (argv) {
|
||||
var options = info.options(argv);
|
||||
var pkg = options.argv.remain[1];
|
||||
var property = options.argv.remain[2];
|
||||
|
||||
return [pkg, property];
|
||||
if (!pkg) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return info(pkg, property);
|
||||
};
|
||||
|
||||
info.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
info.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = info;
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
var mout = require('mout');
|
||||
var fs = require('../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var Logger = require('bower-logger');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
var GitHubResolver = require('../core/resolvers/GitHubResolver');
|
||||
var GitFsResolver = require('../core/resolvers/GitFsResolver');
|
||||
var cli = require('../util/cli');
|
||||
var cmd = require('../util/cmd');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function init(logger, config) {
|
||||
function init(config) {
|
||||
var project;
|
||||
var logger = new Logger();
|
||||
|
||||
config = config || {};
|
||||
|
||||
if (!config.cwd) {
|
||||
config.cwd = process.cwd();
|
||||
}
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
|
||||
// This command requires interactive to be enabled
|
||||
if (!config.interactive) {
|
||||
throw createError('Register requires an interactive shell', 'ENOINT', {
|
||||
details: 'Note that you can manually force an interactive shell with --config.interactive'
|
||||
process.nextTick(function () {
|
||||
logger.emit('error', createError('Register requires an interactive shell', 'ENOINT', {
|
||||
details: 'Note that you can manually force an interactive shell with --config.interactive'
|
||||
}));
|
||||
});
|
||||
return logger;
|
||||
}
|
||||
|
||||
project = new Project(config, logger);
|
||||
|
||||
// Start with existing JSON details
|
||||
return readJson(project, logger)
|
||||
readJson(project, logger)
|
||||
// Fill in defaults
|
||||
.then(setDefaults.bind(null, config))
|
||||
// Now prompt user to make changes
|
||||
@@ -40,7 +41,14 @@ function init(logger, config) {
|
||||
// Set dependencies based on the response
|
||||
.spread(setDependencies.bind(null, project))
|
||||
// All done!
|
||||
.spread(saveJson.bind(null, project, logger));
|
||||
.spread(saveJson.bind(null, project, logger))
|
||||
.done(function (json) {
|
||||
logger.emit('end', json);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function readJson(project, logger) {
|
||||
@@ -57,7 +65,7 @@ function readJson(project, logger) {
|
||||
function saveJson(project, logger, json) {
|
||||
// Cleanup empty props (null values, empty strings, objects and arrays)
|
||||
mout.object.forOwn(json, function (value, key) {
|
||||
if (!validConfigValue(value)) {
|
||||
if (value == null || mout.lang.isEmpty(value)) {
|
||||
delete json[key];
|
||||
}
|
||||
});
|
||||
@@ -80,18 +88,6 @@ function saveJson(project, logger, json) {
|
||||
});
|
||||
}
|
||||
|
||||
// Test if value is of a type supported by bower.json[0] - Object, Array, String, Boolean - or a Number
|
||||
// [0]: https://github.com/bower/bower.json-spec
|
||||
function validConfigValue(val) {
|
||||
return (
|
||||
mout.lang.isObject(val) ||
|
||||
mout.lang.isArray(val) ||
|
||||
mout.lang.isString(val) ||
|
||||
mout.lang.isBoolean(val) ||
|
||||
mout.lang.isNumber(val)
|
||||
);
|
||||
}
|
||||
|
||||
function setDefaults(config, json) {
|
||||
var name;
|
||||
var promise = Q.resolve();
|
||||
@@ -101,6 +97,19 @@ function setDefaults(config, json) {
|
||||
json.name = path.basename(config.cwd);
|
||||
}
|
||||
|
||||
// Version
|
||||
if (!json.version) {
|
||||
// Assume latest semver tag if it's a git repo
|
||||
promise = promise.then(function () {
|
||||
return GitFsResolver.versions(config.cwd)
|
||||
.then(function (versions) {
|
||||
json.version = versions[0] || '0.0.0';
|
||||
}, function () {
|
||||
json.version = '0.0.0';
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Main
|
||||
if (!json.main) {
|
||||
// Remove '.js' from the end of the package name if it is there
|
||||
@@ -174,6 +183,12 @@ function promptUser(logger, json) {
|
||||
'default': json.name,
|
||||
'type': 'input'
|
||||
},
|
||||
{
|
||||
'name': 'version',
|
||||
'message': 'version',
|
||||
'default': json.version,
|
||||
'type': 'input'
|
||||
},
|
||||
{
|
||||
'name': 'description',
|
||||
'message': 'description',
|
||||
@@ -186,6 +201,12 @@ function promptUser(logger, json) {
|
||||
'default': json.main,
|
||||
'type': 'input'
|
||||
},
|
||||
{
|
||||
'name': 'moduleType',
|
||||
'message': 'what types of modules does this package expose?',
|
||||
'type': 'checkbox',
|
||||
'choices': ['amd', 'es6', 'globals', 'node', 'yui']
|
||||
},
|
||||
{
|
||||
'name': 'keywords',
|
||||
'message': 'keywords',
|
||||
@@ -233,8 +254,10 @@ function promptUser(logger, json) {
|
||||
return Q.nfcall(logger.prompt.bind(logger), questions)
|
||||
.then(function (answers) {
|
||||
json.name = answers.name;
|
||||
json.version = answers.version;
|
||||
json.description = answers.description;
|
||||
json.main = answers.main;
|
||||
json.moduleType = answers.moduleType;
|
||||
json.keywords = toArray(answers.keywords);
|
||||
json.authors = toArray(answers.authors, ',');
|
||||
json.license = answers.license;
|
||||
@@ -306,8 +329,16 @@ function setDependencies(project, json, answers) {
|
||||
|
||||
// -------------------
|
||||
|
||||
init.readOptions = function (argv) {
|
||||
return [];
|
||||
init.line = function () {
|
||||
return init();
|
||||
};
|
||||
|
||||
init.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
init.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = init;
|
||||
|
||||
@@ -1,51 +1,61 @@
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var Project = require('../core/Project');
|
||||
var cli = require('../util/cli');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function install(logger, endpoints, options, config) {
|
||||
function install(endpoints, options, config) {
|
||||
var project;
|
||||
var decEndpoints;
|
||||
var tracker;
|
||||
var logger = new Logger();
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
if (options.save === undefined) {
|
||||
options.save = config.defaultSave;
|
||||
}
|
||||
project = new Project(config, logger);
|
||||
tracker = new Tracker(config);
|
||||
|
||||
// Convert endpoints to decomposed endpoints
|
||||
endpoints = endpoints || [];
|
||||
decEndpoints = endpoints.map(function (endpoint) {
|
||||
|
||||
// handle @ as version divider
|
||||
var splitParts = endpoint.split('/');
|
||||
splitParts[splitParts.length - 1] = splitParts[splitParts.length - 1].replace('@', '#');
|
||||
endpoint = splitParts.join('/');
|
||||
|
||||
return endpointParser.decompose(endpoint);
|
||||
});
|
||||
tracker.trackDecomposedEndpoints('install', decEndpoints);
|
||||
|
||||
return project.install(decEndpoints, options, config);
|
||||
project.install(decEndpoints, options)
|
||||
.done(function (installed) {
|
||||
tracker.trackPackages('installed', installed);
|
||||
logger.emit('end', installed);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
install.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
install.line = function (argv) {
|
||||
var options = install.options(argv);
|
||||
return install(options.argv.remain.slice(1), options);
|
||||
};
|
||||
|
||||
var options = cli.readOptions({
|
||||
'force-latest': {type: Boolean, shorthand: 'F'},
|
||||
'production': {type: Boolean, shorthand: 'p'},
|
||||
'save': {type: Boolean, shorthand: 'S'},
|
||||
'save-dev': {type: Boolean, shorthand: 'D'},
|
||||
'save-exact': {type: Boolean, shorthand: 'E'}
|
||||
install.options = function (argv) {
|
||||
return cli.readOptions({
|
||||
'force-latest': { type: Boolean, shorthand: 'F'},
|
||||
'production': { type: Boolean, shorthand: 'p' },
|
||||
'save': { type: Boolean, shorthand: 'S' },
|
||||
'save-dev': { type: Boolean, shorthand: 'D' }
|
||||
}, argv);
|
||||
};
|
||||
|
||||
var packages = options.argv.remain.slice(1);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [packages, options];
|
||||
install.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = install;
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
var path = require('path');
|
||||
var rimraf = require('../util/rimraf');
|
||||
var rimraf = require('rimraf');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var Logger = require('bower-logger');
|
||||
var Project = require('../core/Project');
|
||||
var createLink = require('../util/createLink');
|
||||
var cli = require('../util/cli');
|
||||
var defaultConfig = require('../config');
|
||||
var relativeToBaseDir = require('../util/relativeToBaseDir');
|
||||
|
||||
function link(logger, name, localName, config) {
|
||||
if (name) {
|
||||
return linkTo(logger, name, localName, config);
|
||||
} else {
|
||||
return linkSelf(logger, config);
|
||||
}
|
||||
}
|
||||
|
||||
function linkSelf(logger, config) {
|
||||
function linkSelf(config) {
|
||||
var project;
|
||||
var logger = new Logger();
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return project.getJson()
|
||||
project.getJson()
|
||||
.then(function (json) {
|
||||
var src = config.cwd;
|
||||
var dst = path.join(config.storage.links, json.name);
|
||||
@@ -37,23 +32,30 @@ function linkSelf(logger, config) {
|
||||
dst: dst
|
||||
};
|
||||
});
|
||||
})
|
||||
.done(function (result) {
|
||||
logger.emit('end', result);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function linkTo(logger, name, localName, config) {
|
||||
function linkTo(name, localName, config) {
|
||||
var src;
|
||||
var dst;
|
||||
var project;
|
||||
var logger = new Logger();
|
||||
var project = new Project(config, logger);
|
||||
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
|
||||
localName = localName || name;
|
||||
src = path.join(config.storage.links, name);
|
||||
dst = path.join(relativeToBaseDir(config.cwd)(config.directory), localName);
|
||||
dst = path.join(process.cwd(), config.directory, localName);
|
||||
|
||||
// Delete destination folder if any
|
||||
return Q.nfcall(rimraf, dst)
|
||||
Q.nfcall(rimraf, dst)
|
||||
// Link locally
|
||||
.then(function () {
|
||||
return createLink(src, dst);
|
||||
@@ -68,18 +70,41 @@ function linkTo(logger, name, localName, config) {
|
||||
dst: dst,
|
||||
installed: installed
|
||||
};
|
||||
})
|
||||
.done(function (result) {
|
||||
logger.emit('end', result);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
link.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
var link = {
|
||||
linkTo: linkTo,
|
||||
linkSelf: linkSelf
|
||||
};
|
||||
|
||||
link.line = function (argv) {
|
||||
var options = link.options(argv);
|
||||
var name = options.argv.remain[1];
|
||||
var localName = options.argv.remain[2];
|
||||
|
||||
return [name, localName];
|
||||
if (name) {
|
||||
return linkTo(name, localName);
|
||||
}
|
||||
|
||||
return linkSelf();
|
||||
};
|
||||
|
||||
link.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
link.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = link;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var Logger = require('bower-logger');
|
||||
var Project = require('../core/Project');
|
||||
var semver = require('../util/semver');
|
||||
var cli = require('../util/cli');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function list(logger, options, config) {
|
||||
function list(options, config) {
|
||||
var project;
|
||||
var logger = new Logger();
|
||||
|
||||
options = options || {};
|
||||
|
||||
@@ -15,11 +18,13 @@ function list(logger, options, config) {
|
||||
options.relative = true;
|
||||
}
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return project.getTree(options)
|
||||
project.getTree(options)
|
||||
.spread(function (tree, flattened) {
|
||||
var baseDir = path.dirname(path.join(config.cwd, config.directory));
|
||||
|
||||
// Relativize paths
|
||||
// Also normalize paths on windows
|
||||
project.walkTree(tree, function (node) {
|
||||
@@ -28,7 +33,7 @@ function list(logger, options, config) {
|
||||
}
|
||||
|
||||
if (options.relative) {
|
||||
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
|
||||
node.canonicalDir = path.relative(baseDir, node.canonicalDir);
|
||||
}
|
||||
if (options.paths) {
|
||||
node.canonicalDir = normalize(node.canonicalDir);
|
||||
@@ -43,7 +48,7 @@ function list(logger, options, config) {
|
||||
}
|
||||
|
||||
if (options.relative) {
|
||||
node.canonicalDir = path.relative(config.cwd, node.canonicalDir);
|
||||
node.canonicalDir = path.relative(baseDir, node.canonicalDir);
|
||||
}
|
||||
if (options.paths) {
|
||||
node.canonicalDir = normalize(node.canonicalDir);
|
||||
@@ -65,7 +70,16 @@ function list(logger, options, config) {
|
||||
.then(function () {
|
||||
return tree;
|
||||
});
|
||||
})
|
||||
.done(function (value) {
|
||||
logger.emit('end', value);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
logger.json = !!options.paths;
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function checkVersions(project, tree, logger) {
|
||||
@@ -81,7 +95,7 @@ function checkVersions(project, tree, logger) {
|
||||
}, true);
|
||||
|
||||
if (nodes.length) {
|
||||
logger.info('check-new', 'Checking for new versions of the project dependencies...');
|
||||
logger.info('check-new', 'Checking for new versions of the project dependencies..');
|
||||
}
|
||||
|
||||
// Check for new versions for each node
|
||||
@@ -150,17 +164,20 @@ function normalize(src) {
|
||||
|
||||
// -------------------
|
||||
|
||||
list.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
list.line = function (argv) {
|
||||
var options = list.options(argv);
|
||||
return list(options);
|
||||
};
|
||||
|
||||
var options = cli.readOptions({
|
||||
list.options = function (argv) {
|
||||
return cli.readOptions({
|
||||
'paths': { type: Boolean, shorthand: 'p' },
|
||||
'relative': { type: Boolean, shorthand: 'r' }
|
||||
}, argv);
|
||||
};
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [options];
|
||||
list.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = list;
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
var Configstore = require('configstore');
|
||||
var GitHub = require('github');
|
||||
var Q = require('q');
|
||||
|
||||
var createError = require('../util/createError');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function login(logger, options, config) {
|
||||
var configstore = new Configstore('bower-github');
|
||||
|
||||
config = defaultConfig(config);
|
||||
|
||||
var promise;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (options.token) {
|
||||
promise = Q.resolve({ token: options.token });
|
||||
} else {
|
||||
// This command requires interactive to be enabled
|
||||
if (!config.interactive) {
|
||||
logger.emit('error', createError('Login requires an interactive shell', 'ENOINT', {
|
||||
details: 'Note that you can manually force an interactive shell with --config.interactive'
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var questions = [
|
||||
{
|
||||
'name': 'username',
|
||||
'message': 'Username',
|
||||
'type': 'input',
|
||||
'default': configstore.get('username')
|
||||
},
|
||||
{
|
||||
'name': 'password',
|
||||
'message': 'Password',
|
||||
'type': 'password'
|
||||
}
|
||||
];
|
||||
|
||||
var github = new GitHub({
|
||||
version: '3.0.0'
|
||||
});
|
||||
|
||||
promise = Q.nfcall(logger.prompt.bind(logger), questions)
|
||||
.then(function (answers) {
|
||||
configstore.set('username', answers.username);
|
||||
|
||||
github.authenticate({
|
||||
type: 'basic',
|
||||
username: answers.username,
|
||||
password: answers.password
|
||||
});
|
||||
|
||||
return Q.ninvoke(github.authorization, 'create', {
|
||||
scopes: ['user', 'repo'],
|
||||
note: 'Bower command line client (' + (new Date()).toISOString() + ')'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(function (result) {
|
||||
configstore.set('accessToken', result.token);
|
||||
logger.info('EAUTH', 'Logged in as ' + configstore.get('username'), {});
|
||||
|
||||
return result;
|
||||
}, function (error) {
|
||||
var message;
|
||||
|
||||
try {
|
||||
message = JSON.parse(error.message).message;
|
||||
} catch (e) {
|
||||
message = 'Authorization failed';
|
||||
}
|
||||
|
||||
var questions = [
|
||||
{
|
||||
'name': 'otpcode',
|
||||
'message': 'Two-Factor Auth Code',
|
||||
'type': 'input'
|
||||
}
|
||||
];
|
||||
|
||||
if (message === 'Must specify two-factor authentication OTP code.') {
|
||||
return Q.nfcall(logger.prompt.bind(logger), questions)
|
||||
.then(function (answers) {
|
||||
return Q.ninvoke(github.authorization, 'create', {
|
||||
scopes: ['user', 'repo'],
|
||||
note: 'Bower command line client (' + (new Date()).toISOString() + ')',
|
||||
headers: {
|
||||
'X-GitHub-OTP': answers.otpcode
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
configstore.set('accessToken', result.token);
|
||||
logger.info('EAUTH', 'Logged in as ' + configstore.get('username'), {});
|
||||
|
||||
return result;
|
||||
}, function () {
|
||||
logger.emit('error', createError(message, 'EAUTH'));
|
||||
});
|
||||
} else {
|
||||
logger.emit('error', createError(message, 'EAUTH'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
login.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions({
|
||||
token: { type: String, shorthand: 't' },
|
||||
}, argv);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [options];
|
||||
};
|
||||
|
||||
module.exports = login;
|
||||
@@ -1,34 +1,56 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var Logger = require('bower-logger');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
var cli = require('../util/cli');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function lookup(logger, name, config) {
|
||||
if (!name) {
|
||||
return new Q(null);
|
||||
}
|
||||
function lookup(name, config) {
|
||||
var registryClient;
|
||||
var logger = new Logger();
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
config.cache = config.storage.registry;
|
||||
|
||||
var repository = new PackageRepository(config, logger);
|
||||
var registryClient = repository.getRegistryClient();
|
||||
registryClient = new RegistryClient(config, logger);
|
||||
|
||||
return Q.nfcall(registryClient.lookup.bind(registryClient), name)
|
||||
Q.nfcall(registryClient.lookup.bind(registryClient), name)
|
||||
.then(function (entry) {
|
||||
// TODO: Handle entry.type.. for now it's only 'alias'
|
||||
// When we got published packages, this needs to be adjusted
|
||||
return !entry ? null : {
|
||||
name: name,
|
||||
url: entry.url
|
||||
url: entry && entry.url
|
||||
};
|
||||
})
|
||||
.done(function (result) {
|
||||
logger.emit('end', result);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
lookup.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
var options = cli.readOptions(argv);
|
||||
lookup.line = function (argv) {
|
||||
var options = lookup.options(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
return [name];
|
||||
if (!name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return lookup(name);
|
||||
};
|
||||
|
||||
lookup.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
lookup.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = lookup;
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var Project = require('../core/Project');
|
||||
var cli = require('../util/cli');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function prune(logger, options, config) {
|
||||
function prune(options, config) {
|
||||
var project;
|
||||
var logger = new Logger();
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return clean(project, options);
|
||||
clean(project, options)
|
||||
.done(function (removed) {
|
||||
logger.emit('end', removed);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function clean(project, options, removed) {
|
||||
@@ -40,16 +50,19 @@ function clean(project, options, removed) {
|
||||
|
||||
// -------------------
|
||||
|
||||
prune.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
prune.line = function (argv) {
|
||||
var options = prune.options(argv);
|
||||
return prune(options);
|
||||
};
|
||||
|
||||
var options = cli.readOptions({
|
||||
prune.options = function (argv) {
|
||||
return cli.readOptions({
|
||||
'production': { type: Boolean, shorthand: 'p' },
|
||||
}, argv);
|
||||
};
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [options];
|
||||
prune.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = prune;
|
||||
|
||||
@@ -1,88 +1,137 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var chalk = require('chalk');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var Logger = require('bower-logger');
|
||||
var Config = require('bower-config');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var cli = require('../util/cli');
|
||||
var createError = require('../util/createError');
|
||||
var defaultConfig = require('../config');
|
||||
var GitHubResolver = require('../core/resolvers/GitHubResolver');
|
||||
|
||||
function register(logger, name, source, config) {
|
||||
function register(name, url, config) {
|
||||
var repository;
|
||||
var registryClient;
|
||||
var tracker;
|
||||
var logger = new Logger();
|
||||
var force;
|
||||
var url;
|
||||
var githubSourceRegex = /^\w[\w-]*\/\w[\w-]*$/;
|
||||
var getGithubUrl = function (source) {
|
||||
return 'git@github.com:' + source + '.git';
|
||||
};
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
force = config.force;
|
||||
|
||||
name = (name || '').trim();
|
||||
source = (source || '').trim();
|
||||
|
||||
url = source.match(githubSourceRegex) ? getGithubUrl(source) : source;
|
||||
tracker = new Tracker(config);
|
||||
|
||||
// Bypass any cache
|
||||
config.offline = false;
|
||||
config.force = true;
|
||||
|
||||
return Q.try(function () {
|
||||
// Verify name and url
|
||||
if (!name || !url) {
|
||||
throw createError('Usage: bower register <name> <url>', 'EINVFORMAT');
|
||||
// Trim name
|
||||
name = name.trim();
|
||||
|
||||
process.nextTick(function () {
|
||||
// Verify name
|
||||
// TODO: Verify with the new spec regexp?
|
||||
if (!name) {
|
||||
return logger.emit('error', createError('Please type a name', 'EINVNAME'));
|
||||
}
|
||||
|
||||
// The public registry only allows git:// endpoints
|
||||
// As such, we attempt to convert URLs as necessary
|
||||
if (config.registry.register === Config.DEFAULT_REGISTRY) {
|
||||
url = convertUrl(url, logger);
|
||||
|
||||
if (!mout.string.startsWith(url, 'git://')) {
|
||||
return logger.emit('error', createError('The registry only accepts URLs starting with git://', 'EINVFORMAT'));
|
||||
}
|
||||
}
|
||||
|
||||
tracker.track('register');
|
||||
|
||||
// Attempt to resolve the package referenced by the URL to ensure
|
||||
// everything is ok before registering
|
||||
repository = new PackageRepository(config, logger);
|
||||
return repository.fetch({ name: name, source: url, target: '*' });
|
||||
})
|
||||
.spread(function (canonicalDir, pkgMeta) {
|
||||
if (pkgMeta.private) {
|
||||
throw createError('The package you are trying to register is marked as private', 'EPRIV');
|
||||
}
|
||||
repository.fetch({ name: name, source: url, target: '*' })
|
||||
.spread(function (canonicalDir, pkgMeta) {
|
||||
if (pkgMeta.private) {
|
||||
throw createError('The package you are trying to register is marked as private', 'EPRIV');
|
||||
}
|
||||
|
||||
// If non interactive or user forced, bypass confirmation
|
||||
if (!config.interactive || force) {
|
||||
return true;
|
||||
}
|
||||
// If non interactive or user forced, bypass confirmation
|
||||
if (!config.interactive || force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Confirm if the user really wants to register
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message: 'Registering a package will make it installable via the registry (' +
|
||||
chalk.cyan.underline(config.registry.register) + '), continue?',
|
||||
default: true
|
||||
// Confirm if the user really wants to register
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message: 'Registering a package will make it installable via the registry (' +
|
||||
chalk.cyan.underline(config.registry.register) + '), continue?',
|
||||
default: true
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
// If user response was negative, abort
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register
|
||||
registryClient = repository.getRegistryClient();
|
||||
|
||||
logger.action('register', url, {
|
||||
name: name,
|
||||
url: url
|
||||
});
|
||||
|
||||
return Q.nfcall(registryClient.register.bind(registryClient), name, url);
|
||||
})
|
||||
.done(function (result) {
|
||||
tracker.track('registered');
|
||||
logger.emit('end', result);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
// If user response was negative, abort
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Register
|
||||
registryClient = repository.getRegistryClient();
|
||||
|
||||
logger.action('register', url, {
|
||||
name: name,
|
||||
url: url
|
||||
});
|
||||
|
||||
return Q.nfcall(registryClient.register.bind(registryClient), name, url);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function convertUrl(url, logger) {
|
||||
var pair;
|
||||
var newUrl;
|
||||
|
||||
if (!mout.string.startsWith(url, 'git://')) {
|
||||
// Convert GitHub ssh & https to git://
|
||||
pair = GitHubResolver.getOrgRepoPair(url);
|
||||
if (pair) {
|
||||
newUrl = 'git://github.com/' + pair.org + '/' + pair.repo + '.git';
|
||||
logger.warn('convert', 'Converted ' + url + ' to ' + newUrl);
|
||||
}
|
||||
}
|
||||
|
||||
return newUrl || url;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
register.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(argv);
|
||||
register.line = function (argv) {
|
||||
var options = register.options(argv);
|
||||
var name = options.argv.remain[1];
|
||||
var url = options.argv.remain[2];
|
||||
|
||||
return [name, url];
|
||||
if (!name || !url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return register(name, url);
|
||||
};
|
||||
|
||||
register.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
register.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = register;
|
||||
|
||||
@@ -1,39 +1,55 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var defaultConfig = require('../config');
|
||||
var Logger = require('bower-logger');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
var cli = require('../util/cli');
|
||||
var createError = require('../util/createError');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function search(logger, name, config) {
|
||||
function search(name, config) {
|
||||
var registryClient;
|
||||
var promise;
|
||||
var tracker;
|
||||
var logger = new Logger();
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
config.cache = config.storage.registry;
|
||||
|
||||
var repository = new PackageRepository(config, logger);
|
||||
var registryClient = repository.getRegistryClient();
|
||||
registryClient = new RegistryClient(config, logger);
|
||||
tracker = new Tracker(config);
|
||||
tracker.track('search', name);
|
||||
|
||||
if (name) {
|
||||
return Q.nfcall(registryClient.search.bind(registryClient), name);
|
||||
// If no name was specified, list all packages
|
||||
if (!name) {
|
||||
promise = Q.nfcall(registryClient.list.bind(registryClient));
|
||||
// Otherwise search it
|
||||
} else {
|
||||
// List all packages when in interactive mode + json enabled, and
|
||||
// always when in non-interactive mode
|
||||
if (config.interactive && !config.json) {
|
||||
throw createError('no parameter to bower search', 'EREADOPTIONS');
|
||||
}
|
||||
|
||||
return Q.nfcall(registryClient.list.bind(registryClient));
|
||||
promise = Q.nfcall(registryClient.search.bind(registryClient), name);
|
||||
}
|
||||
|
||||
promise
|
||||
.done(function (results) {
|
||||
logger.emit('end', results);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
search.readOptions = function (argv) {
|
||||
var options = cli.readOptions(argv);
|
||||
var terms = options.argv.remain.slice(1);
|
||||
search.line = function (argv) {
|
||||
var options = search.options(argv);
|
||||
return search(options.argv.remain.slice(1).join(' '), options);
|
||||
};
|
||||
|
||||
var name = terms.join(' ');
|
||||
search.options = function (argv) {
|
||||
return cli.readOptions(argv);
|
||||
};
|
||||
|
||||
return [name];
|
||||
search.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = search;
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var Q = require('q');
|
||||
var Project = require('../core/Project');
|
||||
var cli = require('../util/cli');
|
||||
var Tracker = require('../util/analytics').Tracker;
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function uninstall(logger, names, options, config) {
|
||||
if (!names.length) {
|
||||
return new Q();
|
||||
}
|
||||
|
||||
function uninstall(names, options, config) {
|
||||
var project;
|
||||
var tracker;
|
||||
var logger = new Logger();
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
project = new Project(config, logger);
|
||||
tracker = new Tracker(config);
|
||||
|
||||
return project.getTree(options)
|
||||
tracker.trackNames('uninstall', names);
|
||||
|
||||
project.getTree(options)
|
||||
.spread(function (tree, flattened) {
|
||||
// Uninstall nodes
|
||||
return project.uninstall(names, options)
|
||||
@@ -33,7 +37,15 @@ function uninstall(logger, names, options, config) {
|
||||
// Clean them!
|
||||
return clean(project, children, uninstalled);
|
||||
});
|
||||
})
|
||||
.done(function (uninstalled) {
|
||||
logger.emit('end', uninstalled);
|
||||
tracker.trackNames('uninstalled', Object.keys(uninstalled));
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function clean(project, names, removed) {
|
||||
@@ -97,19 +109,26 @@ function clean(project, names, removed) {
|
||||
|
||||
// -------------------
|
||||
|
||||
uninstall.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
uninstall.line = function (argv) {
|
||||
var options = uninstall.options(argv);
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
var options = cli.readOptions({
|
||||
if (!names.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return uninstall(names, options);
|
||||
};
|
||||
|
||||
uninstall.options = function (argv) {
|
||||
return cli.readOptions({
|
||||
'save': { type: Boolean, shorthand: 'S' },
|
||||
'save-dev': { type: Boolean, shorthand: 'D' }
|
||||
}, argv);
|
||||
};
|
||||
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [names, options];
|
||||
uninstall.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = uninstall;
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
var chalk = require('chalk');
|
||||
var Q = require('q');
|
||||
|
||||
var defaultConfig = require('../config');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function unregister(logger, name, config) {
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
var repository;
|
||||
var registryClient;
|
||||
var force;
|
||||
|
||||
config = defaultConfig(config);
|
||||
force = config.force;
|
||||
|
||||
// Bypass any cache
|
||||
config.offline = false;
|
||||
config.force = true;
|
||||
|
||||
// Trim name
|
||||
name = name.trim();
|
||||
|
||||
repository = new PackageRepository(config, logger);
|
||||
|
||||
if (!config.accessToken) {
|
||||
return logger.emit('error',
|
||||
createError('Use "bower login" with collaborator credentials', 'EFORBIDDEN')
|
||||
);
|
||||
}
|
||||
|
||||
return Q.resolve()
|
||||
.then(function () {
|
||||
// If non interactive or user forced, bypass confirmation
|
||||
if (!config.interactive || force) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message: 'You are about to remove component "' + chalk.cyan.underline(name) + '" from the bower registry (' + chalk.cyan.underline(config.registry.register) + '). It is generally considered bad behavior to remove versions of a library that others are depending on. Are you really sure?',
|
||||
default: false
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
// If user response was negative, abort
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
registryClient = repository.getRegistryClient();
|
||||
|
||||
logger.action('unregister', name, { name: name });
|
||||
|
||||
return Q.nfcall(registryClient.unregister.bind(registryClient), name);
|
||||
})
|
||||
.then(function (result) {
|
||||
logger.info('Package unregistered', name);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
unregister.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
|
||||
return [name];
|
||||
};
|
||||
|
||||
module.exports = unregister;
|
||||
@@ -1,11 +1,15 @@
|
||||
var mout = require('mout');
|
||||
var Logger = require('bower-logger');
|
||||
var Project = require('../core/Project');
|
||||
var cli = require('../util/cli');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function update(logger, names, options, config) {
|
||||
function update(names, options, config) {
|
||||
var project;
|
||||
var logger = new Logger();
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
project = new Project(config, logger);
|
||||
|
||||
// If names is an empty array, null them
|
||||
@@ -13,24 +17,32 @@ function update(logger, names, options, config) {
|
||||
names = null;
|
||||
}
|
||||
|
||||
return project.update(names, options);
|
||||
project.update(names, options)
|
||||
.done(function (installed) {
|
||||
logger.emit('end', installed);
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
update.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
update.line = function (argv) {
|
||||
var options = update.options(argv);
|
||||
return update(options.argv.remain.slice(1), options);
|
||||
};
|
||||
|
||||
var options = cli.readOptions({
|
||||
update.options = function (argv) {
|
||||
return cli.readOptions({
|
||||
'force-latest': { type: Boolean, shorthand: 'F' },
|
||||
'production': { type: Boolean, shorthand: 'p' }
|
||||
}, argv);
|
||||
};
|
||||
|
||||
var names = options.argv.remain.slice(1);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [names, options];
|
||||
update.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = update;
|
||||
|
||||
@@ -1,161 +1,140 @@
|
||||
var mout = require('mout');
|
||||
var semver = require('semver');
|
||||
var Logger = require('bower-logger');
|
||||
var which = require('which');
|
||||
var fs = require('../util/fs');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var execFile = require('child_process').execFile;
|
||||
var Project = require('../core/Project');
|
||||
var cli = require('../util/cli');
|
||||
var defaultConfig = require('../config');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function version(logger, versionArg, options, config) {
|
||||
options = options || {};
|
||||
function version(versionArg, options, config) {
|
||||
var project;
|
||||
var logger = new Logger();
|
||||
|
||||
config = defaultConfig(config);
|
||||
config = mout.object.deepFillIn(config || {}, defaultConfig);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return bump(logger, config, versionArg, options.message);
|
||||
bump(project, versionArg, options.message)
|
||||
.done(function () {
|
||||
logger.emit('end');
|
||||
}, function (error) {
|
||||
logger.emit('error', error);
|
||||
});
|
||||
|
||||
return logger;
|
||||
}
|
||||
|
||||
function bump(logger, config, versionArg, message) {
|
||||
var cwd = config.cwd || process.cwd();
|
||||
function bump(project, versionArg, message) {
|
||||
var newVersion;
|
||||
var doGitCommit = false;
|
||||
|
||||
if (!versionArg) {
|
||||
throw createError('No <version> agrument provided', 'EREADOPTIONS');
|
||||
}
|
||||
|
||||
return driver.check(cwd)
|
||||
return checkGit()
|
||||
.then(function (hasGit) {
|
||||
doGitCommit = hasGit;
|
||||
})
|
||||
.then(project.getJson.bind(project))
|
||||
.then(function (json) {
|
||||
newVersion = getNewVersion(json.version, versionArg);
|
||||
json.version = newVersion;
|
||||
})
|
||||
.then(project.saveJson.bind(project))
|
||||
.then(function () {
|
||||
return Q.all([driver.versions(cwd), driver.currentVersion(cwd)]);
|
||||
})
|
||||
.spread(function (versions, currentVersion) {
|
||||
currentVersion = currentVersion || '0.0.0';
|
||||
|
||||
if (semver.valid(versionArg)) {
|
||||
newVersion = semver.valid(versionArg);
|
||||
} else {
|
||||
newVersion = semver.inc(currentVersion, versionArg);
|
||||
|
||||
if (!newVersion) {
|
||||
throw createError('Invalid <version> argument: ' + versionArg, 'EINVALIDVERSION', { version: versionArg });
|
||||
}
|
||||
if (doGitCommit) {
|
||||
return gitCommitAndTag(newVersion, message);
|
||||
}
|
||||
|
||||
newVersion = (currentVersion[0] === 'v') ? 'v' + newVersion : newVersion;
|
||||
|
||||
if (versions) {
|
||||
versions.forEach(function (version) {
|
||||
if (semver.eq(version, newVersion)) {
|
||||
throw createError('Version exists: ' + newVersion, 'EVERSIONEXISTS', { versions: versions, newVersion: newVersion });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return driver.bump(cwd, newVersion, message).then(function () {
|
||||
return {
|
||||
oldVersion: currentVersion,
|
||||
newVersion: newVersion
|
||||
}
|
||||
});
|
||||
})
|
||||
.then(function (result) {
|
||||
logger.info('version', 'Bumped package version from ' + result.oldVersion + ' to ' + result.newVersion, result);
|
||||
|
||||
return result.newVersion;
|
||||
.then(function () {
|
||||
console.log('v' + newVersion);
|
||||
});
|
||||
}
|
||||
|
||||
var driver = {
|
||||
check: function (cwd) {
|
||||
function checkGit(cwd) {
|
||||
var gitDir = path.join(cwd, '.git');
|
||||
return Q.nfcall(fs.stat, gitDir)
|
||||
.then(function (stat) {
|
||||
if (stat.isDirectory()) {
|
||||
return checkGitStatus(cwd);
|
||||
}
|
||||
return false;
|
||||
}, function () {
|
||||
//Ignore not found .git directory
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function checkGitStatus(cwd) {
|
||||
return Q.nfcall(which, 'git')
|
||||
.fail(function (err) {
|
||||
err.code = 'ENOGIT';
|
||||
throw err;
|
||||
})
|
||||
.then(function () {
|
||||
return Q.nfcall(execFile, 'git', ['status', '--porcelain'], {env: process.env, cwd: cwd});
|
||||
})
|
||||
.then(function (value) {
|
||||
var stdout = value[0];
|
||||
var lines = filterModifiedStatusLines(stdout);
|
||||
if (lines.length) {
|
||||
throw createError('Version bump requires clean working directory', 'EWORKINGDIRECTORYDIRTY');
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function filterModifiedStatusLines(stdout) {
|
||||
return stdout.trim().split('\n')
|
||||
.filter(function (line) {
|
||||
return line.trim() && !line.match(/^\?\? /);
|
||||
}).map(function (line) {
|
||||
return line.trim();
|
||||
});
|
||||
}
|
||||
|
||||
return checkGit(cwd).then(function (hasGit) {
|
||||
if (!hasGit) {
|
||||
throw createError('Version bump currently supports only git repositories', 'ENOTGITREPOSITORY');
|
||||
}
|
||||
});
|
||||
},
|
||||
versions: function (cwd) {
|
||||
return Q.nfcall(execFile, 'git', ['tag'], {env: process.env, cwd: cwd})
|
||||
.then(function (res) {
|
||||
var versions = res[0]
|
||||
.split(/\r?\n/)
|
||||
.filter(semver.valid);
|
||||
|
||||
return versions;
|
||||
}, function () {
|
||||
return [];
|
||||
});
|
||||
},
|
||||
currentVersion: function (cwd) {
|
||||
return Q.nfcall(execFile, 'git', ['describe', '--abbrev=0', '--tags'], {env: process.env, cwd: cwd})
|
||||
.then(function (res) {
|
||||
var version = res[0]
|
||||
.split(/\r?\n/)
|
||||
.filter(semver.valid)[0];
|
||||
|
||||
return version;
|
||||
}, function () {
|
||||
return undefined;
|
||||
});
|
||||
},
|
||||
bump: function (cwd, tag, message) {
|
||||
message = message || tag;
|
||||
message = message.replace(/%s/g, tag);
|
||||
return Q.nfcall(execFile, 'git', ['commit', '-m', message, '--allow-empty'], {env: process.env, cwd: cwd}) .then(function () {
|
||||
return Q.nfcall(execFile, 'git', ['tag', tag, '-am', message], {env: process.env, cwd: cwd});
|
||||
});
|
||||
function getNewVersion(currentVersion, versionArg) {
|
||||
var newVersion = semver.valid(versionArg);
|
||||
if (!newVersion) {
|
||||
newVersion = semver.inc(currentVersion, versionArg);
|
||||
}
|
||||
if (!newVersion) {
|
||||
throw createError('Invalid version argument: `' + versionArg + '`. Usage: `bower version [<newversion> | major | minor | patch]`', 'EINVALIDVERSION');
|
||||
}
|
||||
if (currentVersion === newVersion) {
|
||||
throw createError('Version not changed', 'EVERSIONNOTCHANGED');
|
||||
}
|
||||
return newVersion;
|
||||
}
|
||||
|
||||
function checkGit() {
|
||||
var gitDir = path.join(process.cwd(), '.git');
|
||||
return Q.nfcall(fs.stat, gitDir)
|
||||
.then(function (stat) {
|
||||
if (stat.isDirectory()) {
|
||||
return checkGitStatus();
|
||||
}
|
||||
return false;
|
||||
}, function () {
|
||||
//Ignore not found .git directory
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
version.readOptions = function (argv) {
|
||||
var cli = require('../util/cli');
|
||||
function checkGitStatus() {
|
||||
return Q.nfcall(which, 'git')
|
||||
.fail(function (err) {
|
||||
err.code = 'ENOGIT';
|
||||
throw err;
|
||||
})
|
||||
.then(function () {
|
||||
return Q.nfcall(execFile, 'git', ['status', '--porcelain'], {env: process.env});
|
||||
})
|
||||
.then(function (value) {
|
||||
var stdout = value[0];
|
||||
var lines = filterModifiedStatusLines(stdout);
|
||||
if (lines.length) {
|
||||
throw createError('Git working directory not clean.\n' + lines.join('\n'), 'EWORKINGDIRECTORYDIRTY');
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
var options = cli.readOptions({
|
||||
'message': { type: String, shorthand: 'm'}
|
||||
}, argv);
|
||||
function filterModifiedStatusLines(stdout) {
|
||||
return stdout.trim().split('\n')
|
||||
.filter(function (line) {
|
||||
return line.trim() && !line.match(/^\?\? /);
|
||||
}).map(function (line) {
|
||||
return line.trim();
|
||||
});
|
||||
}
|
||||
|
||||
return [options.argv.remain[1], options];
|
||||
function gitCommitAndTag(newVersion, message) {
|
||||
message = message || 'v' + newVersion;
|
||||
message = message.replace(/%s/g, newVersion);
|
||||
return Q.nfcall(execFile, 'git', ['add', 'bower.json'], {env: process.env})
|
||||
.then(function () {
|
||||
return Q.nfcall(execFile, 'git', ['commit', '-m', message], {env: process.env});
|
||||
})
|
||||
.then(function () {
|
||||
return Q.nfcall(execFile, 'git', ['tag', newVersion, '-am', message], {env: process.env});
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
version.line = function (argv) {
|
||||
var options = version.options(argv);
|
||||
return version(options.argv.remain[1], options);
|
||||
};
|
||||
|
||||
module.exports = version;
|
||||
version.options = function (argv) {
|
||||
return cli.readOptions({
|
||||
'message': { type: String, shorthand: 'm'}
|
||||
}, argv);
|
||||
};
|
||||
|
||||
version.completion = function () {
|
||||
// TODO:
|
||||
};
|
||||
|
||||
module.exports = version;
|
||||
@@ -1,63 +1,33 @@
|
||||
var tty = require('tty');
|
||||
var object = require('mout').object;
|
||||
var bowerConfig = require('bower-config');
|
||||
var Configstore = require('configstore');
|
||||
var mout = require('mout');
|
||||
var config = require('bower-config').read();
|
||||
var cli = require('./util/cli');
|
||||
|
||||
var current;
|
||||
// Delete the json attribute because it is no longer supported
|
||||
// and conflicts with --json
|
||||
delete config.json;
|
||||
|
||||
function defaultConfig(config) {
|
||||
config = config || {};
|
||||
|
||||
return readCachedConfig(config.cwd || process.cwd(), config);
|
||||
// If interactive is auto (null), guess its value
|
||||
if (config.interactive == null) {
|
||||
config.interactive = process.bin === 'bower' && tty.isatty(1);
|
||||
}
|
||||
|
||||
function readCachedConfig(cwd, overwrites) {
|
||||
current = bowerConfig.create(cwd).load(overwrites);
|
||||
|
||||
var config = current.toObject();
|
||||
|
||||
var configstore = new Configstore('bower-github').all;
|
||||
|
||||
object.mixIn(config, configstore);
|
||||
|
||||
// If interactive is auto (null), guess its value
|
||||
if (config.interactive == null) {
|
||||
config.interactive = (
|
||||
process.bin === 'bower' &&
|
||||
tty.isatty(1) &&
|
||||
!process.env.CI
|
||||
);
|
||||
}
|
||||
|
||||
// Merge common CLI options into the config
|
||||
if (process.bin === 'bower') {
|
||||
var cli = require('./util/cli');
|
||||
|
||||
object.mixIn(config, cli.readOptions({
|
||||
force: { type: Boolean, shorthand: 'f' },
|
||||
offline: { type: Boolean, shorthand: 'o' },
|
||||
verbose: { type: Boolean, shorthand: 'V' },
|
||||
quiet: { type: Boolean, shorthand: 'q' },
|
||||
loglevel: { type: String, shorthand: 'l' },
|
||||
json: { type: Boolean, shorthand: 'j' },
|
||||
silent: { type: Boolean, shorthand: 's' }
|
||||
}));
|
||||
}
|
||||
|
||||
return config;
|
||||
// If `analytics` hasn't been explicitly set, we disable
|
||||
// it when ran programatically.
|
||||
if (config.analytics == null) {
|
||||
// Don't enable analytics on CI server unless explicitly configured.
|
||||
config.analytics = config.interactive && !process.env.CI;
|
||||
}
|
||||
|
||||
function restoreConfig() {
|
||||
if (current) {
|
||||
current.restore();
|
||||
}
|
||||
}
|
||||
// Merge common CLI options into the config
|
||||
mout.object.mixIn(config, cli.readOptions({
|
||||
force: { type: Boolean, shorthand: 'f' },
|
||||
offline: { type: Boolean, shorthand: 'o' },
|
||||
verbose: { type: Boolean, shorthand: 'V' },
|
||||
quiet: { type: Boolean, shorthand: 'q' },
|
||||
loglevel: { type: String, shorthand: 'l' },
|
||||
json: { type: Boolean, shorthand: 'j' },
|
||||
silent: { type: Boolean, shorthand: 's' }
|
||||
}));
|
||||
|
||||
function resetCache() {
|
||||
restoreConfig();
|
||||
current = undefined;
|
||||
}
|
||||
|
||||
module.exports = defaultConfig;
|
||||
module.exports.restore = restoreConfig;
|
||||
module.exports.reset = resetCache;
|
||||
module.exports = config;
|
||||
|
||||
@@ -2,15 +2,15 @@ var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var path = require('path');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('../util/rimraf');
|
||||
var fs = require('../util/fs');
|
||||
var rimraf = require('rimraf');
|
||||
var fs = require('graceful-fs');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var PackageRepository = require('./PackageRepository');
|
||||
var semver = require('../util/semver');
|
||||
var copy = require('../util/copy');
|
||||
var createError = require('../util/createError');
|
||||
var dependencyLinker = require('../util/dependencyLinker');
|
||||
var scripts = require('./scripts');
|
||||
var relativeToBaseDir = require('../util/relativeToBaseDir');
|
||||
|
||||
function Manager(config, logger) {
|
||||
this._config = config;
|
||||
@@ -24,13 +24,11 @@ function Manager(config, logger) {
|
||||
|
||||
Manager.prototype.configure = function (setup) {
|
||||
var targetsHash = {};
|
||||
|
||||
this._conflicted = {};
|
||||
|
||||
// Targets - ignore those specified in ignoredDependencies
|
||||
this._targets = mout.array.reject(setup.targets || [], function (target) {
|
||||
return mout.array.contains(this._config.ignoredDependencies, target.name );
|
||||
}, this);
|
||||
|
||||
// Targets
|
||||
this._targets = setup.targets || [];
|
||||
this._targets.forEach(function (decEndpoint) {
|
||||
decEndpoint.initialName = decEndpoint.name;
|
||||
decEndpoint.dependants = mout.object.values(decEndpoint.dependants);
|
||||
@@ -83,17 +81,6 @@ Manager.prototype.configure = function (setup) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* It starts recursive fetching of targets. The flow is as follows:
|
||||
*
|
||||
* 1. this._fetch() that calls:
|
||||
* 2. this._onFetchError() or this._onFetchSuccess() that calls:
|
||||
* 3. this._parseDependencies() that calls for all dependencies:
|
||||
* 4. this._fetch() again.
|
||||
*
|
||||
* Fetching and parsing of dependencies continues until all dependencies are fetched.
|
||||
*
|
||||
*/
|
||||
Manager.prototype.resolve = function () {
|
||||
// If already resolving, error out
|
||||
if (this._working) {
|
||||
@@ -123,43 +110,8 @@ Manager.prototype.resolve = function () {
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Manager.prototype.preinstall = function (json) {
|
||||
var that = this;
|
||||
var componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
|
||||
|
||||
// If nothing to install, skip the code bellow
|
||||
if (mout.lang.isEmpty(that._dissected)) {
|
||||
return Q.resolve({});
|
||||
}
|
||||
|
||||
return Q.nfcall(mkdirp, componentsDir)
|
||||
.then(function () {
|
||||
return scripts.preinstall(
|
||||
that._config, that._logger, that._dissected, that._installed, json
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
Manager.prototype.postinstall = function (json) {
|
||||
var that = this;
|
||||
var componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
|
||||
|
||||
// If nothing to install, skip the code bellow
|
||||
if (mout.lang.isEmpty(that._dissected)) {
|
||||
return Q.resolve({});
|
||||
}
|
||||
|
||||
return Q.nfcall(mkdirp, componentsDir)
|
||||
.then(function () {
|
||||
return scripts.postinstall(
|
||||
that._config, that._logger, that._dissected, that._installed, json
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
Manager.prototype.install = function (json) {
|
||||
var componentsDir;
|
||||
var componentsDirContents;
|
||||
var that = this;
|
||||
|
||||
// If already resolving, error out
|
||||
@@ -172,29 +124,23 @@ Manager.prototype.install = function (json) {
|
||||
return Q.resolve({});
|
||||
}
|
||||
|
||||
componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
|
||||
componentsDir = path.join(this._config.cwd, this._config.directory);
|
||||
return Q.nfcall(mkdirp, componentsDir)
|
||||
.then(function () {
|
||||
return scripts.preinstall(that._config, that._logger, that._dissected, that._installed, json);
|
||||
})
|
||||
.then(function () {
|
||||
var promises = [];
|
||||
componentsDirContents = fs.readdirSync(componentsDir);
|
||||
|
||||
mout.object.forOwn(that._dissected, function (decEndpoint, name) {
|
||||
var promise;
|
||||
var dst;
|
||||
var release = decEndpoint.pkgMeta._release;
|
||||
var exists = mout.array.contains(componentsDirContents, name);
|
||||
|
||||
that._logger.action('install', name + (release ? '#' + release : ''), that.toData(decEndpoint));
|
||||
|
||||
dst = path.join(componentsDir, name);
|
||||
|
||||
if (exists && !that._installed[name] && !that._config.force) {
|
||||
// There is a folder in the components directory that has
|
||||
// this name, but it was not installed by bower.
|
||||
that._logger.warn('skipped', name + ' was not installed because there is already a non-bower directory with that name in the components directory (' + path.relative(that._config.cwd, dst) + '). You can force installation with --force.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove existent and copy canonical dir
|
||||
promise = Q.nfcall(rimraf, dst)
|
||||
.then(copy.copyDir.bind(copy, decEndpoint.canonicalDir, dst))
|
||||
@@ -224,6 +170,9 @@ Manager.prototype.install = function (json) {
|
||||
|
||||
return Q.all(promises);
|
||||
})
|
||||
.then(function () {
|
||||
return scripts.postinstall(that._config, that._logger, that._dissected, that._installed, json);
|
||||
})
|
||||
.then(function () {
|
||||
// Sync up dissected dependencies and dependants
|
||||
// See: https://github.com/bower/bower/issues/879
|
||||
@@ -242,7 +191,13 @@ Manager.prototype.install = function (json) {
|
||||
return dissected;
|
||||
}, this);
|
||||
}, that);
|
||||
|
||||
})
|
||||
.then(function () {
|
||||
// Create symlinks for sub-dependencies
|
||||
var flattened = mout.object.mixIn({}, that._installed, that._dissected);
|
||||
return dependencyLinker.link(flattened, that._config, that._logger);
|
||||
})
|
||||
.then(function () {
|
||||
// Resolve with meaningful data
|
||||
return mout.object.map(that._dissected, function (decEndpoint) {
|
||||
return this.toData(decEndpoint);
|
||||
@@ -361,7 +316,7 @@ Manager.prototype._onFetchSuccess = function (decEndpoint, canonicalDir, pkgMeta
|
||||
resolved.push(decEndpoint);
|
||||
|
||||
// Parse dependencies
|
||||
this._parseDependencies(decEndpoint, pkgMeta);
|
||||
this._parseDependencies(decEndpoint, pkgMeta, 'dependencies');
|
||||
|
||||
// Check if there are incompatibilities for this package name
|
||||
// If there are, we need to fetch them
|
||||
@@ -386,7 +341,7 @@ Manager.prototype._onFetchSuccess = function (decEndpoint, canonicalDir, pkgMeta
|
||||
|
||||
// If the package is not targetable, flag it
|
||||
// It will be needed later so that untargetable endpoints
|
||||
// will not get * converted to ^version
|
||||
// will not get * converted to ~version
|
||||
if (!isTargetable) {
|
||||
decEndpoint.untargetable = true;
|
||||
}
|
||||
@@ -438,32 +393,18 @@ Manager.prototype._failFast = function () {
|
||||
}.bind(this), 20000);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses decEndpoint dependencies and fetches missing ones.
|
||||
*
|
||||
* It fetches all non-yet-fetching dependencies with _fetch.
|
||||
*
|
||||
*
|
||||
* @param {string} pkgMeta Metadata of package to resolve dependencies.
|
||||
*/
|
||||
Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta) {
|
||||
Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta, jsonKey) {
|
||||
var pending = [];
|
||||
|
||||
decEndpoint.dependencies = decEndpoint.dependencies || {};
|
||||
|
||||
// Parse package dependencies
|
||||
mout.object.forOwn(pkgMeta.dependencies, function (value, key) {
|
||||
mout.object.forOwn(pkgMeta[jsonKey], function (value, key) {
|
||||
var resolved;
|
||||
var fetching;
|
||||
var compatible;
|
||||
var childDecEndpoint = endpointParser.json2decomposed(key, value);
|
||||
|
||||
// Check if this depdendency should be skipped.
|
||||
if (mout.array.contains(this._config.ignoredDependencies, childDecEndpoint.name)) {
|
||||
this._logger.action('skipped', childDecEndpoint.name, this.toData(decEndpoint));
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if a compatible one is already resolved
|
||||
// If there's one, we don't need to resolve it twice
|
||||
resolved = this._resolved[key];
|
||||
@@ -527,24 +468,11 @@ Manager.prototype._parseDependencies = function (decEndpoint, pkgMeta) {
|
||||
if (pending.length > 0) {
|
||||
Q.all(pending)
|
||||
.then(function () {
|
||||
this._parseDependencies(decEndpoint, pkgMeta);
|
||||
this._parseDependencies(decEndpoint, pkgMeta, jsonKey);
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* After batch of packages has been fetched, the results are stored in _resolved hash.
|
||||
*
|
||||
* {
|
||||
* packageName: [array, of, decEndpoints]
|
||||
* }
|
||||
*
|
||||
* This method takes all possible decEndpoints and selects only one of them per packageName.
|
||||
*
|
||||
* It stores selected packages in the this._dissected array.
|
||||
*
|
||||
* The _dissected array is used later on by install method to move packages to config.directory.
|
||||
*/
|
||||
Manager.prototype._dissect = function () {
|
||||
var err;
|
||||
var componentsDir;
|
||||
@@ -587,17 +515,6 @@ Manager.prototype._dissect = function () {
|
||||
}
|
||||
}
|
||||
|
||||
// If they are equal and one of them is an exact target,
|
||||
// give higher priority
|
||||
if (!result) {
|
||||
if (first.target === first.pkgMeta.version) {
|
||||
return -1;
|
||||
}
|
||||
if (second.target === second.pkgMeta.version) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
@@ -606,7 +523,7 @@ Manager.prototype._dissect = function () {
|
||||
// If they are not, the resolver is incapable of handling targets
|
||||
semvers.forEach(function (decEndpoint) {
|
||||
if (decEndpoint.newly && decEndpoint.target === '*' && !decEndpoint.untargetable) {
|
||||
decEndpoint.target = '^' + decEndpoint.pkgMeta.version;
|
||||
decEndpoint.target = '~' + decEndpoint.pkgMeta.version;
|
||||
decEndpoint.originalTarget = '*';
|
||||
}
|
||||
});
|
||||
@@ -633,46 +550,34 @@ Manager.prototype._dissect = function () {
|
||||
return;
|
||||
}
|
||||
|
||||
// Packages that didn't have any conflicts in resolution yet have
|
||||
// "resolution" entry in bower.json are deemed unnecessary
|
||||
//
|
||||
// Probably in future we want to use them for force given resolution, but for now...
|
||||
// See: https://github.com/bower/bower/issues/1362
|
||||
this._logger.warn('extra-resolution', 'Unnecessary resolution: ' + name + '#' + resolution, {
|
||||
this._logger.info('resolution', 'Removed unnecessary ' + name + '#' + resolution + ' resolution', {
|
||||
name: name,
|
||||
resolution: resolution,
|
||||
action: 'delete'
|
||||
});
|
||||
|
||||
delete this._resolutions[name];
|
||||
}, this);
|
||||
|
||||
// Filter only packages that need to be installed
|
||||
componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
|
||||
componentsDir = path.resolve(that._config.cwd, that._config.directory);
|
||||
this._dissected = mout.object.filter(suitables, function (decEndpoint, name) {
|
||||
var installedMeta = this._installed[name];
|
||||
var dst;
|
||||
|
||||
// Skip linked dependencies
|
||||
if (decEndpoint.linked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if source is the same as dest
|
||||
// FIXME: Shoudn't we force installation if force flag is used here?
|
||||
dst = path.join(componentsDir, name);
|
||||
if (dst === decEndpoint.canonicalDir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We skip installing decEndpoint, if:
|
||||
// 1. We have installed package with the same name
|
||||
// 2. Packages have matching meta fields (needs explanation)
|
||||
// 3. We didn't force the installation
|
||||
// Analyse a few props
|
||||
if (installedMeta &&
|
||||
installedMeta._target === decEndpoint.target &&
|
||||
installedMeta._originalSource === decEndpoint.source &&
|
||||
installedMeta._release === decEndpoint.pkgMeta._release
|
||||
) {
|
||||
return this._config.force;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip if source is the same as dest
|
||||
dst = path.join(componentsDir, name);
|
||||
if (dst === decEndpoint.canonicalDir) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -687,9 +592,8 @@ Manager.prototype._electSuitable = function (name, semvers, nonSemvers) {
|
||||
var unresolvable;
|
||||
var dataPicks;
|
||||
var save;
|
||||
var choices;
|
||||
var picks = [];
|
||||
var versionRegex = /(?:[\d\w]\.){2}[\d\w](?:.)*/;
|
||||
var picksReleases;
|
||||
|
||||
// If there are both semver and non-semver, there's no way
|
||||
// to figure out the suitable one
|
||||
@@ -844,21 +748,15 @@ Manager.prototype._electSuitable = function (name, semvers, nonSemvers) {
|
||||
picks: dataPicks
|
||||
});
|
||||
|
||||
picksReleases = picks.map(function (pick) { return pick.pkgMeta._release; });
|
||||
choices = picks.map(function (pick, index) { return index + 1; });
|
||||
return Q.nfcall(this._logger.prompt.bind(this._logger), {
|
||||
type: 'input',
|
||||
message: 'Answer',
|
||||
message: 'Answer:',
|
||||
validate: function (choice) {
|
||||
var invalidChoice = 'Invalid choice';
|
||||
|
||||
if (choice.match(versionRegex)) {
|
||||
return picksReleases.indexOf(choice) != -1 ? true : invalidChoice;
|
||||
}
|
||||
|
||||
choice = Number(mout.string.trim(choice.trim(), '!'));
|
||||
|
||||
if (!choice || choice < 1 || choice > picks.length) {
|
||||
return invalidChoice;
|
||||
return 'Invalid choice';
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -870,13 +768,8 @@ Manager.prototype._electSuitable = function (name, semvers, nonSemvers) {
|
||||
// Sanitize choice
|
||||
choice = choice.trim();
|
||||
save = /^!/.test(choice) || /!$/.test(choice); // Save if prefixed or suffixed with !
|
||||
|
||||
if (choice.match(versionRegex)) {
|
||||
pick = picks[picksReleases.indexOf(choice)];
|
||||
} else {
|
||||
choice = Number(mout.string.trim(choice, '!'));
|
||||
pick = picks[choice - 1];
|
||||
}
|
||||
choice = Number(mout.string.trim(choice, '!'));
|
||||
pick = picks[choice - 1];
|
||||
|
||||
// Save resolution
|
||||
if (save) {
|
||||
@@ -905,28 +798,10 @@ Manager.prototype._storeResolution = function (pick) {
|
||||
this._resolutions[name] = resolution;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if some endpoint is compatible with already resolved target.
|
||||
*
|
||||
* It is used in two situations:
|
||||
* * checks if resolved component matches dependency constraint
|
||||
* * checks if not resolved component matches already fetched component
|
||||
*
|
||||
* If candidate matches already resolved component, it won't be downloaded.
|
||||
*
|
||||
* @param {Endpoint} candidate endpoint
|
||||
* @param {Endpoint} resolved endpoint
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
Manager.prototype._areCompatible = function (candidate, resolved) {
|
||||
var resolvedVersion;
|
||||
var highestCandidate;
|
||||
var highestResolved;
|
||||
var candidateIsRange = semver.validRange(candidate.target);
|
||||
var resolvedIsRange = semver.validRange(resolved.target);
|
||||
var candidateIsVersion = semver.valid(candidate.target);
|
||||
var resolvedIsVersion = semver.valid(resolved.target);
|
||||
|
||||
// Check if targets are equal
|
||||
if (candidate.target === resolved.target) {
|
||||
@@ -934,77 +809,30 @@ Manager.prototype._areCompatible = function (candidate, resolved) {
|
||||
}
|
||||
|
||||
resolvedVersion = resolved.pkgMeta && resolved.pkgMeta.version;
|
||||
// If there is no pkgMeta, resolvedVersion is downloading now
|
||||
// Check based on target requirements
|
||||
if (!resolvedVersion) {
|
||||
// If one of the targets is range and other is version,
|
||||
// check version against the range
|
||||
if (candidateIsVersion && resolvedIsRange) {
|
||||
return semver.satisfies(candidate.target, resolved.target);
|
||||
}
|
||||
|
||||
if (resolvedIsVersion && candidateIsRange) {
|
||||
return semver.satisfies(resolved.target, candidate.target);
|
||||
}
|
||||
|
||||
if (resolvedIsVersion && candidateIsVersion) {
|
||||
return semver.eq(resolved.target, candidate.target);
|
||||
}
|
||||
|
||||
// If both targets are range, check that both have same
|
||||
// higher cap
|
||||
if (resolvedIsRange && candidateIsRange) {
|
||||
highestCandidate =
|
||||
this._getCap(semver.toComparators(candidate.target), 'highest');
|
||||
highestResolved =
|
||||
this._getCap(semver.toComparators(resolved.target), 'highest');
|
||||
|
||||
// This never happens, but you can't be sure without tests
|
||||
if (!highestResolved.version || !highestCandidate.version) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return semver.eq(highestCandidate.version, highestResolved.version) &&
|
||||
highestCandidate.comparator === highestResolved.comparator;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If target is a version, compare against the resolved version
|
||||
if (candidateIsVersion) {
|
||||
if (semver.valid(candidate.target)) {
|
||||
return semver.eq(candidate.target, resolvedVersion);
|
||||
}
|
||||
|
||||
// If target is a range, check if resolved version satisfies it
|
||||
if (candidateIsRange) {
|
||||
return semver.satisfies(resolvedVersion, candidate.target);
|
||||
// If target is a range, check if the max versions of the range are the same
|
||||
// and if the resolved version satisfies the candidate target
|
||||
if (semver.validRange(candidate.target) && semver.validRange(resolved.target)) {
|
||||
highestCandidate = this._getCap(semver.toComparators(candidate.target), 'highest');
|
||||
highestResolved = this._getCap(semver.toComparators(resolved.target), 'highest');
|
||||
|
||||
return highestCandidate.version && highestResolved.version &&
|
||||
semver.eq(highestCandidate.version, highestResolved.version) &&
|
||||
highestCandidate.comparator === highestResolved.comparator &&
|
||||
semver.satisfies(resolvedVersion, candidate.target);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets highest/lowest version from set of comparators.
|
||||
*
|
||||
* The only thing that matters for this function is version number.
|
||||
* Returned comparator is splitted to comparator and version parts.
|
||||
*
|
||||
* It is used to receive lowest / highest bound of toComparators result:
|
||||
* semver.toComparators('~0.1.1') // => [ [ '>=0.1.1-0', '<0.2.0-0' ] ]
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* _getCap([['>=2.1.1-0', '<2.2.0-0'], '<3.2.0'], 'highest')
|
||||
* // => { comparator: '<', version: '3.2.0' }
|
||||
*
|
||||
* _getCap([['>=2.1.1-0', '<2.2.0-0'], '<3.2.0'], 'lowest')
|
||||
* // => { comparator: '>=', version: '2.1.1-0' }
|
||||
*
|
||||
* @param {Array.<Array|string>} comparators
|
||||
* @param {string} side, 'highest' (default) or 'lowest'
|
||||
*
|
||||
* @return {{ comparator: string, version: string }}
|
||||
*/
|
||||
Manager.prototype._getCap = function (comparators, side) {
|
||||
var matches;
|
||||
var candidate;
|
||||
@@ -1040,23 +868,6 @@ Manager.prototype._getCap = function (comparators, side) {
|
||||
return cap;
|
||||
};
|
||||
|
||||
/**
|
||||
* Filters out unique endpoints, comparing by name and then source.
|
||||
*
|
||||
* It leaves last matching endpoint.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* manager._uniquify([
|
||||
* { name: 'foo', source: 'google.com' },
|
||||
* { name: 'foo', source: 'facebook.com' }
|
||||
* ]);
|
||||
* // => { name: 'foo', source: 'facebook.com' }
|
||||
*
|
||||
* @param {Array.<Endpoint>} decEndpoints
|
||||
* @return {Array.<Endpoint>} Filtered elements of decEndpoints
|
||||
*
|
||||
*/
|
||||
Manager.prototype._uniquify = function (decEndpoints) {
|
||||
var length = decEndpoints.length;
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
var ResolveCache = require('./ResolveCache');
|
||||
var resolverFactory = require('./resolverFactory');
|
||||
var createError = require('../util/createError');
|
||||
var RegistryClient = require('bower-registry-client');
|
||||
|
||||
function PackageRepository(config, logger) {
|
||||
|
||||
var registryOptions;
|
||||
|
||||
this._config = config;
|
||||
@@ -15,7 +14,6 @@ function PackageRepository(config, logger) {
|
||||
// Instantiate the registry
|
||||
registryOptions = mout.object.deepMixIn({}, this._config);
|
||||
registryOptions.cache = this._config.storage.registry;
|
||||
|
||||
this._registryClient = new RegistryClient(registryOptions, logger);
|
||||
|
||||
// Instantiate the resolve cache
|
||||
@@ -40,18 +38,15 @@ PackageRepository.prototype.fetch = function (decEndpoint) {
|
||||
that._extendLog(log, info);
|
||||
});
|
||||
|
||||
return this._getResolver(decEndpoint, logger)
|
||||
// Get the appropriate resolver
|
||||
return resolverFactory(decEndpoint, this._config, logger, this._registryClient)
|
||||
// Decide if we retrieve from the cache or not
|
||||
// Also decide if we validate the cached entry or not
|
||||
.then(function (resolver) {
|
||||
info.resolver = resolver;
|
||||
isTargetable = resolver.constructor.isTargetable;
|
||||
|
||||
if (!resolver.isCacheable()) {
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
// If force flag is used, bypass cache, but write to cache anyway
|
||||
// If force flag is used, bypass cache
|
||||
if (that._config.force) {
|
||||
logger.action('resolve', resolver.getSource() + '#' + resolver.getTarget());
|
||||
return that._resolve(resolver, logger);
|
||||
@@ -89,10 +84,10 @@ PackageRepository.prototype.fetch = function (decEndpoint) {
|
||||
}
|
||||
|
||||
// Otherwise check for new contents
|
||||
logger.action('validate', (pkgMeta._release ? pkgMeta._release + ' against ' : '') +
|
||||
logger.action('validate', (pkgMeta._release ? pkgMeta._release + ' against ': '') +
|
||||
resolver.getSource() + (resolver.getTarget() ? '#' + resolver.getTarget() : ''));
|
||||
|
||||
return resolver.hasNew(pkgMeta)
|
||||
return resolver.hasNew(canonicalDir, pkgMeta)
|
||||
.then(function (hasNew) {
|
||||
// If there are no new contents, resolve to
|
||||
// the cached one
|
||||
@@ -118,15 +113,15 @@ PackageRepository.prototype.fetch = function (decEndpoint) {
|
||||
PackageRepository.prototype.versions = function (source) {
|
||||
// Resolve the source using the factory because the
|
||||
// source can actually be a registry name
|
||||
return this._getResolver({ source: source })
|
||||
.then(function (resolver) {
|
||||
return resolverFactory.getConstructor(source, this._config, this._registryClient)
|
||||
.spread(function (ConcreteResolver, source) {
|
||||
// If offline, resolve using the cached versions
|
||||
if (this._config.offline) {
|
||||
return this._resolveCache.versions(resolver.getSource());
|
||||
return this._resolveCache.versions(source);
|
||||
}
|
||||
|
||||
// Otherwise, fetch remotely
|
||||
return resolver.constructor.versions(resolver.getSource());
|
||||
return ConcreteResolver.versions(source);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
@@ -169,33 +164,20 @@ PackageRepository.clearRuntimeCache = function () {
|
||||
|
||||
// ---------------------
|
||||
|
||||
PackageRepository.prototype._getResolver = function (decEndpoint, logger) {
|
||||
logger = logger || this._logger;
|
||||
|
||||
// Get the appropriate resolver
|
||||
return resolverFactory(decEndpoint, { config: this._config, logger: logger }, this._registryClient);
|
||||
};
|
||||
|
||||
PackageRepository.prototype._resolve = function (resolver, logger) {
|
||||
var that = this;
|
||||
|
||||
// Resolve the resolver
|
||||
return resolver.resolve()
|
||||
// Store in the cache
|
||||
.then(function (canonicalDir) {
|
||||
if (!resolver.isCacheable()) {
|
||||
return canonicalDir;
|
||||
}
|
||||
|
||||
return that._resolveCache.store(canonicalDir, resolver.getPkgMeta());
|
||||
})
|
||||
return this._resolveCache.store(canonicalDir, resolver.getPkgMeta());
|
||||
}.bind(this))
|
||||
// Resolve promise with canonical dir and package meta
|
||||
.then(function (dir) {
|
||||
var pkgMeta = resolver.getPkgMeta();
|
||||
|
||||
logger.info('resolved', resolver.getSource() + (pkgMeta._release ? '#' + pkgMeta._release : ''));
|
||||
return [dir, pkgMeta, resolver.constructor.isTargetable()];
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
PackageRepository.prototype._extendLog = function (log, info) {
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
var glob = require('glob');
|
||||
var path = require('path');
|
||||
var fs = require('../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var rimraf = require('../util/rimraf');
|
||||
var rimraf = require('rimraf');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var Logger = require('bower-logger');
|
||||
var md5 = require('md5-hex');
|
||||
var Manager = require('./Manager');
|
||||
var defaultConfig = require('../config');
|
||||
var semver = require('../util/semver');
|
||||
var md5 = require('../util/md5');
|
||||
var createError = require('../util/createError');
|
||||
var readJson = require('../util/readJson');
|
||||
var validLink = require('../util/validLink');
|
||||
var scripts = require('./scripts');
|
||||
var relativeToBaseDir = require('../util/relativeToBaseDir');
|
||||
|
||||
function Project(config, logger) {
|
||||
this._config = config;
|
||||
// This is the only architecture component that ensures defaults
|
||||
// on config and logger
|
||||
// The reason behind it is that users can likely use this component
|
||||
// directly if commands do not fulfil their needs
|
||||
this._config = config || defaultConfig;
|
||||
this._logger = logger || new Logger();
|
||||
this._manager = new Manager(this._config, this._logger);
|
||||
|
||||
@@ -25,7 +29,7 @@ function Project(config, logger) {
|
||||
|
||||
// -----------------
|
||||
|
||||
Project.prototype.install = function (decEndpoints, options, config) {
|
||||
Project.prototype.install = function (decEndpoints, options) {
|
||||
var that = this;
|
||||
var targets = [];
|
||||
var resolved = {};
|
||||
@@ -37,28 +41,21 @@ Project.prototype.install = function (decEndpoints, options, config) {
|
||||
}
|
||||
|
||||
this._options = options || {};
|
||||
this._config = config || {};
|
||||
this._working = true;
|
||||
|
||||
// Analyse the project
|
||||
return this._analyse()
|
||||
.spread(function (json, tree) {
|
||||
// It shows an error when issuing `bower install`
|
||||
// and no bower.json is present in current directory
|
||||
if (!that._jsonFile && decEndpoints.length === 0 ) {
|
||||
throw createError('No bower.json present', 'ENOENT');
|
||||
}
|
||||
|
||||
// Recover tree
|
||||
that.walkTree(tree, function (node, name) {
|
||||
if (node.incompatible) {
|
||||
incompatibles.push(node);
|
||||
} else if (node.missing || node.different || that._config.force) {
|
||||
if (node.missing || node.different) {
|
||||
targets.push(node);
|
||||
} else if (node.incompatible) {
|
||||
incompatibles.push(node);
|
||||
} else {
|
||||
resolved[name] = node;
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
|
||||
// Add decomposed endpoints as targets
|
||||
decEndpoints = decEndpoints || [];
|
||||
@@ -74,33 +71,21 @@ Project.prototype.install = function (decEndpoints, options, config) {
|
||||
// Bootstrap the process
|
||||
return that._bootstrap(targets, resolved, incompatibles);
|
||||
})
|
||||
.then(function () {
|
||||
return that._manager.preinstall(that._json);
|
||||
})
|
||||
.then(function () {
|
||||
return that._manager.install(that._json);
|
||||
})
|
||||
.then(function (installed) {
|
||||
// Handle save and saveDev options
|
||||
if (that._options.save || that._options.saveDev || that._options.saveExact || that._config.save || that._config.saveExact) {
|
||||
if (that._options.save || that._options.saveDev) {
|
||||
// Cycle through the specified endpoints
|
||||
decEndpoints.forEach(function (decEndpoint) {
|
||||
var jsonEndpoint;
|
||||
|
||||
jsonEndpoint = endpointParser.decomposed2json(decEndpoint);
|
||||
|
||||
if (that._options.saveExact || that._config.saveExact) {
|
||||
if (decEndpoint.name !== decEndpoint.source) {
|
||||
jsonEndpoint[decEndpoint.name] = decEndpoint.source + '#' + decEndpoint.pkgMeta.version;
|
||||
} else {
|
||||
jsonEndpoint[decEndpoint.name] = decEndpoint.pkgMeta.version;
|
||||
}
|
||||
if (that._options.save) {
|
||||
that._json.dependencies = mout.object.mixIn(that._json.dependencies || {}, jsonEndpoint);
|
||||
}
|
||||
|
||||
if (that._options.saveDev) {
|
||||
that._json.devDependencies = mout.object.mixIn(that._json.devDependencies || {}, jsonEndpoint);
|
||||
} else {
|
||||
that._json.dependencies = mout.object.mixIn(that._json.dependencies || {}, jsonEndpoint);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -108,9 +93,7 @@ Project.prototype.install = function (decEndpoints, options, config) {
|
||||
// Save JSON, might contain changes to dependencies and resolutions
|
||||
return that.saveJson()
|
||||
.then(function () {
|
||||
return that._manager.postinstall(that._json).then(function () {
|
||||
return installed;
|
||||
});
|
||||
return installed;
|
||||
});
|
||||
})
|
||||
.fin(function () {
|
||||
@@ -191,19 +174,11 @@ Project.prototype.update = function (names, options) {
|
||||
|
||||
// Bootstrap the process
|
||||
return that._bootstrap(targets, resolved, incompatibles)
|
||||
.then(function () {
|
||||
return that._manager.preinstall(that._json);
|
||||
})
|
||||
.then(function () {
|
||||
return that._manager.install(that._json);
|
||||
})
|
||||
.then(function (installed) {
|
||||
// Save JSON, might contain changes to resolutions
|
||||
return that.saveJson()
|
||||
.then(function () {
|
||||
return that._manager.postinstall(that._json).then(function () {
|
||||
return installed;
|
||||
});
|
||||
return installed;
|
||||
});
|
||||
});
|
||||
})
|
||||
@@ -213,20 +188,6 @@ Project.prototype.update = function (names, options) {
|
||||
});
|
||||
};
|
||||
|
||||
function resolveUrlNames(names, flattened)
|
||||
{
|
||||
for (var i = 0; i < names.length; i++)
|
||||
if (! flattened[names[i]])
|
||||
{
|
||||
var url = names[i].trim().replace(/\/$/, '');
|
||||
var packName;
|
||||
for (packName in flattened)
|
||||
if (! ( !flattened[packName].source))
|
||||
if (url == flattened[packName].source.trim().replace(/\/$/, ''))
|
||||
names[i] = packName;
|
||||
}
|
||||
}
|
||||
|
||||
Project.prototype.uninstall = function (names, options) {
|
||||
var that = this;
|
||||
var packages = {};
|
||||
@@ -244,7 +205,6 @@ Project.prototype.uninstall = function (names, options) {
|
||||
// Fill in the packages to be uninstalled
|
||||
.spread(function (json, tree, flattened) {
|
||||
var promise = Q.resolve();
|
||||
resolveUrlNames(names, flattened);
|
||||
|
||||
names.forEach(function (name) {
|
||||
var decEndpoint = flattened[name];
|
||||
@@ -502,8 +462,7 @@ Project.prototype._analyse = function () {
|
||||
var isSaved = jsonCopy.dependencies[key] || jsonCopy.devDependencies[key];
|
||||
|
||||
// The _direct propery is saved by the manager when .newly is specified
|
||||
// It may happen pkgMeta is undefined if package is uninstalled
|
||||
if (!isSaved && pkgMeta && pkgMeta._direct) {
|
||||
if (!isSaved && pkgMeta._direct) {
|
||||
decEndpoint.extraneous = true;
|
||||
|
||||
if (decEndpoint.linked) {
|
||||
@@ -562,7 +521,9 @@ Project.prototype._bootstrap = function (targets, resolved, incompatibles) {
|
||||
if (!mout.object.size(this._json.resolutions)) {
|
||||
delete this._json.resolutions;
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this))
|
||||
// Install resolved ones
|
||||
.then(this._manager.install.bind(this._manager, this._json));
|
||||
};
|
||||
|
||||
Project.prototype._readJson = function () {
|
||||
@@ -572,29 +533,9 @@ Project.prototype._readJson = function () {
|
||||
return Q.resolve(this._json);
|
||||
}
|
||||
|
||||
return Q.fcall(function () {
|
||||
// This will throw if package.json does not exist
|
||||
return fs.readFileSync(path.join(that._config.cwd, 'package.json'));
|
||||
})
|
||||
.then(function (buffer) {
|
||||
// If package.json exists, use it's values as defaults
|
||||
var defaults = {}, npm = JSON.parse(buffer.toString());
|
||||
|
||||
defaults.name = npm.name || path.basename(that._config.cwd) || 'root';
|
||||
defaults.description = npm.description;
|
||||
defaults.main = npm.main;
|
||||
defaults.authors = npm.contributors || npm.author;
|
||||
defaults.license = npm.license;
|
||||
defaults.keywords = npm.keywords;
|
||||
|
||||
return defaults;
|
||||
})
|
||||
.catch(function (err) {
|
||||
// Most likely no package.json so just set default name
|
||||
return { name: path.basename(that._config.cwd) || 'root' };
|
||||
})
|
||||
.then(function (defaults) {
|
||||
return that._json = readJson(that._config.cwd, { assume: defaults, logger: that._logger });
|
||||
// Read local json
|
||||
return this._json = readJson(this._config.cwd, {
|
||||
assume: { name: path.basename(this._config.cwd) || 'root' }
|
||||
})
|
||||
.spread(function (json, deprecated, assumed) {
|
||||
var jsonStr;
|
||||
@@ -623,7 +564,7 @@ Project.prototype._readInstalled = function () {
|
||||
|
||||
// Gather all folders that are actual packages by
|
||||
// looking for the package metadata file
|
||||
componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
|
||||
componentsDir = path.join(this._config.cwd, this._config.directory);
|
||||
return this._installed = Q.nfcall(glob, '*/.bower.json', {
|
||||
cwd: componentsDir,
|
||||
dot: true
|
||||
@@ -664,7 +605,7 @@ Project.prototype._readLinks = function () {
|
||||
var that = this;
|
||||
|
||||
// Read directory, looking for links
|
||||
componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
|
||||
componentsDir = path.join(this._config.cwd, this._config.directory);
|
||||
return Q.nfcall(fs.readdir, componentsDir)
|
||||
.then(function (filenames) {
|
||||
var promises;
|
||||
@@ -756,7 +697,7 @@ Project.prototype._removePackages = function (packages) {
|
||||
}
|
||||
|
||||
// Remove from json only if successfully deleted
|
||||
if ((that._options.save || that._config.save) && that._json.dependencies) {
|
||||
if (that._options.save && that._json.dependencies) {
|
||||
promise = promise
|
||||
.then(function () {
|
||||
delete that._json.dependencies[name];
|
||||
@@ -779,9 +720,6 @@ Project.prototype._removePackages = function (packages) {
|
||||
.then(function () {
|
||||
return that.saveJson();
|
||||
})
|
||||
// Run post-uninstall hook before resolving with removed packages.
|
||||
.then(scripts.postuninstall.bind(
|
||||
null, that._config, that._logger, packages, that._installed, that._json))
|
||||
// Resolve with removed packages
|
||||
.then(function () {
|
||||
return mout.object.filter(packages, function (dir) {
|
||||
@@ -790,7 +728,7 @@ Project.prototype._removePackages = function (packages) {
|
||||
});
|
||||
};
|
||||
|
||||
Project.prototype._restoreNode = function (node, flattened, jsonKey, processed) {
|
||||
Project.prototype._restoreNode = function (node, flattened, jsonKey) {
|
||||
var deps;
|
||||
|
||||
// Do not restore if the node is missing
|
||||
@@ -800,11 +738,10 @@ Project.prototype._restoreNode = function (node, flattened, jsonKey, processed)
|
||||
|
||||
node.dependencies = node.dependencies || {};
|
||||
node.dependants = node.dependants || {};
|
||||
processed = processed || {};
|
||||
|
||||
// Only process deps that are not yet processed
|
||||
deps = mout.object.filter(node.pkgMeta[jsonKey], function (value, key) {
|
||||
return !processed[node.name + ':' + key];
|
||||
return !node.dependencies[key];
|
||||
});
|
||||
|
||||
mout.object.forOwn(deps, function (value, key) {
|
||||
@@ -851,17 +788,15 @@ Project.prototype._restoreNode = function (node, flattened, jsonKey, processed)
|
||||
|
||||
// Cross reference
|
||||
node.dependencies[key] = restored;
|
||||
processed[node.name + ':' + key] = true;
|
||||
|
||||
restored.dependants = restored.dependants || {};
|
||||
restored.dependants[node.name] = mout.object.mixIn({}, node); // We need to clone due to shared objects in the manager!
|
||||
|
||||
// Call restore for this dependency
|
||||
this._restoreNode(restored, flattened, 'dependencies', processed);
|
||||
this._restoreNode(restored, flattened, 'dependencies');
|
||||
|
||||
// Do the same for the incompatible local package
|
||||
if (local && restored !== local) {
|
||||
this._restoreNode(local, flattened, 'dependencies', processed);
|
||||
this._restoreNode(local, flattened, 'dependencies');
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
var fs = require('../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('../util/rimraf');
|
||||
var rimraf = require('rimraf');
|
||||
var LRU = require('lru-cache');
|
||||
var lockFile = require('lockfile');
|
||||
var md5 = require('md5-hex');
|
||||
var semver = require('../util/semver');
|
||||
var readJson = require('../util/readJson');
|
||||
var copy = require('../util/copy');
|
||||
var md5 = require('../util/md5');
|
||||
|
||||
function ResolveCache(config) {
|
||||
// TODO: Make some config entries, such as:
|
||||
|
||||
@@ -1,228 +1,166 @@
|
||||
var Q = require('q');
|
||||
var fs = require('../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var resolvers = require('./resolvers');
|
||||
var createError = require('../util/createError');
|
||||
var resolve = require('../util/resolve');
|
||||
|
||||
var pluginResolverFactory = require('./resolvers/pluginResolverFactory');
|
||||
function createInstance(decEndpoint, config, logger, registryClient) {
|
||||
return getConstructor(decEndpoint.source, config, registryClient)
|
||||
.spread(function (ConcreteResolver, source, fromRegistry) {
|
||||
var decEndpointCopy = mout.object.pick(decEndpoint, ['name', 'target']);
|
||||
|
||||
function createInstance(decEndpoint, options, registryClient) {
|
||||
decEndpoint = mout.object.pick(decEndpoint, ['name', 'target', 'source']);
|
||||
decEndpointCopy.source = source;
|
||||
|
||||
options.version = require('../version');
|
||||
// Signal if it was fetched from the registry
|
||||
if (fromRegistry) {
|
||||
decEndpoint.registry = true;
|
||||
// If no name was specified, assume the name from the registry
|
||||
if (!decEndpointCopy.name) {
|
||||
decEndpointCopy.name = decEndpoint.name = decEndpoint.source;
|
||||
}
|
||||
}
|
||||
|
||||
return getConstructor(decEndpoint, options, registryClient)
|
||||
.spread(function (ConcreteResolver, decEndpoint) {
|
||||
return new ConcreteResolver(decEndpoint, options.config, options.logger);
|
||||
return new ConcreteResolver(decEndpointCopy, config, logger);
|
||||
});
|
||||
}
|
||||
|
||||
function getConstructor(decEndpoint, options, registryClient) {
|
||||
var source = decEndpoint.source;
|
||||
var config = options.config;
|
||||
|
||||
// Below we try a series of async tests to guess the type of resolver to use
|
||||
// If a step was unable to guess the resolver, it returns undefined
|
||||
// If a step can guess the resolver, it returns with constructor of resolver
|
||||
|
||||
var promise = Q.resolve();
|
||||
|
||||
var addResolver = function (resolverFactory) {
|
||||
promise = promise.then(function (result) {
|
||||
if (result === undefined) {
|
||||
return resolverFactory(decEndpoint, options);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Plugin resolvers.
|
||||
//
|
||||
// It requires each resolver defined in config.resolvers and calls
|
||||
// its "match" to check if given resolves supports given decEndpoint
|
||||
addResolver(function () {
|
||||
var selectedResolver;
|
||||
var resolverNames;
|
||||
|
||||
if (Array.isArray(config.resolvers)) {
|
||||
resolverNames = config.resolvers;
|
||||
} else if (!!config.resolvers) {
|
||||
resolverNames = config.resolvers.split(',');
|
||||
} else {
|
||||
resolverNames = [];
|
||||
}
|
||||
|
||||
var resolverPromises = resolverNames.map(function (resolverName) {
|
||||
|
||||
|
||||
var resolver = resolvers[resolverName];
|
||||
|
||||
if (resolver === undefined) {
|
||||
var resolverPath = resolve(resolverName, { cwd: config.cwd });
|
||||
|
||||
if (resolverPath === undefined) {
|
||||
throw createError('Bower resolver not found: ' + resolverName, 'ENORESOLVER')
|
||||
}
|
||||
|
||||
resolver = pluginResolverFactory(require(resolverPath), options);
|
||||
}
|
||||
|
||||
return function () {
|
||||
if (selectedResolver === undefined) {
|
||||
var match = resolver.match.bind(resolver);
|
||||
|
||||
return Q.fcall(match, source).then(function (result) {
|
||||
if (result) {
|
||||
return selectedResolver = resolver;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return selectedResolver;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return resolverPromises.reduce(Q.when, new Q(undefined)).then(function (resolver) {
|
||||
if (resolver) {
|
||||
return Q.fcall(resolver.locate.bind(resolver), decEndpoint.source).then(function (result) {
|
||||
if (result && result !== decEndpoint.source) {
|
||||
decEndpoint.source = result;
|
||||
decEndpoint.registry = true;
|
||||
return getConstructor(decEndpoint, options, registryClient);
|
||||
} else {
|
||||
return [resolver, decEndpoint];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
function getConstructor(source, config, registryClient) {
|
||||
var absolutePath,
|
||||
promise;
|
||||
|
||||
// Git case: git git+ssh, git+http, git+https
|
||||
// .git at the end (probably ssh shorthand)
|
||||
// git@ at the start
|
||||
addResolver(function () {
|
||||
if (/^git(\+(ssh|https?))?:\/\//i.test(source) || /\.git\/?$/i.test(source) || /^git@/i.test(source)) {
|
||||
decEndpoint.source = source.replace(/^git\+/, '');
|
||||
if (/^git(\+(ssh|https?))?:\/\//i.test(source) || /\.git\/?$/i.test(source) || /^git@/i.test(source)) {
|
||||
source = source.replace(/^git\+/, '');
|
||||
return Q.fcall(function () {
|
||||
|
||||
// If it's a GitHub repository, return the specialized resolver
|
||||
if (resolvers.GitHub.getOrgRepoPair(source)) {
|
||||
return [resolvers.GitHub, decEndpoint];
|
||||
return [resolvers.GitHub, source];
|
||||
}
|
||||
|
||||
return [resolvers.GitRemote, decEndpoint];
|
||||
}
|
||||
});
|
||||
return [resolvers.GitRemote, source];
|
||||
});
|
||||
}
|
||||
|
||||
// SVN case: svn, svn+ssh, svn+http, svn+https, svn+file
|
||||
addResolver(function () {
|
||||
if (/^svn(\+(ssh|https?|file))?:\/\//i.test(source)) {
|
||||
return [resolvers.Svn, decEndpoint];
|
||||
}
|
||||
});
|
||||
if (/^svn(\+(ssh|https?|file))?:\/\//i.test(source)) {
|
||||
return Q.fcall(function () {
|
||||
return [resolvers.Svn, source];
|
||||
});
|
||||
}
|
||||
|
||||
// URL case
|
||||
addResolver(function () {
|
||||
if (/^https?:\/\//i.exec(source)) {
|
||||
return [resolvers.Url, decEndpoint];
|
||||
}
|
||||
});
|
||||
if (/^https?:\/\//i.exec(source)) {
|
||||
return Q.fcall(function () {
|
||||
return [resolvers.Url, source];
|
||||
});
|
||||
}
|
||||
|
||||
// Below we try a series of async tests to guess the type of resolver to use
|
||||
// If a step was unable to guess the resolver, it throws an error
|
||||
// If a step was able to guess the resolver, it resolves with a function
|
||||
// That function returns a promise that will resolve with the concrete type
|
||||
|
||||
// If source is ./ or ../ or an absolute path
|
||||
absolutePath = path.resolve(config.cwd, source);
|
||||
|
||||
addResolver(function () {
|
||||
var absolutePath = path.resolve(config.cwd, source);
|
||||
if (/^\.\.?[\/\\]/.test(source) || /^~\//.test(source) || path.normalize(source).replace(/[\/\\]+$/, '') === absolutePath) {
|
||||
promise = Q.nfcall(fs.stat, path.join(absolutePath, '.git'))
|
||||
.then(function (stats) {
|
||||
if (stats.isDirectory()) {
|
||||
return function () {
|
||||
return Q.resolve([resolvers.GitFs, absolutePath]);
|
||||
};
|
||||
}
|
||||
|
||||
if (/^\.\.?[\/\\]/.test(source) || /^~\//.test(source) ||
|
||||
path.normalize(source).replace(/[\/\\]+$/, '') === absolutePath
|
||||
) {
|
||||
return Q.nfcall(fs.stat, path.join(absolutePath, '.git'))
|
||||
throw new Error('Not a Git repository');
|
||||
})
|
||||
// If not, check if source is a valid Subversion repository
|
||||
.fail(function () {
|
||||
return Q.nfcall(fs.stat, path.join(absolutePath, '.svn'))
|
||||
.then(function (stats) {
|
||||
decEndpoint.source = absolutePath;
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return Q.resolve([resolvers.GitFs, decEndpoint]);
|
||||
return function () {
|
||||
return Q.resolve([resolvers.Svn, absolutePath]);
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error('Not a Git repository');
|
||||
})
|
||||
// If not, check if source is a valid Subversion repository
|
||||
.fail(function () {
|
||||
return Q.nfcall(fs.stat, path.join(absolutePath, '.svn'))
|
||||
.then(function (stats) {
|
||||
decEndpoint.source = absolutePath;
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return Q.resolve([resolvers.Svn, decEndpoint]);
|
||||
}
|
||||
|
||||
throw new Error('Not a Subversion repository');
|
||||
});
|
||||
})
|
||||
// If not, check if source is a valid file/folder
|
||||
.fail(function () {
|
||||
return Q.nfcall(fs.stat, absolutePath)
|
||||
.then(function () {
|
||||
decEndpoint.source = absolutePath;
|
||||
|
||||
return Q.resolve([resolvers.Fs, decEndpoint]);
|
||||
});
|
||||
throw new Error('Not a Subversion repository');
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
// If not, check if source is a valid file/folder
|
||||
.fail(function () {
|
||||
return Q.nfcall(fs.stat, absolutePath)
|
||||
.then(function () {
|
||||
return function () {
|
||||
return Q.resolve([resolvers.Fs, absolutePath]);
|
||||
};
|
||||
});
|
||||
});
|
||||
} else {
|
||||
promise = Q.reject(new Error('Not an absolute or relative file'));
|
||||
}
|
||||
|
||||
return promise
|
||||
// Check if is a shorthand and expand it
|
||||
addResolver(function () {
|
||||
.fail(function (err) {
|
||||
var parts;
|
||||
|
||||
// Skip ssh and/or URL with auth
|
||||
if (/[:@]/.test(source)) {
|
||||
return;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Ensure exactly only one "/"
|
||||
var parts = source.split('/');
|
||||
parts = source.split('/');
|
||||
if (parts.length === 2) {
|
||||
decEndpoint.source = mout.string.interpolate(config.shorthandResolver, {
|
||||
source = mout.string.interpolate(config.shorthandResolver, {
|
||||
shorthand: source,
|
||||
owner: parts[0],
|
||||
package: parts[1]
|
||||
});
|
||||
|
||||
return getConstructor(decEndpoint, options, registryClient);
|
||||
return function () {
|
||||
return getConstructor(source, config, registryClient);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
throw err;
|
||||
})
|
||||
// As last resort, we try the registry
|
||||
addResolver(function () {
|
||||
.fail(function (err) {
|
||||
if (!registryClient) {
|
||||
return;
|
||||
throw err;
|
||||
}
|
||||
|
||||
return Q.nfcall(registryClient.lookup.bind(registryClient), source)
|
||||
.then(function (entry) {
|
||||
if (!entry) {
|
||||
throw createError('Package ' + source + ' not found', 'ENOTFOUND');
|
||||
}
|
||||
return function () {
|
||||
return Q.nfcall(registryClient.lookup.bind(registryClient), source)
|
||||
.then(function (entry) {
|
||||
if (!entry) {
|
||||
throw createError('Package ' + source + ' not found', 'ENOTFOUND');
|
||||
}
|
||||
|
||||
decEndpoint.registry = true;
|
||||
// TODO: Handle entry.type.. for now it's only 'alias'
|
||||
// When we got published packages, this needs to be adjusted
|
||||
source = entry.url;
|
||||
|
||||
if (!decEndpoint.name) {
|
||||
decEndpoint.name = decEndpoint.source;
|
||||
}
|
||||
|
||||
decEndpoint.source = entry.url;
|
||||
|
||||
return getConstructor(decEndpoint, options);
|
||||
});
|
||||
});
|
||||
|
||||
addResolver(function () {
|
||||
return getConstructor(source, config, registryClient)
|
||||
.spread(function (ConcreteResolver, source) {
|
||||
return [ConcreteResolver, source, true];
|
||||
});
|
||||
});
|
||||
};
|
||||
})
|
||||
// If we got the function, simply call and return
|
||||
.then(function (func) {
|
||||
return func();
|
||||
// Finally throw a meaningful error
|
||||
}, function () {
|
||||
throw createError('Could not find appropriate resolver for ' + source, 'ENORESOLVER');
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function clearRuntimeCache() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
var util = require('util');
|
||||
var fs = require('../../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var GitRemoteResolver = require('./GitRemoteResolver');
|
||||
var download = require('../../util/download');
|
||||
var extract = require('../../util/extract');
|
||||
@@ -29,15 +28,13 @@ function GitHubResolver(decEndpoint, config, logger) {
|
||||
this._source += '.git';
|
||||
}
|
||||
|
||||
// Check if it's public
|
||||
this._public = mout.string.startsWith(this._source, 'git://');
|
||||
|
||||
// Use https:// rather than git:// if on a proxy
|
||||
if (this._config.proxy || this._config.httpsProxy) {
|
||||
this._source = this._source.replace('git://', 'https://');
|
||||
}
|
||||
|
||||
// Enable shallow clones for GitHub repos
|
||||
this._shallowClone = function () {
|
||||
return Q.resolve(true);
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(GitHubResolver, GitRemoteResolver);
|
||||
@@ -46,10 +43,14 @@ mout.object.mixIn(GitHubResolver, GitRemoteResolver);
|
||||
// -----------------
|
||||
|
||||
GitHubResolver.prototype._checkout = function () {
|
||||
var msg;
|
||||
var name = this._resolution.tag || this._resolution.branch || this._resolution.commit;
|
||||
var tarballUrl = 'https://github.com/' + this._org + '/' + this._repo + '/archive/' + name + '.tar.gz';
|
||||
// Only fully works with public repositories and tags
|
||||
// Could work with https/ssh protocol but not with 100% certainty
|
||||
if (!this._public || !this._resolution.tag) {
|
||||
return GitRemoteResolver.prototype._checkout.call(this);
|
||||
}
|
||||
|
||||
var msg;
|
||||
var tarballUrl = 'https://github.com/' + this._org + '/' + this._repo + '/archive/' + this._resolution.tag + '.tar.gz';
|
||||
var file = path.join(this._tempDir, 'archive.tar.gz');
|
||||
var reqHeaders = {};
|
||||
var that = this;
|
||||
@@ -65,7 +66,7 @@ GitHubResolver.prototype._checkout = function () {
|
||||
|
||||
// Download tarball
|
||||
return download(tarballUrl, file, {
|
||||
ca: this._config.ca.default,
|
||||
proxy: this._config.httpsProxy,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
@@ -114,6 +115,7 @@ GitHubResolver.prototype._checkout = function () {
|
||||
|
||||
return that._cleanTempDir()
|
||||
.then(GitRemoteResolver.prototype._checkout.bind(that));
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -19,17 +19,12 @@ function GitRemoteResolver(decEndpoint, config, logger) {
|
||||
this._name = this._name.slice(0, -4);
|
||||
}
|
||||
|
||||
// Get the remote of this source
|
||||
// Get the host of this source
|
||||
if (!/:\/\//.test(this._source)) {
|
||||
this._remote = url.parse('ssh://' + this._source);
|
||||
this._host = url.parse('ssh://' + this._source).host;
|
||||
} else {
|
||||
this._remote = url.parse(this._source);
|
||||
this._host = url.parse(this._source).host;
|
||||
}
|
||||
|
||||
this._host = this._remote.host;
|
||||
|
||||
// Verify whether the server supports shallow cloning
|
||||
this._shallowClone = this._supportsShallowCloning;
|
||||
}
|
||||
|
||||
util.inherits(GitRemoteResolver, GitResolver);
|
||||
@@ -117,36 +112,34 @@ GitRemoteResolver.prototype._fastClone = function (resolution) {
|
||||
branch = resolution.tag || resolution.branch;
|
||||
args = ['clone', this._source, '-b', branch, '--progress', '.'];
|
||||
|
||||
return this._shallowClone().then(function (shallowCloningSupported) {
|
||||
// If the host does not support shallow clones, we don't use --depth=1
|
||||
if (shallowCloningSupported && !GitRemoteResolver._noShallow.get(that._host)) {
|
||||
args.push('--depth', 1);
|
||||
// If the host does not support shallow clones, we don't use --depth=1
|
||||
if (!GitRemoteResolver._noShallow.get(this._host)) {
|
||||
args.push('--depth', 1);
|
||||
}
|
||||
|
||||
return cmd('git', args, { cwd: this._tempDir })
|
||||
.spread(function (stdout, stderr) {
|
||||
// Only after 1.7.10 --branch accepts tags
|
||||
// Detect those cases and inform the user to update git otherwise it's
|
||||
// a lot slower than newer versions
|
||||
if (!/branch .+? not found/i.test(stderr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return cmd('git', args, { cwd: that._tempDir })
|
||||
.spread(function (stdout, stderr) {
|
||||
// Only after 1.7.10 --branch accepts tags
|
||||
// Detect those cases and inform the user to update git otherwise it's
|
||||
// a lot slower than newer versions
|
||||
if (!/branch .+? not found/i.test(stderr)) {
|
||||
return;
|
||||
}
|
||||
that._logger.warn('old-git', 'It seems you are using an old version of git, it will be slower and propitious to errors!');
|
||||
return cmd('git', ['checkout', resolution.commit], { cwd: that._tempDir });
|
||||
}, function (err) {
|
||||
// Some git servers do not support shallow clones
|
||||
// When that happens, we mark this host and try again
|
||||
if (!GitRemoteResolver._noShallow.has(that._source) &&
|
||||
err.details &&
|
||||
/(rpc failed|shallow|--depth)/i.test(err.details)
|
||||
) {
|
||||
GitRemoteResolver._noShallow.set(that._host, true);
|
||||
return that._fastClone(resolution);
|
||||
}
|
||||
|
||||
that._logger.warn('old-git', 'It seems you are using an old version of git, it will be slower and propitious to errors!');
|
||||
return cmd('git', ['checkout', resolution.commit], { cwd: that._tempDir });
|
||||
}, function (err) {
|
||||
// Some git servers do not support shallow clones
|
||||
// When that happens, we mark this host and try again
|
||||
if (!GitRemoteResolver._noShallow.has(that._source) &&
|
||||
err.details &&
|
||||
/(rpc failed|shallow|--depth)/i.test(err.details)
|
||||
) {
|
||||
GitRemoteResolver._noShallow.set(that._host, true);
|
||||
return that._fastClone(resolution);
|
||||
}
|
||||
|
||||
throw err;
|
||||
});
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -164,79 +157,6 @@ GitRemoteResolver.prototype._suggestProxyWorkaround = function (err) {
|
||||
}
|
||||
};
|
||||
|
||||
// Verifies whether the server supports shallow cloning.
|
||||
// This is done according to the rules found in the following links:
|
||||
// * https://github.com/dimitri/el-get/pull/1921/files
|
||||
// * http://stackoverflow.com/questions/9270488/is-it-possible-to-detect-whether-a-http-git-remote-is-smart-or-dumb
|
||||
//
|
||||
// Summary of the rules:
|
||||
// * Protocols like ssh or git always support shallow cloning
|
||||
// * HTTP-based protocols can be verified by sending a HEAD or GET request to the URI (appended to the URL of the Git repo):
|
||||
// /info/refs?service=git-upload-pack
|
||||
// * If the server responds with a 'Content-Type' header of 'application/x-git-upload-pack-advertisement',
|
||||
// the server supports shallow cloning ("smart server")
|
||||
// * If the server responds with a different content type, the server does not support shallow cloning ("dumb server")
|
||||
// * Instead of doing the HEAD or GET request using an HTTP client, we're letting Git and Curl do the heavy lifting.
|
||||
// Calling Git with the GIT_CURL_VERBOSE=2 env variable will provide the Git and Curl output, which includes
|
||||
// the content type. This has the advantage that Git will take care of using stored credentials and any additional
|
||||
// negotiation that needs to take place.
|
||||
//
|
||||
// The above should cover most cases, including BitBucket.
|
||||
GitRemoteResolver.prototype._supportsShallowCloning = function () {
|
||||
var value = true;
|
||||
|
||||
// Verify that the remote could be parsed and that a protocol is set
|
||||
// This case is unlikely, but let's still cover it.
|
||||
if (this._remote == null || this._remote.protocol == null) {
|
||||
return Q.resolve(false);
|
||||
}
|
||||
|
||||
|
||||
if (!this._host || !this._config.shallowCloneHosts || this._config.shallowCloneHosts.indexOf(this._host) === -1) {
|
||||
return Q.resolve(false);
|
||||
}
|
||||
|
||||
// Check for protocol - the remote check for hosts supporting shallow cloning is only required for
|
||||
// HTTP or HTTPS, not for Git or SSH.
|
||||
// Also check for hosts that have been checked in a previous request and have been found to support
|
||||
// shallow cloning.
|
||||
if (mout.string.startsWith(this._remote.protocol, 'http')
|
||||
&& !GitRemoteResolver._canShallow.get(this._host)) {
|
||||
// Provide GIT_CURL_VERBOSE=2 environment variable to capture curl output.
|
||||
// Calling ls-remote includes a call to the git-upload-pack service, which returns the content type in the response.
|
||||
var processEnv = mout.object.merge(process.env, { 'GIT_CURL_VERBOSE': '2' });
|
||||
|
||||
value = cmd('git', ['ls-remote', '--heads', this._source], {
|
||||
env: processEnv
|
||||
})
|
||||
.spread(function (stdout, stderr) {
|
||||
// Check stderr for content-type, ignore stdout
|
||||
var isSmartServer;
|
||||
|
||||
// If the content type is 'x-git', then the server supports shallow cloning
|
||||
isSmartServer = mout.string.contains(stderr,
|
||||
'Content-Type: application/x-git-upload-pack-advertisement');
|
||||
|
||||
this._logger.debug('detect-smart-git', 'Smart Git host detected: ' + isSmartServer);
|
||||
|
||||
if (isSmartServer) {
|
||||
// Cache this host
|
||||
GitRemoteResolver._canShallow.set(this._host, true);
|
||||
}
|
||||
|
||||
return isSmartServer;
|
||||
}.bind(this));
|
||||
}
|
||||
else {
|
||||
// One of the following cases:
|
||||
// * A non-HTTP/HTTPS protocol
|
||||
// * A host that has been checked before and that supports shallow cloning
|
||||
return Q.resolve(true);
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
// Grab refs remotely
|
||||
@@ -275,7 +195,4 @@ GitRemoteResolver.refs = function (source) {
|
||||
// Store hosts that do not support shallow clones here
|
||||
GitRemoteResolver._noShallow = new LRU({ max: 50, maxAge: 5 * 60 * 1000 });
|
||||
|
||||
// Store hosts that support shallow clones here
|
||||
GitRemoteResolver._canShallow = new LRU({ max: 50, maxAge: 5 * 60 * 1000 });
|
||||
|
||||
module.exports = GitRemoteResolver;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var rimraf = require('../../util/rimraf');
|
||||
var chmodr = require('chmodr');
|
||||
var rimraf = require('rimraf');
|
||||
var mkdirp = require('mkdirp');
|
||||
var which = require('which');
|
||||
var LRU = require('lru-cache');
|
||||
@@ -9,6 +10,7 @@ var mout = require('mout');
|
||||
var Resolver = require('./Resolver');
|
||||
var semver = require('../../util/semver');
|
||||
var createError = require('../../util/createError');
|
||||
var defaultConfig = require('../../config');
|
||||
|
||||
var hasGit;
|
||||
|
||||
@@ -20,25 +22,13 @@ try {
|
||||
hasGit = false;
|
||||
}
|
||||
|
||||
// Set template dir to the empty directory so that user templates are not run
|
||||
// This environment variable is not multiple config aware but it's not documented
|
||||
// anyway
|
||||
mkdirp.sync(defaultConfig.storage.empty);
|
||||
process.env.GIT_TEMPLATE_DIR = defaultConfig.storage.empty;
|
||||
|
||||
function GitResolver(decEndpoint, config, logger) {
|
||||
// Set template dir to the empty directory so that user templates are not run
|
||||
// This environment variable is not multiple config aware but it's not documented
|
||||
// anyway
|
||||
mkdirp.sync(config.storage.empty);
|
||||
process.env.GIT_TEMPLATE_DIR = config.storage.empty;
|
||||
|
||||
if (!config.strictSsl) {
|
||||
process.env.GIT_SSL_NO_VERIFY = 'true';
|
||||
}
|
||||
|
||||
if (!config.interactive) {
|
||||
process.env.GIT_TERMINAL_PROMPT = '0';
|
||||
|
||||
if (!process.env.SSH_ASKPASS) {
|
||||
process.env.SSH_ASKPASS = 'echo';
|
||||
}
|
||||
}
|
||||
|
||||
Resolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
if (!hasGit) {
|
||||
@@ -51,7 +41,7 @@ mout.object.mixIn(GitResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
GitResolver.prototype._hasNew = function (pkgMeta) {
|
||||
GitResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
|
||||
var oldResolution = pkgMeta._resolution || {};
|
||||
|
||||
return this._findResolution()
|
||||
@@ -121,6 +111,8 @@ GitResolver.prototype._findResolution = function (target) {
|
||||
version,
|
||||
index;
|
||||
|
||||
versionsArr = versions.map(function (obj) { return obj.version; });
|
||||
|
||||
// If there are no tags and target is *,
|
||||
// fallback to the latest commit on master
|
||||
if (!versions.length && target === '*') {
|
||||
@@ -153,7 +145,7 @@ GitResolver.prototype._findResolution = function (target) {
|
||||
throw createError('No tag found that was able to satisfy ' + target, 'ENORESTARGET', {
|
||||
details: !versions.length ?
|
||||
'No versions found in ' + that._source :
|
||||
'Available versions in ' + that._source + ': ' + versions.map(function (version) { return version.version; }).join(', ')
|
||||
'Available versions: ' + versions.map(function (version) { return version.version; }).join(', ')
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -173,18 +165,6 @@ GitResolver.prototype._findResolution = function (target) {
|
||||
return that._resolution = { type: 'branch', branch: target, commit: branches[target] };
|
||||
}
|
||||
|
||||
if ((/^[a-f0-9]{4,40}$/).test(target)) {
|
||||
if (target.length < 12) {
|
||||
that._logger.warn(
|
||||
'short-sha',
|
||||
'Consider using longer commit SHA to avoid conflicts'
|
||||
);
|
||||
}
|
||||
|
||||
that._resolution = { type: 'commit', commit: target };
|
||||
return that._resolution;
|
||||
}
|
||||
|
||||
branches = Object.keys(branches);
|
||||
tags = Object.keys(tags);
|
||||
|
||||
@@ -204,7 +184,23 @@ GitResolver.prototype._findResolution = function (target) {
|
||||
GitResolver.prototype._cleanup = function () {
|
||||
var gitFolder = path.join(this._tempDir, '.git');
|
||||
|
||||
return Q.nfcall(rimraf, gitFolder);
|
||||
// Remove the .git folder
|
||||
// Note that on windows, we need to chmod to 0777 before due to a bug in git
|
||||
// See: https://github.com/isaacs/rimraf/issues/19
|
||||
if (process.platform === 'win32') {
|
||||
return Q.nfcall(chmodr, gitFolder, 0777)
|
||||
.then(function () {
|
||||
return Q.nfcall(rimraf, gitFolder);
|
||||
}, function (err) {
|
||||
// If .git does not exist, chmodr returns ENOENT
|
||||
// so, we ignore that error code
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return Q.nfcall(rimraf, gitFolder);
|
||||
}
|
||||
};
|
||||
|
||||
GitResolver.prototype._savePkgMeta = function (meta) {
|
||||
@@ -214,7 +210,7 @@ GitResolver.prototype._savePkgMeta = function (meta) {
|
||||
version = semver.clean(this._resolution.tag);
|
||||
|
||||
// Warn if the package meta version is different than the resolved one
|
||||
if (typeof meta.version === 'string' && semver.valid(meta.version) && semver.neq(meta.version, version)) {
|
||||
if (typeof meta.version === 'string' && semver.neq(meta.version, version)) {
|
||||
this._logger.warn('mismatch', 'Version declared in the json (' + meta.version + ') is different than the resolved one (' + version + ')', {
|
||||
resolution: this._resolution,
|
||||
pkgMeta: meta
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
var fs = require('../../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var tmp = require('tmp');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('../../util/rimraf');
|
||||
var rimraf = require('rimraf');
|
||||
var readJson = require('../../util/readJson');
|
||||
var createError = require('../../util/createError');
|
||||
var removeIgnores = require('../../util/removeIgnores');
|
||||
var md5 = require('md5-hex');
|
||||
|
||||
tmp.setGracefulCleanup();
|
||||
|
||||
@@ -44,8 +43,9 @@ Resolver.prototype.getPkgMeta = function () {
|
||||
return this._pkgMeta;
|
||||
};
|
||||
|
||||
Resolver.prototype.hasNew = function (pkgMeta) {
|
||||
Resolver.prototype.hasNew = function (canonicalDir, pkgMeta) {
|
||||
var promise;
|
||||
var metaFile;
|
||||
var that = this;
|
||||
|
||||
// If already working, error out
|
||||
@@ -56,7 +56,24 @@ Resolver.prototype.hasNew = function (pkgMeta) {
|
||||
this._working = true;
|
||||
|
||||
// Avoid reading the package meta if already given
|
||||
promise = this._hasNew(pkgMeta);
|
||||
if (pkgMeta) {
|
||||
promise = this._hasNew(canonicalDir, pkgMeta);
|
||||
// Otherwise call _hasNew with both the package meta and the canonical dir
|
||||
} else {
|
||||
metaFile = path.join(canonicalDir, '.bower.json');
|
||||
|
||||
promise = readJson(metaFile)
|
||||
.spread(function (pkgMeta) {
|
||||
return that._hasNew(canonicalDir, pkgMeta);
|
||||
}, function (err) {
|
||||
that._logger.debug('read-json', 'Failed to read ' + metaFile, {
|
||||
filename: metaFile,
|
||||
error: err
|
||||
});
|
||||
|
||||
return true; // Simply resolve to true if there was an error reading the file
|
||||
});
|
||||
}
|
||||
|
||||
return promise.fin(function () {
|
||||
that._working = false;
|
||||
@@ -97,25 +114,6 @@ Resolver.prototype.resolve = function () {
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype.isCacheable = function () {
|
||||
// Bypass cache for local dependencies
|
||||
if (this._source &&
|
||||
/^(?:file:[\/\\]{2}|[A-Z]:)?\.?\.?[\/\\]/.test(this._source)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We don't want to cache moving targets like branches
|
||||
if (this._pkgMeta &&
|
||||
this._pkgMeta._resolution &&
|
||||
this._pkgMeta._resolution.type === 'branch') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
// -----------------
|
||||
|
||||
// Abstract functions that must be implemented by concrete resolvers
|
||||
@@ -125,7 +123,7 @@ Resolver.prototype._resolve = function () {
|
||||
|
||||
// Abstract functions that can be re-implemented by concrete resolvers
|
||||
// as necessary
|
||||
Resolver.prototype._hasNew = function (pkgMeta) {
|
||||
Resolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
|
||||
return Q.resolve(true);
|
||||
};
|
||||
|
||||
@@ -145,14 +143,14 @@ Resolver.prototype._createTempDir = function () {
|
||||
return Q.nfcall(mkdirp, this._config.tmp)
|
||||
.then(function () {
|
||||
return Q.nfcall(tmp.dir, {
|
||||
template: path.join(this._config.tmp, md5(this._name) + '-' + process.pid + '-XXXXXX'),
|
||||
template: path.join(this._config.tmp, this._name + '-' + process.pid + '-XXXXXX'),
|
||||
mode: 0777 & ~process.umask(),
|
||||
unsafeCleanup: true
|
||||
});
|
||||
}.bind(this))
|
||||
.then(function (dir) {
|
||||
// nfcall may return multiple callback arguments as an array
|
||||
return this._tempDir = Array.isArray(dir) ? dir[0] : dir;
|
||||
this._tempDir = dir;
|
||||
return dir;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
@@ -178,8 +176,7 @@ Resolver.prototype._readJson = function (dir) {
|
||||
|
||||
dir = dir || this._tempDir;
|
||||
return readJson(dir, {
|
||||
assume: { name: this._name },
|
||||
logger: that._logger
|
||||
assume: { name: this._name }
|
||||
})
|
||||
.spread(function (json, deprecated) {
|
||||
if (deprecated) {
|
||||
@@ -204,14 +201,13 @@ Resolver.prototype._applyPkgMeta = function (meta) {
|
||||
}
|
||||
|
||||
// Otherwise remove them from the temp dir
|
||||
return removeIgnores(this._tempDir, meta)
|
||||
return removeIgnores(this._tempDir, meta.ignore)
|
||||
.then(function () {
|
||||
return meta;
|
||||
});
|
||||
};
|
||||
|
||||
Resolver.prototype._savePkgMeta = function (meta) {
|
||||
var that = this;
|
||||
var contents;
|
||||
|
||||
// Store original source & target
|
||||
@@ -223,8 +219,8 @@ Resolver.prototype._savePkgMeta = function (meta) {
|
||||
|
||||
return Q.nfcall(fs.writeFile, path.join(this._tempDir, '.bower.json'), contents)
|
||||
.then(function () {
|
||||
return that._pkgMeta = meta;
|
||||
});
|
||||
return this._pkgMeta = meta;
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
module.exports = Resolver;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var rimraf = require('rimraf');
|
||||
var which = require('which');
|
||||
var LRU = require('lru-cache');
|
||||
var mout = require('mout');
|
||||
@@ -40,7 +42,7 @@ SvnResolver.getSource = function (source) {
|
||||
.replace(/\/+$/, ''); // Remove trailing slashes
|
||||
};
|
||||
|
||||
SvnResolver.prototype._hasNew = function (pkgMeta) {
|
||||
SvnResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
|
||||
var oldResolution = pkgMeta._resolution || {};
|
||||
|
||||
return this._findResolution()
|
||||
@@ -65,13 +67,19 @@ SvnResolver.prototype._resolve = function () {
|
||||
|
||||
return this._findResolution()
|
||||
.then(function () {
|
||||
return that._export();
|
||||
return that._checkout()
|
||||
// Always run cleanup after checkout to ensure that .svn is removed!
|
||||
// If it's not removed, problems might arise when the "tmp" module attempts
|
||||
// to delete the temporary folder
|
||||
.fin(function () {
|
||||
return that._cleanup();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.prototype._export = function () {
|
||||
SvnResolver.prototype._checkout = function () {
|
||||
var promise;
|
||||
var timer;
|
||||
var reporter;
|
||||
@@ -80,19 +88,19 @@ SvnResolver.prototype._export = function () {
|
||||
|
||||
this.source = SvnResolver.getSource(this._source);
|
||||
|
||||
this._logger.action('export', resolution.tag || resolution.branch || resolution.commit, {
|
||||
this._logger.action('checkout', resolution.tag || resolution.branch || resolution.commit, {
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
});
|
||||
|
||||
if (resolution.type === 'commit') {
|
||||
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/trunk', '-r' + resolution.commit, this._tempDir]);
|
||||
promise = cmd('svn', ['checkout', this._source + '/trunk', '-r' + resolution.commit, this._tempDir]);
|
||||
} else if (resolution.type === 'branch' && resolution.branch === 'trunk') {
|
||||
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/trunk', this._tempDir]);
|
||||
promise = cmd('svn', ['checkout', this._source + '/trunk', this._tempDir]);
|
||||
} else if (resolution.type === 'branch') {
|
||||
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/branches/' + resolution.branch, this._tempDir]);
|
||||
promise = cmd('svn', ['checkout', this._source + '/branches/' + resolution.branch, this._tempDir]);
|
||||
} else {
|
||||
promise = cmd('svn', ['export', '--force', '--non-interactive', this._source + '/tags/' + resolution.tag, this._tempDir]);
|
||||
promise = cmd('svn', ['checkout', this._source + '/tags/' + resolution.tag, this._tempDir]);
|
||||
}
|
||||
|
||||
// Throttle the progress reporter to 1 time each sec
|
||||
@@ -188,7 +196,7 @@ SvnResolver.prototype._findResolution = function (target) {
|
||||
throw createError('No tag found that was able to satisfy ' + target, 'ENORESTARGET', {
|
||||
details: !versions.length ?
|
||||
'No versions found in ' + that._source :
|
||||
'Available versions in ' + that._source + ': ' + versions.map(function (version) { return version.version; }).join(', ')
|
||||
'Available versions: ' + versions.map(function (version) { return version.version; }).join(', ')
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -224,6 +232,12 @@ SvnResolver.prototype._findResolution = function (target) {
|
||||
});
|
||||
};
|
||||
|
||||
SvnResolver.prototype._cleanup = function () {
|
||||
var svnFolder = path.join(this._tempDir, '.svn');
|
||||
|
||||
return Q.nfcall(rimraf, svnFolder);
|
||||
};
|
||||
|
||||
SvnResolver.prototype._savePkgMeta = function (meta) {
|
||||
var version;
|
||||
|
||||
@@ -324,7 +338,7 @@ SvnResolver.tags = function (source) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('svn', ['list', source + '/tags', '--verbose', '--non-interactive'])
|
||||
value = cmd('svn', ['list', source + '/tags', '--verbose'])
|
||||
.spread(function (stout) {
|
||||
var tags = SvnResolver.parseSubversionListOutput(stout.toString());
|
||||
|
||||
@@ -349,7 +363,7 @@ SvnResolver.branches = function (source) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('svn', ['list', source + '/branches', '--verbose', '--non-interactive'])
|
||||
value = cmd('svn', ['list', source + '/branches', '--verbose'])
|
||||
.spread(function (stout) {
|
||||
var branches = SvnResolver.parseSubversionListOutput(stout.toString());
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var fs = require('../../util/fs');
|
||||
var fs = require('graceful-fs');
|
||||
var url = require('url');
|
||||
var request = require('request');
|
||||
var Q = require('q');
|
||||
@@ -39,7 +39,7 @@ UrlResolver.isTargetable = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
UrlResolver.prototype._hasNew = function (pkgMeta) {
|
||||
UrlResolver.prototype._hasNew = function (canonicalDir, pkgMeta) {
|
||||
var oldCacheHeaders = pkgMeta._cacheHeaders || {};
|
||||
var reqHeaders = {};
|
||||
|
||||
@@ -55,7 +55,7 @@ UrlResolver.prototype._hasNew = function (pkgMeta) {
|
||||
|
||||
// Make an HEAD request to the source
|
||||
return Q.nfcall(request.head, this._source, {
|
||||
ca: this._config.ca.default,
|
||||
proxy: this._remote.protocol === 'https:' ? this._config.httpsProxy : this._config.proxy,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
@@ -104,18 +104,8 @@ UrlResolver.prototype._resolve = function () {
|
||||
|
||||
// -----------------
|
||||
|
||||
UrlResolver.prototype._parseSourceURL = function (_url) {
|
||||
return url.parse(path.basename(_url)).pathname;
|
||||
};
|
||||
|
||||
UrlResolver.prototype._download = function () {
|
||||
var fileName = this._parseSourceURL(this._source);
|
||||
|
||||
if (!fileName) {
|
||||
this._source = this._source.replace(/\/(?=\?|#)/, '');
|
||||
fileName = this._parseSourceURL(this._source);
|
||||
}
|
||||
|
||||
var fileName = url.parse(path.basename(this._source)).pathname;
|
||||
var file = path.join(this._tempDir, fileName);
|
||||
var reqHeaders = {};
|
||||
var that = this;
|
||||
@@ -131,7 +121,7 @@ UrlResolver.prototype._download = function () {
|
||||
|
||||
// Download the file
|
||||
return download(this._source, file, {
|
||||
ca: this._config.ca.default,
|
||||
proxy: this._remote.protocol === 'https:' ? this._config.httpsProxy : this._config.proxy,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
|
||||
@@ -1,323 +0,0 @@
|
||||
var Q = require('q');
|
||||
var path = require('path');
|
||||
var fs = require('../../util/fs');
|
||||
var object = require('mout/object');
|
||||
|
||||
var semver = require('../../util/semver');
|
||||
var createError = require('../../util/createError');
|
||||
var readJson = require('../../util/readJson');
|
||||
var removeIgnores = require('../../util/removeIgnores');
|
||||
|
||||
function pluginResolverFactory(resolverFactory, bower) {
|
||||
bower = bower || {};
|
||||
|
||||
if (typeof resolverFactory !== 'function') {
|
||||
throw createError('Resolver has "' + typeof resolverFactory + '" type instead of "function" type.', 'ERESOLERAPI');
|
||||
}
|
||||
|
||||
var resolver = resolverFactory(bower);
|
||||
|
||||
function maxSatisfyingVersion(versions, target) {
|
||||
var versionsArr, index;
|
||||
|
||||
versionsArr = versions.map(function (obj) { return obj.version; });
|
||||
|
||||
// Find a satisfying version, enabling strict match so that pre-releases
|
||||
// have lower priority over normal ones when target is *
|
||||
index = semver.maxSatisfyingIndex(versionsArr, target, true);
|
||||
|
||||
if (index !== -1) {
|
||||
return versions[index];
|
||||
}
|
||||
}
|
||||
|
||||
function PluginResolver(decEndpoint) {
|
||||
this._decEndpoint = decEndpoint;
|
||||
}
|
||||
|
||||
// @private
|
||||
PluginResolver.prototype.getEndpoint = function () {
|
||||
return object.merge(this._decEndpoint, {
|
||||
name: this.getName(),
|
||||
source: this.getSource(),
|
||||
target: this.getTarget()
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getSource = function () {
|
||||
return this._decEndpoint.source;
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getTarget = function () {
|
||||
return this._decEndpoint.target || '*';
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getName = function () {
|
||||
if (!this._decEndpoint.name && typeof resolver.getName === 'function') {
|
||||
return resolver.getName.call(resolver, this.getSource());
|
||||
} else if (!this._decEndpoint.name) {
|
||||
return path.basename(this.getSource());
|
||||
} else {
|
||||
return this._decEndpoint.name;
|
||||
}
|
||||
};
|
||||
|
||||
PluginResolver.prototype.getPkgMeta = function () {
|
||||
return this._pkgMeta;
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Plugin Resolver is always considered potentially cacheable
|
||||
// The "resolve" method decides whether to use cached or fetch new version.
|
||||
PluginResolver.prototype.isCacheable = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Not only it's always potentially cacheable, but also always potenially new.
|
||||
// The "resolve" handles logic of re-downloading target if needed.
|
||||
PluginResolver.prototype.hasNew = function (pkgMeta) {
|
||||
if (this.hasNewPromise) {
|
||||
return this.hasNewPromise;
|
||||
}
|
||||
|
||||
this._pkgMeta = pkgMeta;
|
||||
|
||||
return this.hasNewPromise = this.resolve().then(function (result) {
|
||||
return result !== undefined;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype.resolve = function () {
|
||||
if (this.resolvePromise) {
|
||||
return this.resolvePromise;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
|
||||
return this.resolvePromise = Q.fcall(function () {
|
||||
var target = that.getTarget();
|
||||
|
||||
// It means that we can accept ranges as targets
|
||||
if (that.constructor.isTargetable()) {
|
||||
that._release = target;
|
||||
|
||||
if (semver.validRange(target)) {
|
||||
return Q.fcall(resolver.releases.bind(resolver), that.getSource())
|
||||
.then(function (result) {
|
||||
if (!result) {
|
||||
throw createError('Resolver did not provide releases of package.');
|
||||
}
|
||||
|
||||
var releases = that._releases = result;
|
||||
|
||||
var versions = releases.filter(function (target) {
|
||||
return semver.clean(target.version);
|
||||
});
|
||||
|
||||
var maxRelease = maxSatisfyingVersion(versions, target);
|
||||
|
||||
if (maxRelease) {
|
||||
that._version = maxRelease.version;
|
||||
that._release = that._decEndpoint.target = maxRelease.target;
|
||||
} else {
|
||||
throw createError('No version found that was able to satisfy ' + target, 'ENORESTARGET', {
|
||||
details: !versions.length ?
|
||||
'No versions found in ' + that.getSource() :
|
||||
'Available versions: ' + versions.map(function (version) { return version.version; }).join(', ')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (semver.validRange(target) && target !== '*') {
|
||||
return Q.reject(createError('Resolver does not accept version ranges (' + target + ')'));
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(function () {
|
||||
|
||||
// We pass old _resolution (if hasNew has been called before contents).
|
||||
// So resolver can decide wheter use cached version of contents new one.
|
||||
if (typeof resolver.fetch !== 'function') {
|
||||
throw createError('Resolver does not implement the "fetch" method.');
|
||||
}
|
||||
|
||||
var cached = {};
|
||||
|
||||
if (that._releases) {
|
||||
cached.releases = that._releases;
|
||||
}
|
||||
|
||||
if (that._pkgMeta) {
|
||||
cached.endpoint = {
|
||||
name: that._pkgMeta.name,
|
||||
source: that._pkgMeta._source,
|
||||
target: that._pkgMeta._target
|
||||
};
|
||||
|
||||
cached.release = that._pkgMeta._release;
|
||||
|
||||
cached.version = that._pkgMeta.version;
|
||||
|
||||
cached.resolution = that._pkgMeta._resolution || {};
|
||||
}
|
||||
|
||||
return Q.fcall(resolver.fetch.bind(resolver), that.getEndpoint(), cached);
|
||||
})
|
||||
.then(function (result) {
|
||||
// Empty result means to re-use existing resolution
|
||||
if (!result) {
|
||||
return;
|
||||
} else {
|
||||
if (!result.tempPath) {
|
||||
throw createError('Resolver did not provide path to extracted contents of package.');
|
||||
}
|
||||
|
||||
that._tempDir = result.tempPath;
|
||||
|
||||
return that._readJson(that._tempDir).then(function (meta) {
|
||||
return that._applyPkgMeta(meta, result)
|
||||
.then(that._savePkgMeta.bind(that, meta, result))
|
||||
.then(function () {
|
||||
return that._tempDir;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype._readJson = function (dir) {
|
||||
var that = this;
|
||||
|
||||
return readJson(dir, {
|
||||
assume: { name: that.getName() },
|
||||
logger: bower.logger
|
||||
})
|
||||
.spread(function (json, deprecated) {
|
||||
if (deprecated) {
|
||||
bower.logger.warn('deprecated', 'Package ' + that.getName() + ' is using the deprecated ' + deprecated);
|
||||
}
|
||||
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype._applyPkgMeta = function (meta, result) {
|
||||
// Check if name defined in the json is different
|
||||
// If so and if the name was "guessed", assume the json name
|
||||
if (meta.name !== this._name) {
|
||||
this._name = meta.name;
|
||||
}
|
||||
|
||||
// Handle ignore property, deleting all files from the temporary directory
|
||||
// If no ignores were specified, simply resolve
|
||||
if (result.removeIgnores === false || !meta.ignore || !meta.ignore.length) {
|
||||
return Q.resolve(meta);
|
||||
}
|
||||
|
||||
// Otherwise remove them from the temp dir
|
||||
return removeIgnores(this._tempDir, meta).then(function () {
|
||||
return meta;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.prototype._savePkgMeta = function (meta, result) {
|
||||
var that = this;
|
||||
|
||||
meta._source = that.getSource();
|
||||
meta._target = that.getTarget();
|
||||
|
||||
if (result.resolution) {
|
||||
meta._resolution = result.resolution;
|
||||
}
|
||||
|
||||
if (that._release) {
|
||||
meta._release = that._release;
|
||||
}
|
||||
|
||||
if (that._version) {
|
||||
meta.version = that._version;
|
||||
} else {
|
||||
delete meta.version;
|
||||
}
|
||||
|
||||
// Stringify contents
|
||||
var contents = JSON.stringify(meta, null, 2);
|
||||
|
||||
return Q.nfcall(fs.writeFile, path.join(this._tempDir, '.bower.json'), contents)
|
||||
.then(function () {
|
||||
return that._pkgMeta = meta;
|
||||
});
|
||||
};
|
||||
|
||||
// It is used only by "bower info". It returns all semver versions.
|
||||
PluginResolver.versions = function (source) {
|
||||
return Q.fcall(resolver.releases.bind(resolver), source).then(function (result) {
|
||||
if (!result) {
|
||||
throw createError('Resolver did not provide releases of package.');
|
||||
}
|
||||
|
||||
var releases = this._releases = result;
|
||||
|
||||
var versions = releases.map(function (version) {
|
||||
return semver.clean(version.version);
|
||||
});
|
||||
|
||||
versions = versions.filter(function (version) {
|
||||
return version;
|
||||
});
|
||||
|
||||
versions.sort(function (a, b) {
|
||||
return semver.rcompare(a, b);
|
||||
});
|
||||
|
||||
return versions;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.isTargetable = function () {
|
||||
// If resolver doesn't define versions function, it's not targetable..
|
||||
return typeof resolver.releases === 'function';
|
||||
};
|
||||
|
||||
PluginResolver.clearRuntimeCache = function () {
|
||||
resolver = resolverFactory(bower);
|
||||
};
|
||||
|
||||
PluginResolver.match = function (source) {
|
||||
if (typeof resolver.match !== 'function') {
|
||||
throw createError('Resolver is missing "match" method.', 'ERESOLVERAPI');
|
||||
}
|
||||
|
||||
var match = resolver.match.bind(resolver);
|
||||
|
||||
return Q.fcall(match, source).then(function (result) {
|
||||
if (typeof result !== 'boolean') {
|
||||
throw createError('Resolver\'s "match" method should return a boolean', 'ERESOLVERAPI');
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
PluginResolver.locate = function (source) {
|
||||
if (typeof resolver.locate !== 'function') {
|
||||
return source;
|
||||
}
|
||||
|
||||
return Q.fcall(resolver.locate.bind(resolver), source).then(function (result) {
|
||||
if (typeof result !== 'string') {
|
||||
throw createError('Resolver\'s "locate" method should return a string', 'ERESOLVERAPI');
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
};
|
||||
|
||||
return PluginResolver;
|
||||
}
|
||||
|
||||
|
||||
module.exports = pluginResolverFactory;
|
||||
|
||||
@@ -78,20 +78,19 @@ var run = function (cmdString, action, logger, config) {
|
||||
|
||||
var hook = function (action, ordered, config, logger, packages, installed, json) {
|
||||
if (mout.object.keys(packages).length === 0 || !config.scripts || !config.scripts[action]) {
|
||||
/*jshint newcap: false */
|
||||
return Q();
|
||||
}
|
||||
|
||||
var orderedPackages = ordered ? orderByDependencies(packages, installed, json) : mout.object.keys(packages);
|
||||
var placeholder = new RegExp('%', 'g');
|
||||
var cmdString = mout.string.replace(config.scripts[action], placeholder, orderedPackages.join(' '));
|
||||
var cmdString = mout.string.replace(config.scripts[action], '%', orderedPackages.join(' '));
|
||||
return run(cmdString, action, logger, config);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
preuninstall: mout.function.partial(hook, 'preuninstall', false),
|
||||
postuninstall: mout.function.partial(hook, 'postuninstall', false),
|
||||
preinstall: mout.function.partial(hook, 'preinstall', true),
|
||||
postinstall: mout.function.partial(hook, 'postinstall', true),
|
||||
//only exposed for test
|
||||
_orderByDependencies: orderByDependencies
|
||||
};
|
||||
};
|
||||
30
lib/index.js
30
lib/index.js
@@ -1,6 +1,29 @@
|
||||
var abbrev = require('abbrev');
|
||||
var mout = require('mout');
|
||||
var commands = require('./commands');
|
||||
var version = require('./version');
|
||||
var abbreviations = require('./util/abbreviations')(commands);
|
||||
|
||||
var abbreviations = abbrev(expandNames(commands));
|
||||
abbreviations.i = 'install';
|
||||
abbreviations.rm = 'uninstall';
|
||||
abbreviations.unlink = 'uninstall';
|
||||
abbreviations.ls = 'list';
|
||||
|
||||
function expandNames(obj, prefix, stack) {
|
||||
prefix = prefix || '';
|
||||
stack = stack || [];
|
||||
|
||||
mout.object.forOwn(obj, function (value, name) {
|
||||
name = prefix + name;
|
||||
|
||||
stack.push(name);
|
||||
|
||||
if (typeof value === 'object' && !value.line) {
|
||||
expandNames(value, name + ' ', stack);
|
||||
}
|
||||
});
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
function clearRuntimeCache() {
|
||||
// Note that in edge cases, some architecture components instance's
|
||||
@@ -11,9 +34,8 @@ function clearRuntimeCache() {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
version: version,
|
||||
commands: commands,
|
||||
config: require('./config')(),
|
||||
config: require('./config'),
|
||||
abbreviations: abbreviations,
|
||||
reset: clearRuntimeCache
|
||||
};
|
||||
|
||||
@@ -31,8 +31,10 @@ JsonRenderer.prototype.error = function (err) {
|
||||
err.message = message;
|
||||
|
||||
// Stack
|
||||
/*jshint camelcase:false*/
|
||||
stack = err.fstream_stack || err.stack || 'N/A';
|
||||
err.stacktrace = (Array.isArray(stack) ? stack.join('\n') : stack);
|
||||
/*jshint camelcase:true*/
|
||||
|
||||
this.log(err);
|
||||
this.end();
|
||||
@@ -119,6 +121,8 @@ JsonRenderer.prototype.prompt = function (prompts) {
|
||||
});
|
||||
};
|
||||
|
||||
JsonRenderer.prototype.updateNotice = function () {};
|
||||
|
||||
// -------------------------
|
||||
|
||||
JsonRenderer.prototype._stringify = function (log) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
var cardinal = require('cardinal');
|
||||
var chalk = require('chalk');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var archy = require('archy');
|
||||
var Q = require('q');
|
||||
var inquirer = require('inquirer');
|
||||
var stringifyObject = require('stringify-object');
|
||||
var os = require('os');
|
||||
var semverUtils = require('semver-utils');
|
||||
var version = require('../version');
|
||||
var pkg = require(path.join(__dirname, '../..', 'package.json'));
|
||||
var template = require('../util/template');
|
||||
|
||||
function StandardRenderer(command, config) {
|
||||
@@ -24,23 +25,13 @@ function StandardRenderer(command, config) {
|
||||
};
|
||||
|
||||
this._command = command;
|
||||
this._config = config || {};
|
||||
this._config = config;
|
||||
|
||||
if (this.constructor._wideCommands.indexOf(command) === -1) {
|
||||
this._compact = true;
|
||||
} else {
|
||||
this._compact = process.stdout.columns < 120;
|
||||
}
|
||||
|
||||
var exitOnPipeError = function (err) {
|
||||
if (err.code === 'EPIPE') {
|
||||
process.exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
// It happens when piping command to "head" util
|
||||
process.stdout.on('error', exitOnPipeError);
|
||||
process.stderr.on('error', exitOnPipeError);
|
||||
}
|
||||
|
||||
StandardRenderer.prototype.end = function (data) {
|
||||
@@ -72,17 +63,19 @@ StandardRenderer.prototype.error = function (err) {
|
||||
// Print trace if verbose, the error has no code
|
||||
// or if the error is a node error
|
||||
if (this._config.verbose || !err.code || err.errno) {
|
||||
/*jshint camelcase:false*/
|
||||
stack = err.fstream_stack || err.stack || 'N/A';
|
||||
str = chalk.yellow('\nStack trace:\n');
|
||||
str += (Array.isArray(stack) ? stack.join('\n') : stack) + '\n';
|
||||
str += chalk.yellow('\nConsole trace:\n');
|
||||
/*jshint camelcase:true*/
|
||||
|
||||
this._write(process.stderr, str);
|
||||
this._write(process.stderr, new Error().stack);
|
||||
console.trace();
|
||||
|
||||
// Print bower version, node version and system info.
|
||||
this._write(process.stderr, chalk.yellow('\nSystem info:\n'));
|
||||
this._write(process.stderr, 'Bower version: ' + version + '\n');
|
||||
this._write(process.stderr, 'Bower version: ' + pkg.version + '\n');
|
||||
this._write(process.stderr, 'Node version: ' + process.versions.node + '\n');
|
||||
this._write(process.stderr, 'OS: ' + os.type() + ' ' + os.release() + ' ' + os.arch() + '\n');
|
||||
}
|
||||
@@ -113,12 +106,16 @@ StandardRenderer.prototype.prompt = function (prompts) {
|
||||
|
||||
// Prompt
|
||||
deferred = Q.defer();
|
||||
var inquirer = require('inquirer');
|
||||
inquirer.prompt(prompts, deferred.resolve);
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
StandardRenderer.prototype.updateNotice = function (data) {
|
||||
var str = template.render('std/update-notice.std', data);
|
||||
this._write(process.stderr, str);
|
||||
};
|
||||
|
||||
// -------------------------
|
||||
|
||||
StandardRenderer.prototype._help = function (data) {
|
||||
@@ -174,14 +171,10 @@ StandardRenderer.prototype._update = function (packages) {
|
||||
StandardRenderer.prototype._list = function (tree) {
|
||||
var cliTree;
|
||||
|
||||
if (tree.pkgMeta) {
|
||||
tree.root = true;
|
||||
cliTree = archy(this._tree2archy(tree));
|
||||
} else {
|
||||
cliTree = stringifyObject(tree, { indent: ' ' }).replace(/[{}]/g, '') + '\n';
|
||||
}
|
||||
tree.root = true;
|
||||
cliTree = this._tree2archy(tree);
|
||||
|
||||
this._write(process.stdout, cliTree);
|
||||
this._write(process.stdout, archy(cliTree));
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._search = function (results) {
|
||||
@@ -208,19 +201,6 @@ StandardRenderer.prototype._info = function (data) {
|
||||
|
||||
// Render the versions at the end
|
||||
if (includeVersions) {
|
||||
data.hidePreReleases = false;
|
||||
data.numPreReleases = 0;
|
||||
// If output isn't verbose, hide prereleases
|
||||
if (!this._config.verbose) {
|
||||
data.versions = mout.array.filter(data.versions, function (version) {
|
||||
version = semverUtils.parse(version);
|
||||
if (!version.release && !version.build) {
|
||||
return true;
|
||||
}
|
||||
data.numPreReleases++;
|
||||
});
|
||||
data.hidePreReleases = !!data.numPreReleases;
|
||||
}
|
||||
str += '\n' + template.render('std/info.std', data);
|
||||
}
|
||||
|
||||
@@ -414,8 +394,6 @@ StandardRenderer.prototype._write = function (stream, str) {
|
||||
};
|
||||
|
||||
StandardRenderer.prototype._highlightJson = function (json) {
|
||||
var cardinal = require('cardinal');
|
||||
|
||||
return cardinal.highlight(stringifyObject(json, { indent: ' ' }), {
|
||||
theme: {
|
||||
String: {
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"command": "login",
|
||||
"description": "Authenticate with GitHub and store credentials to be used later.",
|
||||
"usage": [
|
||||
"login [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
},
|
||||
{
|
||||
"shorthand": "-t",
|
||||
"flag": "--token",
|
||||
"description": "Pass an existing GitHub auth token rather than prompting for username and password."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"command": "unregister",
|
||||
"description": "Unregisters a package.",
|
||||
"usage": [
|
||||
"unregister <name> [<options>]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-f",
|
||||
"flag": "--force",
|
||||
"description": "Bypasses confirmation. Bower login is still needed."
|
||||
},
|
||||
{
|
||||
"shorthand": "-h",
|
||||
"flag": "--help",
|
||||
"description": "Show this help message"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"command": "version",
|
||||
"description": "Creates an empty version commit and tag, and fail if the repo is not clean.\n\nThe <version> argument should be a valid semver string, or one of following:\nbuild, patch, minor, major.\n\nIf supplied with --message (shorthand: -m) config option, bower will use it\nas a commit message when creating a version commit. If the message config\ncontains %s then that will be replaced with the resulting version number.\n\nFor example:\n\n bower version patch -m \"Upgrade to %s for reasons\"",
|
||||
"usage": [
|
||||
"version [<version> | major | minor | patch]"
|
||||
],
|
||||
"options": [
|
||||
{
|
||||
"shorthand": "-m",
|
||||
"flag": "--message",
|
||||
"description": "Custom git commit and tag message"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{{#yellow}}Please note that,{{/yellow}}
|
||||
{{#condense}}
|
||||
{{#each picks}}
|
||||
{{#if dependants}}{{#green}}{{dependants}}{{/green}}{{else}}none{{/if}} depends on {{#cyan}}{{endpoint.name}}#{{endpoint.target}}{{/cyan}}{{#if pkgMeta._release}} which resolved to {{#green}}{{endpoint.name}}#{{pkgMeta._release}}{{/green}}{{/if}}
|
||||
{{/each}}
|
||||
{{/condense}}
|
||||
|
||||
Resort to using {{#cyan}}{{suitable.endpoint.name}}#{{resolution}}{{/cyan}} which resolved to {{#green}}{{suitable.endpoint.name}}#{{suitable.pkgMeta._release}}{{/green}}
|
||||
Code incompatibilities may occur.
|
||||
@@ -1,30 +0,0 @@
|
||||
var abbrev = require('abbrev');
|
||||
var mout = require('mout');
|
||||
|
||||
function expandNames(obj, prefix, stack) {
|
||||
prefix = prefix || '';
|
||||
stack = stack || [];
|
||||
|
||||
mout.object.forOwn(obj, function (value, name) {
|
||||
name = prefix + name;
|
||||
|
||||
stack.push(name);
|
||||
|
||||
if (typeof value === 'object' && !value.line) {
|
||||
expandNames(value, name + ' ', stack);
|
||||
}
|
||||
});
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
module.exports = function (commands) {
|
||||
var abbreviations = abbrev(expandNames(commands));
|
||||
|
||||
abbreviations.i = 'install';
|
||||
abbreviations.rm = 'uninstall';
|
||||
abbreviations.unlink = 'uninstall';
|
||||
abbreviations.ls = 'list';
|
||||
|
||||
return abbreviations;
|
||||
};
|
||||
61
lib/util/analytics.js
Normal file
61
lib/util/analytics.js
Normal file
@@ -0,0 +1,61 @@
|
||||
var Q = require('q');
|
||||
var Insight = require('insight');
|
||||
var mout = require('mout');
|
||||
var config = require('../config');
|
||||
var pkg = require('../../package.json');
|
||||
|
||||
var analytics = module.exports;
|
||||
var insight;
|
||||
|
||||
// Initializes the application-wide insight singleton and asks for the
|
||||
// permission on the CLI during the first run.
|
||||
analytics.setup = function setup() {
|
||||
var deferred = Q.defer();
|
||||
insight = new Insight({
|
||||
trackingCode: 'UA-43531210-1',
|
||||
packageName: pkg.name,
|
||||
packageVersion: pkg.version
|
||||
});
|
||||
|
||||
// Display the ask prompt only if it hasn't been answered before
|
||||
// and the current session is looking to configure the analytics.
|
||||
if (insight.optOut === undefined && config.analytics) {
|
||||
insight.askPermission(null, deferred.resolve);
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
var Tracker = analytics.Tracker = function Tracker(config) {
|
||||
if (!config.analytics) {
|
||||
this.track = function noop() {};
|
||||
}
|
||||
};
|
||||
|
||||
Tracker.prototype.track = function track() {
|
||||
if (!insight) {
|
||||
throw new Error('You must call analytics.setup() prior to tracking.');
|
||||
}
|
||||
insight.track.apply(insight, arguments);
|
||||
};
|
||||
|
||||
Tracker.prototype.trackDecomposedEndpoints = function trackDecomposedEndpoints(command, endpoints) {
|
||||
endpoints.forEach(function (endpoint) {
|
||||
this.track(command, endpoint.source, endpoint.target);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Tracker.prototype.trackPackages = function trackPackages(command, packages) {
|
||||
mout.object.forOwn(packages, function (package) {
|
||||
var meta = package.pkgMeta;
|
||||
this.track(command, meta.name, meta.version);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Tracker.prototype.trackNames = function trackNames(command, names) {
|
||||
names.forEach(function (name) {
|
||||
this.track(command, name);
|
||||
}.bind(this));
|
||||
};
|
||||
@@ -8,12 +8,9 @@ var createError = require('./createError');
|
||||
|
||||
// The concurrency limit here is kind of magic. You don't really gain a lot from
|
||||
// having a large number of commands spawned at once, so it isn't super
|
||||
// important for this number to be large. Reports have shown that much more than 5
|
||||
// or 10 cause issues for corporate networks, private repos or situations where
|
||||
// internet bandwidth is limited. We're running with a concurrency of 5 until
|
||||
// 1.4.X is released, at which time we'll move to what was discussed in #1262
|
||||
// https://github.com/bower/bower/pull/1262
|
||||
var throttler = new PThrottler(5);
|
||||
// important for this number to be large. However, it would still be nice to
|
||||
// *know* how high this number can be, rather than having to guess low.
|
||||
var throttler = new PThrottler(50);
|
||||
|
||||
var winBatchExtensions;
|
||||
var winWhichCache;
|
||||
@@ -99,7 +96,7 @@ function executeCmd(command, args, options) {
|
||||
fullCommand += args.length ? ' ' + args.join(' ') : '';
|
||||
|
||||
// Build the error instance
|
||||
error = createError('Failed to execute "' + fullCommand + '", exit code of #' + code + '\n' + stderr, 'ECMDERR', {
|
||||
error = createError('Failed to execute "' + fullCommand + '", exit code of #' + code, 'ECMDERR', {
|
||||
details: stderr,
|
||||
exitCode: code
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
var fstream = require('fstream');
|
||||
var fstreamIgnore = require('fstream-ignore');
|
||||
var fs = require('./fs');
|
||||
var fs = require('graceful-fs');
|
||||
var Q = require('q');
|
||||
|
||||
function copy(reader, writer) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var fs = require('./fs');
|
||||
var fs = require('graceful-fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var mkdirp = require('mkdirp');
|
||||
@@ -6,7 +6,9 @@ var createError = require('./createError');
|
||||
|
||||
var isWin = process.platform === 'win32';
|
||||
|
||||
function createLink(src, dst, type) {
|
||||
function createLink(src, dst, options) {
|
||||
options = options || {};
|
||||
|
||||
var dstDir = path.dirname(dst);
|
||||
|
||||
// Create directory
|
||||
@@ -26,7 +28,12 @@ function createLink(src, dst, type) {
|
||||
})
|
||||
// Create symlink
|
||||
.then(function (stat) {
|
||||
type = type || (stat.isDirectory() ? 'dir' : 'file');
|
||||
var type = options.type || (stat.isDirectory() ? 'dir' : 'file');
|
||||
if (options.relative && !isWin) {
|
||||
src = path.relative(path.dirname(dst), src);
|
||||
} else {
|
||||
src = path.resolve(src);
|
||||
}
|
||||
|
||||
return Q.nfcall(fs.symlink, src, dst, type)
|
||||
.fail(function (err) {
|
||||
|
||||
39
lib/util/dependencyLinker.js
Normal file
39
lib/util/dependencyLinker.js
Normal file
@@ -0,0 +1,39 @@
|
||||
var Q = require('q');
|
||||
var Config = require('bower-config');
|
||||
var createLink = require('../util/createLink');
|
||||
var readJson = require('../util/readJson');
|
||||
var path = require('path');
|
||||
var fs = require('graceful-fs');
|
||||
|
||||
module.exports.link = function (packages, config, logger) {
|
||||
var rootComponentsDir = path.join(config.cwd, config.directory);
|
||||
|
||||
// create links for new packages and make sure that existing
|
||||
// ones has all dependencies linked
|
||||
var promises = Object.keys(packages).map(function (depName) {
|
||||
var dep = packages[depName];
|
||||
var depPath = dep.canonicalDir || path.join(rootComponentsDir, dep.name);
|
||||
var conf = Config.read(depPath);
|
||||
var componentsDir = path.join(conf.cwd, conf.directory);
|
||||
|
||||
return readJson(depPath)
|
||||
.spread(function (json, deprecated, assumed) {
|
||||
if (json.dependencies) {
|
||||
return Q.all(Object.keys(json.dependencies || {}).map(function (d) {
|
||||
var dst = path.join(componentsDir, d);
|
||||
var src = path.join(rootComponentsDir, d);
|
||||
|
||||
return Q.nfcall(fs.stat, dst)
|
||||
.fail(function () {
|
||||
logger.info('dep-link', d, {
|
||||
name: json.name + '#' + d
|
||||
});
|
||||
return createLink(src, dst, {relative: true});
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return Q.all(promises);
|
||||
};
|
||||
@@ -3,20 +3,19 @@ var request = require('request');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var retry = require('retry');
|
||||
var fs = require('graceful-fs');
|
||||
var createError = require('./createError');
|
||||
var createWriteStream = require('fs-write-stream-atomic');
|
||||
var destroy = require('destroy');
|
||||
|
||||
var errorCodes = [
|
||||
'EADDRINFO',
|
||||
'ETIMEDOUT',
|
||||
'ECONNRESET',
|
||||
'ESOCKETTIMEDOUT',
|
||||
'ENOTFOUND'
|
||||
'ESOCKETTIMEDOUT'
|
||||
];
|
||||
|
||||
function download(url, file, options) {
|
||||
var operation;
|
||||
var response;
|
||||
var deferred = Q.defer();
|
||||
var progressDelay = 8000;
|
||||
|
||||
@@ -25,24 +24,46 @@ function download(url, file, options) {
|
||||
factor: 2,
|
||||
minTimeout: 1000,
|
||||
maxTimeout: 35000,
|
||||
randomize: true,
|
||||
progressDelay: progressDelay,
|
||||
gzip: true
|
||||
randomize: true
|
||||
}, options || {});
|
||||
|
||||
// Retry on network errors
|
||||
operation = retry.operation(options);
|
||||
|
||||
operation.attempt(function () {
|
||||
Q.fcall(fetch, url, file, options)
|
||||
.then(function (response) {
|
||||
deferred.resolve(response);
|
||||
var req;
|
||||
var writeStream;
|
||||
var contentLength;
|
||||
var bytesDownloaded = 0;
|
||||
|
||||
req = progress(request(url, options), {
|
||||
delay: progressDelay
|
||||
})
|
||||
.progress(function (status) {
|
||||
deferred.notify(status);
|
||||
.on('response', function (res) {
|
||||
var status = res.statusCode;
|
||||
|
||||
if (status < 200 || status >= 300) {
|
||||
return deferred.reject(createError('Status code of ' + status, 'EHTTP'));
|
||||
}
|
||||
|
||||
response = res;
|
||||
contentLength = Number(res.headers['content-length']);
|
||||
})
|
||||
.fail(function (error) {
|
||||
// Save timeout before retrying to report
|
||||
.on('data', function (data) {
|
||||
bytesDownloaded += data.length;
|
||||
})
|
||||
.on('progress', function (state) {
|
||||
deferred.notify(state);
|
||||
})
|
||||
.on('end', function () {
|
||||
// Check if the whole file was downloaded
|
||||
// In some unstable connections the ACK/FIN packet might be sent in the
|
||||
// middle of the download
|
||||
// See: https://github.com/joyent/node/issues/6143
|
||||
if (contentLength && bytesDownloaded < contentLength) {
|
||||
req.emit('error', createError('Transfer closed with ' + (contentLength - bytesDownloaded) + ' bytes remaining to read', 'EINCOMPLETE'));
|
||||
}
|
||||
})
|
||||
.on('error', function (error) {
|
||||
var timeout = operation._timeouts[0];
|
||||
|
||||
// Reject if error is not a network error
|
||||
@@ -53,88 +74,33 @@ function download(url, file, options) {
|
||||
// Next attempt will start reporting download progress immediately
|
||||
progressDelay = 0;
|
||||
|
||||
// This will schedule next retry or return false
|
||||
// Check if there are more retries
|
||||
if (operation.retry(error)) {
|
||||
deferred.notify({
|
||||
// Ensure that there are no more events from this request
|
||||
req.removeAllListeners();
|
||||
req.on('error', function () {});
|
||||
// Ensure that there are no more events from the write stream
|
||||
writeStream.removeAllListeners();
|
||||
writeStream.on('error', function () {});
|
||||
|
||||
return deferred.notify({
|
||||
retry: true,
|
||||
delay: timeout,
|
||||
error: error
|
||||
});
|
||||
} else {
|
||||
deferred.reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function fetch(url, file, options) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var contentLength;
|
||||
var bytesDownloaded = 0;
|
||||
|
||||
var reject = function (error) {
|
||||
deferred.reject(error);
|
||||
};
|
||||
|
||||
var req = progress(request(url, options), {
|
||||
delay: options.progressDelay
|
||||
})
|
||||
.on('response', function (response) {
|
||||
contentLength = Number(response.headers['content-length']);
|
||||
|
||||
var status = response.statusCode;
|
||||
|
||||
if (status < 200 || status >= 300) {
|
||||
return deferred.reject(createError('Status code of ' + status, 'EHTTP'));
|
||||
}
|
||||
|
||||
var writeStream = createWriteStream(file);
|
||||
var errored = false;
|
||||
|
||||
// Change error listener so it cleans up writeStream before exiting
|
||||
req.removeListener('error', reject);
|
||||
req.on('error', function (error) {
|
||||
errored = true;
|
||||
destroy(req);
|
||||
destroy(writeStream);
|
||||
|
||||
// Wait for writeStream to cleanup after itself...
|
||||
// TODO: Maybe there's a better way?
|
||||
setTimeout(function () {
|
||||
deferred.reject(error);
|
||||
}, 50);
|
||||
// No more retries, reject!
|
||||
deferred.reject(error);
|
||||
});
|
||||
|
||||
writeStream.on('finish', function () {
|
||||
if (!errored) {
|
||||
destroy(req);
|
||||
deferred.resolve(response);
|
||||
}
|
||||
// Pipe read stream to write stream
|
||||
writeStream = req
|
||||
.pipe(fs.createWriteStream(file))
|
||||
.on('error', deferred.reject)
|
||||
.on('close', function () {
|
||||
deferred.resolve(response);
|
||||
});
|
||||
|
||||
req.pipe(writeStream);
|
||||
})
|
||||
.on('data', function (data) {
|
||||
bytesDownloaded += data.length;
|
||||
})
|
||||
.on('progress', function (state) {
|
||||
deferred.notify(state);
|
||||
})
|
||||
.on('error', reject)
|
||||
.on('end', function () {
|
||||
// Check if the whole file was downloaded
|
||||
// In some unstable connections the ACK/FIN packet might be sent in the
|
||||
// middle of the download
|
||||
// See: https://github.com/joyent/node/issues/6143
|
||||
if (contentLength && bytesDownloaded < contentLength) {
|
||||
req.emit('error', createError(
|
||||
'Transfer closed with ' + (contentLength - bytesDownloaded) + ' bytes remaining to read',
|
||||
'EINCOMPLETE'
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
var path = require('path');
|
||||
var fs = require('./fs');
|
||||
var fs = require('graceful-fs');
|
||||
var zlib = require('zlib');
|
||||
var DecompressZip = require('decompress-zip');
|
||||
var tar = require('tar-fs');
|
||||
var tar = require('tar');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var junk = require('junk');
|
||||
var createError = require('./createError');
|
||||
var createWriteStream = require('fs-write-stream-atomic');
|
||||
var destroy = require('destroy');
|
||||
var tmp = require('tmp');
|
||||
|
||||
// This forces the default chunk size to something small in an attempt
|
||||
// to avoid issue #314
|
||||
@@ -26,7 +23,6 @@ extractors = {
|
||||
'.gz': extractGz,
|
||||
'application/zip': extractZip,
|
||||
'application/x-zip': extractZip,
|
||||
'application/x-zip-compressed': extractZip,
|
||||
'application/x-tar': extractTar,
|
||||
'application/x-tgz': extractTarGz,
|
||||
'application/x-gzip': extractGz
|
||||
@@ -52,24 +48,15 @@ function extractZip(archive, dst) {
|
||||
function extractTar(archive, dst) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var stream = fs.createReadStream(archive);
|
||||
|
||||
var reject = function (error) {
|
||||
destroy(stream);
|
||||
deferred.reject(error);
|
||||
};
|
||||
|
||||
stream.on('error', reject)
|
||||
.pipe(tar.extract(dst, {
|
||||
ignore: isSymlink, // Filter symlink files
|
||||
dmode: 0555, // Ensure dirs are readable
|
||||
fmode: 0444 // Ensure files are readable
|
||||
fs.createReadStream(archive)
|
||||
.on('error', deferred.reject)
|
||||
.pipe(tar.Extract({
|
||||
path: dst,
|
||||
follow: false, // Do not follow symlinks (#699)
|
||||
filter: filterSymlinks // Filter symlink files
|
||||
}))
|
||||
.on('error', reject)
|
||||
.on('finish', function (result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
.on('error', deferred.reject)
|
||||
.on('close', deferred.resolve.bind(deferred, dst));
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
@@ -77,26 +64,17 @@ function extractTar(archive, dst) {
|
||||
function extractTarGz(archive, dst) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var stream = fs.createReadStream(archive);
|
||||
|
||||
var reject = function (error) {
|
||||
destroy(stream);
|
||||
deferred.reject(error);
|
||||
};
|
||||
|
||||
stream.on('error', reject)
|
||||
fs.createReadStream(archive)
|
||||
.on('error', deferred.reject)
|
||||
.pipe(zlib.createGunzip())
|
||||
.on('error', reject)
|
||||
.pipe(tar.extract(dst, {
|
||||
ignore: isSymlink, // Filter symlink files
|
||||
dmode: 0555, // Ensure dirs are readable
|
||||
fmode: 0444 // Ensure files are readable
|
||||
.on('error', deferred.reject)
|
||||
.pipe(tar.Extract({
|
||||
path: dst,
|
||||
follow: false, // Do not follow symlinks (#699)
|
||||
filter: filterSymlinks // Filter symlink files
|
||||
}))
|
||||
.on('error', reject)
|
||||
.on('finish', function (result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
.on('error', deferred.reject)
|
||||
.on('close', deferred.resolve.bind(deferred, dst));
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
@@ -104,29 +82,17 @@ function extractTarGz(archive, dst) {
|
||||
function extractGz(archive, dst) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
var stream = fs.createReadStream(archive);
|
||||
|
||||
var reject = function (error) {
|
||||
destroy(stream);
|
||||
deferred.reject(error);
|
||||
};
|
||||
stream.on('error', reject)
|
||||
fs.createReadStream(archive)
|
||||
.on('error', deferred.reject)
|
||||
.pipe(zlib.createGunzip())
|
||||
.on('error', reject)
|
||||
.pipe(createWriteStream(dst))
|
||||
.on('error', reject)
|
||||
.on('finish', function (result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
.on('error', deferred.reject)
|
||||
.pipe(fs.createWriteStream(dst))
|
||||
.on('error', deferred.reject)
|
||||
.on('close', deferred.resolve.bind(deferred, dst));
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function isSymlink(entry) {
|
||||
return entry.type === 'SymbolicLink';
|
||||
}
|
||||
|
||||
function filterSymlinks(entry) {
|
||||
return entry.type !== 'SymbolicLink';
|
||||
}
|
||||
@@ -165,11 +131,15 @@ function isSingleDir(dir) {
|
||||
});
|
||||
}
|
||||
|
||||
function moveDirectory(srcDir, destDir) {
|
||||
return Q.nfcall(fs.readdir, srcDir)
|
||||
function moveSingleDirContents(dir) {
|
||||
var destDir = path.dirname(dir);
|
||||
|
||||
return Q.nfcall(fs.readdir, dir)
|
||||
.then(function (files) {
|
||||
var promises = files.map(function (file) {
|
||||
var src = path.join(srcDir, file);
|
||||
var promises;
|
||||
|
||||
promises = files.map(function (file) {
|
||||
var src = path.join(dir, file);
|
||||
var dst = path.join(destDir, file);
|
||||
|
||||
return Q.nfcall(fs.rename, src, dst);
|
||||
@@ -178,7 +148,7 @@ function moveDirectory(srcDir, destDir) {
|
||||
return Q.all(promises);
|
||||
})
|
||||
.then(function () {
|
||||
return Q.nfcall(fs.rmdir, srcDir);
|
||||
return Q.nfcall(fs.rmdir, dir);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -212,53 +182,44 @@ function extract(src, dst, opts) {
|
||||
return Q.reject(createError('File ' + src + ' is not a known archive', 'ENOTARCHIVE'));
|
||||
}
|
||||
|
||||
// Extract to a temporary directory in case of file name clashes
|
||||
return Q.nfcall(tmp.dir, {
|
||||
template: dst + '-XXXXXX',
|
||||
mode: 0777 & ~process.umask()
|
||||
}).then(function (tempDir) {
|
||||
// nfcall may return multiple callback arguments as an array
|
||||
return Array.isArray(tempDir) ? tempDir[0] : tempDir;
|
||||
}).then(function (tempDir) {
|
||||
|
||||
// Check archive file size
|
||||
promise = Q.nfcall(fs.stat, src)
|
||||
.then(function (stat) {
|
||||
if (stat.size <= 8) {
|
||||
throw createError('File ' + src + ' is an invalid archive', 'ENOTARCHIVE');
|
||||
}
|
||||
|
||||
// Extract archive
|
||||
return extractor(src, tempDir);
|
||||
});
|
||||
|
||||
// Remove archive
|
||||
if (!opts.keepArchive) {
|
||||
promise = promise
|
||||
.then(function () {
|
||||
return Q.nfcall(fs.unlink, src);
|
||||
});
|
||||
// Check archive file size
|
||||
promise = Q.nfcall(fs.stat, src)
|
||||
.then(function (stat) {
|
||||
if (stat.size <= 8) {
|
||||
throw createError('File ' + src + ' is an invalid archive', 'ENOTARCHIVE');
|
||||
}
|
||||
|
||||
// Move contents from the temporary directory
|
||||
// If the contents are a single directory (and we're not preserving structure),
|
||||
// move its contents directly instead.
|
||||
// Extract archive
|
||||
return extractor(src, dst);
|
||||
});
|
||||
|
||||
// TODO: There's an issue here if the src and dst are the same and
|
||||
// The zip name is the same as some of the zip file contents
|
||||
// Maybe create a temp directory inside dst, unzip it there,
|
||||
// unlink zip and then move contents
|
||||
|
||||
// Remove archive
|
||||
if (!opts.keepArchive) {
|
||||
promise = promise
|
||||
.then(function () {
|
||||
return isSingleDir(tempDir);
|
||||
return Q.nfcall(fs.unlink, src);
|
||||
});
|
||||
}
|
||||
|
||||
// Move contents if a single directory was extracted
|
||||
if (!opts.keepStructure) {
|
||||
promise = promise
|
||||
.then(function () {
|
||||
return isSingleDir(dst);
|
||||
})
|
||||
.then(function (singleDir) {
|
||||
if (singleDir && !opts.keepStructure) {
|
||||
return moveDirectory(singleDir, dst);
|
||||
} else {
|
||||
return moveDirectory(tempDir, dst);
|
||||
}
|
||||
return singleDir ? moveSingleDirContents(singleDir) : null;
|
||||
});
|
||||
}
|
||||
|
||||
// Resolve promise to the dst dir
|
||||
return promise.then(function () {
|
||||
return dst;
|
||||
});
|
||||
// Resolve promise to the dst dir
|
||||
return promise.then(function () {
|
||||
return dst;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
var fs = require('graceful-fs');
|
||||
|
||||
var readdir = fs.readdir.bind(fs);
|
||||
var readdirSync = fs.readdirSync.bind(fs);
|
||||
|
||||
module.exports = fs;
|
||||
|
||||
module.exports.readdir = function (dir, callback) {
|
||||
fs.stat(dir, function (err, stats) {
|
||||
if (err) return callback(err);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return readdir(dir, callback);
|
||||
} else {
|
||||
var error = new Error('ENOTDIR, not a directory \'' + dir + '\'');
|
||||
error.code = 'ENOTDIR';
|
||||
error.path = dir;
|
||||
error.errono = -20;
|
||||
return callback(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
module.exports.readdirSync = function (dir) {
|
||||
var stats = fs.statSync(dir);
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return readdirSync(dir);
|
||||
} else {
|
||||
var error = new Error();
|
||||
error.code = 'ENOTDIR';
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
function isPathAbsolute(filePath) {
|
||||
return filePath.charAt(0) === '/';
|
||||
}
|
||||
|
||||
module.exports = isPathAbsolute;
|
||||
@@ -17,16 +17,6 @@ function readJson(file, options) {
|
||||
.spread(function (json, jsonFile) {
|
||||
var deprecated;
|
||||
|
||||
if (options.logger) {
|
||||
var issues = bowerJson.getIssues(json);
|
||||
if (issues.warnings.length > 0) {
|
||||
options.logger.warn('invalid-meta', 'for:' + jsonFile);
|
||||
}
|
||||
issues.warnings.forEach(function (warning) {
|
||||
options.logger.warn('invalid-meta', warning);
|
||||
});
|
||||
}
|
||||
|
||||
jsonFile = path.basename(jsonFile);
|
||||
deprecated = jsonFile === 'component.json' ? jsonFile : false;
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
var path = require('path');
|
||||
var isPathAbsolute = require('./isPathAbsolute');
|
||||
|
||||
function relativeToBaseDir(baseDir) {
|
||||
return function (filePath) {
|
||||
if (isPathAbsolute(filePath)) {
|
||||
return path.resolve(filePath);
|
||||
} else {
|
||||
return path.resolve(baseDir, filePath);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = relativeToBaseDir;
|
||||
@@ -1,29 +1,22 @@
|
||||
var path = require('path');
|
||||
var rimraf = require('../util/rimraf');
|
||||
var rimraf = require('rimraf');
|
||||
var fstreamIgnore = require('fstream-ignore');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
|
||||
function removeIgnores(dir, meta) {
|
||||
function removeIgnores(dir, ignore) {
|
||||
var reader;
|
||||
var applyIgnores;
|
||||
var deferred = Q.defer();
|
||||
var ignored = [];
|
||||
var nonIgnored = ['bower.json'];
|
||||
|
||||
// Don't ignore main files
|
||||
nonIgnored = nonIgnored.concat(meta.main || []);
|
||||
|
||||
nonIgnored = nonIgnored.map(function (file) {
|
||||
return path.join(dir, file);
|
||||
});
|
||||
var nonIgnored = [];
|
||||
|
||||
reader = fstreamIgnore({
|
||||
path: dir,
|
||||
type: 'Directory'
|
||||
});
|
||||
|
||||
reader.addIgnoreRules(meta.ignore || []);
|
||||
reader.addIgnoreRules(ignore);
|
||||
|
||||
// Monkey patch applyIgnores such that we get hold of all ignored files
|
||||
applyIgnores = reader.applyIgnores;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
var requireg = require('requireg');
|
||||
var resolve = require('resolve');
|
||||
|
||||
function startsWith(string, searchString, position) {
|
||||
position = position || 0;
|
||||
return string.substr(position, searchString.length) === searchString;
|
||||
};
|
||||
|
||||
module.exports = function (id, options) {
|
||||
var resolvedPath;
|
||||
|
||||
var cwd = (options || {}).cwd || process.cwd();
|
||||
|
||||
try {
|
||||
resolvedPath = resolve.sync(id, { basedir: cwd });
|
||||
} catch (e) {
|
||||
// Fallback to global require
|
||||
resolvedPath = requireg.resolve(id);
|
||||
}
|
||||
|
||||
return resolvedPath;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
var rimraf = require('rimraf');
|
||||
var chmodr = require('chmodr');
|
||||
var fs = require('./fs');
|
||||
|
||||
module.exports = function (dir, callback) {
|
||||
var checkAndRetry = function (e) {
|
||||
fs.lstat(dir, function (err, stats) {
|
||||
if (err) {
|
||||
if (err.code === 'ENOENT') return callback();
|
||||
return callback(e);
|
||||
}
|
||||
|
||||
chmodr(dir, 0777, function (err) {
|
||||
if (err) return callback(e);
|
||||
rimraf(dir, callback);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
checkAndRetry();
|
||||
} else {
|
||||
rimraf(dir, checkAndRetry);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.sync = function (dir) {
|
||||
var checkAndRetry = function () {
|
||||
try {
|
||||
fs.lstatSync(dir);
|
||||
chmodr.sync(dir, 0777);
|
||||
return rimraf.sync(dir);
|
||||
} catch (e) {
|
||||
if (e.code === 'ENOENT') return;
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
return rimraf.sync(dir);
|
||||
} catch (e) {
|
||||
return checkAndRetry();
|
||||
} finally {
|
||||
return checkAndRetry();
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,8 @@
|
||||
/*jshint multistr:true*/
|
||||
'use strict';
|
||||
var isRoot = require('is-root');
|
||||
var createError = require('./createError');
|
||||
var cli = require('./cli');
|
||||
|
||||
var renderer;
|
||||
|
||||
@@ -18,10 +20,9 @@ sudo, please spend a few minutes learning more about how your system should work
|
||||
make any necessary repairs.\n\n\
|
||||
http://www.joyent.com/blog/installing-node-and-npm\n\
|
||||
https://gist.github.com/isaacs/579814\n\n\
|
||||
You can however run a command with sudo using "--allow-root" option';
|
||||
You can however run a command with sudo using --allow-root option';
|
||||
|
||||
if (isRoot()) {
|
||||
var cli = require('./cli');
|
||||
renderer = cli.getRenderer('', false, config);
|
||||
renderer.error(createError('Cannot be run with sudo', 'ESUDO', { details : errorMsg }));
|
||||
process.exit(1);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
var path = require('path');
|
||||
var fs = require('./fs');
|
||||
var fs = require('graceful-fs');
|
||||
var Handlebars = require('handlebars');
|
||||
var mout = require('mout');
|
||||
var helpers = require('../templates/helpers');
|
||||
var helpers = require('../../templates/helpers');
|
||||
|
||||
var templatesDir = path.resolve(__dirname, '../templates');
|
||||
var templatesDir = path.resolve(__dirname, '../../templates');
|
||||
var cache = {};
|
||||
|
||||
// Register helpers
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
var version = require('../version');
|
||||
|
||||
module.exports = 'node/' + process.version + ' ' + process.platform + ' ' + process.arch + ' ' + ';Bower ' + version;
|
||||
@@ -1,5 +1,5 @@
|
||||
var Q = require('q');
|
||||
var fs = require('./fs');
|
||||
var fs = require('graceful-fs');
|
||||
|
||||
function validLink(file) {
|
||||
// Ensures that a file is a symlink that points
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
var findup = require('findup-sync');
|
||||
|
||||
module.exports = require(findup('package.json', { cwd: __dirname })).version;
|
||||
154
package.json
154
package.json
@@ -1,100 +1,84 @@
|
||||
{
|
||||
"name": "bower",
|
||||
"version": "1.8.0",
|
||||
"description": "The browser package manager",
|
||||
"version": "1.3.2",
|
||||
"description": "The browser package manager.",
|
||||
"author": "Twitter",
|
||||
"license": "MIT",
|
||||
"repository": "bower/bower",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "https://github.com/bower/bower/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/bower/bower.git"
|
||||
},
|
||||
"main": "lib",
|
||||
"bin": "bin/bower",
|
||||
"homepage": "http://bower.io",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"keywords": [
|
||||
"bower"
|
||||
],
|
||||
"dependencies": {
|
||||
"abbrev": "^1.0.5",
|
||||
"archy": "1.0.0",
|
||||
"bower-config": "^1.4.1",
|
||||
"bower-endpoint-parser": "^0.2.2",
|
||||
"bower-json": "^0.8.1",
|
||||
"bower-logger": "^0.2.2",
|
||||
"bower-registry-client": "^1.0.1",
|
||||
"cardinal": "0.4.4",
|
||||
"chalk": "^1.0.0",
|
||||
"chmodr": "^1.0.2",
|
||||
"configstore": "^2.0.0",
|
||||
"decompress-zip": "^0.2.1",
|
||||
"destroy": "^1.0.3",
|
||||
"findup-sync": "^0.3.0",
|
||||
"fs-write-stream-atomic": "1.0.8",
|
||||
"fstream": "^1.0.3",
|
||||
"fstream-ignore": "^1.0.2",
|
||||
"github": "^0.2.3",
|
||||
"glob": "^4.3.2",
|
||||
"graceful-fs": "^4.1.3",
|
||||
"handlebars": "^4.0.5",
|
||||
"inquirer": "0.10.0",
|
||||
"is-root": "^1.0.0",
|
||||
"junk": "^1.0.0",
|
||||
"lockfile": "^1.0.0",
|
||||
"lru-cache": "^2.5.0",
|
||||
"md5-hex": "^1.0.2",
|
||||
"mkdirp": "0.5.0",
|
||||
"mout": "^0.11.0",
|
||||
"nopt": "^3.0.1",
|
||||
"opn": "^4.0.0",
|
||||
"p-throttler": "0.1.1",
|
||||
"promptly": "0.2.0",
|
||||
"q": "^1.1.2",
|
||||
"request": "2.79.0",
|
||||
"request-progress": "0.3.1",
|
||||
"requireg": "^0.1.5",
|
||||
"resolve": "^1.1.7",
|
||||
"retry": "0.6.1",
|
||||
"rimraf": "^2.2.8",
|
||||
"semver": "^2.3.0",
|
||||
"semver-utils": "^1.1.1",
|
||||
"shell-quote": "^1.4.2",
|
||||
"stringify-object": "^1.0.0",
|
||||
"tar-fs": "^1.4.1",
|
||||
"tmp": "0.0.28",
|
||||
"update-notifier": "^0.6.0",
|
||||
"user-home": "^1.1.0",
|
||||
"which": "^1.0.8"
|
||||
"abbrev": "~1.0.4",
|
||||
"archy": "0.0.2",
|
||||
"bower-config": "~0.5.0",
|
||||
"bower-endpoint-parser": "~0.2.0",
|
||||
"bower-json": "~0.4.0",
|
||||
"bower-logger": "~0.2.2",
|
||||
"bower-registry-client": "~0.1.4",
|
||||
"cardinal": "~0.4.0",
|
||||
"chalk": "~0.4.0",
|
||||
"chmodr": "~0.1.0",
|
||||
"decompress-zip": "~0.0.3",
|
||||
"fstream": "~0.1.22",
|
||||
"fstream-ignore": "~0.0.6",
|
||||
"glob": "~3.2.9",
|
||||
"graceful-fs": "~2.0.0",
|
||||
"handlebars": "~1.3.0",
|
||||
"inquirer": "~0.4.0",
|
||||
"junk": "~0.2.2",
|
||||
"mkdirp": "~0.3.5",
|
||||
"mout": "~0.9.1",
|
||||
"nopt": "~2.1.2",
|
||||
"lru-cache": "~2.5.0",
|
||||
"open": "~0.0.3",
|
||||
"osenv": "~0.0.3",
|
||||
"promptly": "~0.2.0",
|
||||
"q": "~1.0.1",
|
||||
"request": "~2.33.0",
|
||||
"request-progress": "~0.3.0",
|
||||
"retry": "~0.6.0",
|
||||
"rimraf": "~2.2.0",
|
||||
"semver": "~2.2.1",
|
||||
"stringify-object": "~0.2.0",
|
||||
"tar": "~0.1.17",
|
||||
"tmp": "~0.0.20",
|
||||
"update-notifier": "~0.1.3",
|
||||
"which": "~1.0.5",
|
||||
"p-throttler": "~0.0.1",
|
||||
"insight": "~0.3.0",
|
||||
"is-root": "~0.1.0",
|
||||
"shell-quote": "~1.4.1",
|
||||
"lockfile": "~0.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"arr-diff": "^2.0.0",
|
||||
"chai": "^3.5.0",
|
||||
"coveralls": "^2.11.9",
|
||||
"expect.js": "^0.3.1",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-cli": "^1.1.0",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"grunt-eslint": "^18.1.0",
|
||||
"grunt-exec": "^0.4.7",
|
||||
"grunt-simple-mocha": "^0.4.1",
|
||||
"in-publish": "^2.0.0",
|
||||
"istanbul": "^0.4.3",
|
||||
"load-grunt-tasks": "^3.5.0",
|
||||
"mocha": "^2.5.3",
|
||||
"multiline": "^1.0.2",
|
||||
"nock": "^9.2.3",
|
||||
"node-uuid": "^1.4.7",
|
||||
"proxyquire": "^1.7.9",
|
||||
"spawn-sync": "1.0.15",
|
||||
"wrench": "^1.5.8"
|
||||
"expect.js": "~0.2.0",
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-simple-mocha": "~0.4.0",
|
||||
"grunt-contrib-watch": "~0.5.3",
|
||||
"grunt-contrib-jshint": "~0.8.0",
|
||||
"grunt-exec": "~0.4.2",
|
||||
"mocha": "~1.18",
|
||||
"nock": "~0.27.2",
|
||||
"istanbul": "~0.2.4",
|
||||
"proxyquire": "~0.5.3",
|
||||
"load-grunt-tasks": "~0.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test",
|
||||
"ci": "grunt travis",
|
||||
"coveralls": "coveralls",
|
||||
"prepublish": "in-publish && echo 'You need to use \"grunt publish\" to publish bower' && false || not-in-publish"
|
||||
"test": "grunt test"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"lib"
|
||||
]
|
||||
"bin": {
|
||||
"bower": "bin/bower"
|
||||
},
|
||||
"preferGlobal": true
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
7
packages/bower-config/.gitignore
vendored
7
packages/bower-config/.gitignore
vendored
@@ -1,7 +0,0 @@
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
test/assets/github-test-package
|
||||
test/assets/github-test-package-copy
|
||||
test/assets/temp
|
||||
test/reports
|
||||
@@ -1,61 +0,0 @@
|
||||
{
|
||||
"predef": [
|
||||
"console",
|
||||
"describe",
|
||||
"it",
|
||||
"after",
|
||||
"afterEach",
|
||||
"before",
|
||||
"beforeEach"
|
||||
],
|
||||
|
||||
"indent": 4,
|
||||
"node": true,
|
||||
"devel": true,
|
||||
|
||||
"bitwise": false,
|
||||
"curly": false,
|
||||
"eqeqeq": true,
|
||||
"forin": false,
|
||||
"immed": true,
|
||||
"latedef": false,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": false,
|
||||
"nonew": true,
|
||||
"plusplus": false,
|
||||
"regexp": false,
|
||||
"undef": true,
|
||||
"unused": true,
|
||||
"quotmark": "single",
|
||||
"strict": false,
|
||||
"trailing": true,
|
||||
|
||||
"asi": false,
|
||||
"boss": true,
|
||||
"debug": false,
|
||||
"eqnull": true,
|
||||
"es5": false,
|
||||
"esnext": false,
|
||||
"evil": false,
|
||||
"expr": false,
|
||||
"funcscope": false,
|
||||
"globalstrict": false,
|
||||
"iterator": false,
|
||||
"lastsemic": false,
|
||||
"laxbreak": true,
|
||||
"laxcomma": false,
|
||||
"loopfunc": true,
|
||||
"multistr": false,
|
||||
"onecase": true,
|
||||
"regexdash": false,
|
||||
"scripturl": false,
|
||||
"smarttabs": false,
|
||||
"shadow": false,
|
||||
"sub": false,
|
||||
"supernew": true,
|
||||
"validthis": false,
|
||||
|
||||
"nomen": false,
|
||||
"white": true
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- '5'
|
||||
- '4'
|
||||
- '0.12'
|
||||
- '0.10'
|
||||
script:
|
||||
- grunt travis
|
||||
@@ -1,103 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
## 1.4.0
|
||||
|
||||
- Change default shorthand resolver from git:// to https://
|
||||
|
||||
## 1.3.1
|
||||
|
||||
- Ignore hook scripts for environment variable expansion
|
||||
|
||||
## 1.3.0 - 2015-12-07
|
||||
|
||||
- Allow the use of environment variables in .bowerrc. Fixes [#41](https://github.com/bower/config/issues/41)
|
||||
- Loads the .bowerrc file from the cwd specified on the command line. Fixes [bower/bower#1993](https://github.com/bower/bower/issues/1993)
|
||||
- Allwow for array notation in ENV variables [#44](https://github.com/bower/config/issues/44)
|
||||
|
||||
## 1.2.3 - 2015-11-27
|
||||
|
||||
- Restores env variables if they are undefined at the beginning
|
||||
- Handles default setting for config.ca. Together with [bower/bower PR #1972](https://github.com/bower/bower/pull/1972), fixes downloading with `strict-ssl` using custom CA
|
||||
- Displays an error message if .bowerrc is a directory instead of file. Fixes [bower/bower#2022](https://github.com/bower/bower/issues/2022)
|
||||
|
||||
## 1.2.2 - 2015-10-16
|
||||
- Fixes registry configurartion expanding [bower/bower#1950](https://github.com/bower/bower/issues/1950)
|
||||
|
||||
## 1.2.1 - 2015-10-15
|
||||
- Fixes case insenstivity HTTP_PROXY setting issue on Windows
|
||||
|
||||
## 1.2.0 - 2015-09-28
|
||||
- Prevent defaulting cwd to process.cwd()
|
||||
|
||||
## 1.1.2 - 2015-09-27
|
||||
- Performs only camel case normalisation before merging
|
||||
|
||||
## 1.1.1 - 2015-09-27
|
||||
- Fix: Merge extra options after camel-case normalisation, instead of before it
|
||||
|
||||
## 1.1.0 - 2015-09-27
|
||||
- Allow for overwriting options with .load(overwrites) / .read(cwd, overwrites)
|
||||
|
||||
## 1.0.1 - 2015-09-27
|
||||
- Update dependencies and relax "mout" version range
|
||||
- Most significant changes:
|
||||
- graceful-fs updated from 2.x version to 4.x
|
||||
- osenv updated to from 0.0.x to 0.1.x, [tmp location changed](https://github.com/npm/osenv/commit/d6eddbc026538b09026b1dbd60fbc081a8c67e03)
|
||||
|
||||
## 1.0.0 - 2015-09-27
|
||||
- Support for no-proxy configuration variable
|
||||
- Overwrite HTTP_PROXY, HTTPS_PROXY, and NO_PROXY env variables in load method
|
||||
- Normalise paths to certificates with contents of them, [#28](https://github.com/bower/config/pull/28)
|
||||
|
||||
## 0.6.1 - 2015-04-1
|
||||
- Fixes merging .bowerrc files upward directory tree. [#25](https://github.com/bower/config/issues/25)
|
||||
|
||||
## 0.6.0 - 2015-03-30
|
||||
- Merge .bowerrc files upward directory tree (fixes [bower/bower#1689](https://github.com/bower/bower/issues/1689)) [#24](https://github.com/bower/config/pull/24)
|
||||
- Allow NPM config variables (resolves [bower/bower#1711](https://github.com/bower/bower/issues/1711)) [#23](https://github.com/bower/config/pull/23)
|
||||
|
||||
## 0.5.2 - 2014-06-09
|
||||
- Fixes downloading of bower modules with ignores when .bowerrc is overridden with a relative tmp path. [#17](https://github.com/bower/config/issues/17) [bower/bower#1299](https://github.com/bower/bower/issues/1299)
|
||||
|
||||
## 0.5.1 - 2014-05-21
|
||||
- [perf] Uses the same mout version as bower
|
||||
- [perf] Uses only relevant parts of mout. Related [bower/bower#1134](https://github.com/bower/bower/pull/1134)
|
||||
|
||||
## 0.5.0 - 2013-08-30
|
||||
- Adds a DEFAULT_REGISTRY key to the Config class that exposes the bower registry UR. [#6](https://github.com/bower/config/issues/6)
|
||||
|
||||
## 0.4.5 - 2013-08-28
|
||||
- Fixes crashing when home is not set
|
||||
|
||||
## 0.4.4 - 2013-08-21
|
||||
- Supports nested environment variables [#8](https://github.com/bower/config/issues/8)
|
||||
|
||||
## 0.4.3 - 2013-08-19
|
||||
- Improvement in argv.config parsing
|
||||
|
||||
## 0.4.2 - 2013-08-18
|
||||
- Sets interative to auto
|
||||
|
||||
## 0.4.1 - 2013-08-18
|
||||
- Generates a fake user instead of using 'unknown'
|
||||
|
||||
## 0.4.0 - 2013-08-16
|
||||
- Suffixes temp folder with the user and 'bower'
|
||||
|
||||
## 0.3.5 - 2013-08-14
|
||||
- Casts buffer to string
|
||||
|
||||
## 0.3.4 - 2013-08-11
|
||||
- Empty .bowerrc files no longer throw an error.
|
||||
|
||||
## 0.3.3 - 2013-08-11
|
||||
- Changes git folder to empty (was not being used anyway)
|
||||
|
||||
## 0.3.2 - 2013-08-07
|
||||
- Uses a known user agent by default when a proxy.
|
||||
|
||||
## 0.3.1 - 2013-08-06
|
||||
- Fixes Typo
|
||||
|
||||
## 0.3.0 - 2013-08-06
|
||||
- Appends the username when using the temporary folder.
|
||||
@@ -1,53 +0,0 @@
|
||||
'use strict';
|
||||
module.exports = function (grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.initConfig({
|
||||
jshint: {
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
},
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
'bin/*',
|
||||
'lib/**/*.js',
|
||||
'test/**/*.js',
|
||||
'!test/assets/**/*',
|
||||
'!test/reports/**/*',
|
||||
'!test/tmp/**/*'
|
||||
]
|
||||
},
|
||||
simplemocha: {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
timeout: '10000'
|
||||
},
|
||||
full: {
|
||||
src: ['test/test.js']
|
||||
},
|
||||
short: {
|
||||
options: {
|
||||
reporter: 'dot'
|
||||
},
|
||||
src: ['test/test.js']
|
||||
}
|
||||
},
|
||||
exec: {
|
||||
cover: {
|
||||
command: 'STRICT_REQUIRE=1 node node_modules/istanbul/lib/cli.js cover --dir ./test/reports node_modules/mocha/bin/_mocha -- --timeout 30000 -R dot test/test.js'
|
||||
},
|
||||
coveralls: {
|
||||
command: 'node node_modules/.bin/coveralls < test/reports/lcov.info'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: ['<%= jshint.files %>'],
|
||||
tasks: ['jshint', 'simplemocha:short']
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('test', ['jshint', 'simplemocha:full']);
|
||||
grunt.registerTask('cover', 'exec:cover');
|
||||
grunt.registerTask('travis', ['jshint', 'exec:cover', 'exec:coveralls']);
|
||||
grunt.registerTask('default', 'test');
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2012 Twitter and other contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
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.
|
||||
@@ -1,76 +0,0 @@
|
||||
# bower-config [](http://travis-ci.org/bower/config)[](https://coveralls.io/github/bower/config?branch=master)
|
||||
|
||||
> The Bower config (`.bowerrc`) reader and writer.
|
||||
|
||||
[Bower](http://bower.io/) can be configured using JSON in a `.bowerrc` file. For example:
|
||||
|
||||
{
|
||||
"directory": "app/components/",
|
||||
"timeout": 120000,
|
||||
"registry": {
|
||||
"search": [
|
||||
"http://localhost:8000",
|
||||
"https://registry.bower.io"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
View the complete [.bowerrc specification](http://bower.io/docs/config/#bowerrc-specification) on the website for more details. Both the `bower.json` and `.bowerrc` specifications are maintained at [github.com/bower/spec](https://github.com/bower/spec).
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
$ npm install --save bower-config
|
||||
```
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
#### .load(overwrites)
|
||||
|
||||
Loads the bower configuration from the configuration files.
|
||||
|
||||
Configuration is overwritten (after camelcase normalisation) with `overwrites` argument.
|
||||
|
||||
This method overwrites following environment variables:
|
||||
|
||||
- `HTTP_PROXY` with `proxy` configuration variable
|
||||
- `HTTPS_PROXY` with `https-proxy` configuration variable
|
||||
- `NO_PROXY` with `no-proxy` configuration variable
|
||||
|
||||
It also clears `http_proxy`, `https_proxy`, and `no_proxy` environment variables.
|
||||
|
||||
To restore those variables you can use `restore` method.
|
||||
|
||||
#### restore()
|
||||
|
||||
Restores environment variables overwritten by `.load` method.
|
||||
|
||||
#### .toObject()
|
||||
|
||||
Returns a deep copy of the underlying configuration object.
|
||||
The returned configuration is normalised.
|
||||
The object keys will be camelCase.
|
||||
|
||||
|
||||
#### #create(cwd)
|
||||
|
||||
Obtains a instance where `cwd` is the current working directory (defaults to `process.cwd`);
|
||||
|
||||
```js
|
||||
var config = require('bower-config').create();
|
||||
// You can also specify a working directory
|
||||
var config2 = require('bower-config').create('./some/path');
|
||||
```
|
||||
|
||||
#### #read(cwd, overrides)
|
||||
|
||||
Alias for:
|
||||
|
||||
```js
|
||||
var configObject = (new Config(cwd)).load(overrides).toJson();
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
|
||||
@@ -1,109 +0,0 @@
|
||||
var lang = require('mout/lang');
|
||||
var object = require('mout/object');
|
||||
var rc = require('./util/rc');
|
||||
var expand = require('./util/expand');
|
||||
var EnvProxy = require('./util/proxy');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
function Config(cwd) {
|
||||
this._cwd = cwd;
|
||||
this._proxy = new EnvProxy();
|
||||
this._config = {};
|
||||
}
|
||||
|
||||
Config.prototype.load = function (overwrites) {
|
||||
this._config = rc('bower', this._cwd);
|
||||
|
||||
this._config = object.merge(
|
||||
expand(this._config || {}),
|
||||
expand(overwrites || {})
|
||||
);
|
||||
|
||||
this._config = normalise(this._config);
|
||||
|
||||
this._proxy.set(this._config);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Config.prototype.restore = function () {
|
||||
this._proxy.restore();
|
||||
};
|
||||
|
||||
function readCertFile(path) {
|
||||
path = path || '';
|
||||
|
||||
var sep = '-----END CERTIFICATE-----';
|
||||
|
||||
var certificates;
|
||||
|
||||
if (path.indexOf(sep) === -1) {
|
||||
certificates = fs.readFileSync(path, { encoding: 'utf8' });
|
||||
} else {
|
||||
certificates = path;
|
||||
}
|
||||
|
||||
return certificates.
|
||||
split(sep).
|
||||
filter(function(s) { return !s.match(/^\s*$/); }).
|
||||
map(function(s) { return s + sep; });
|
||||
}
|
||||
|
||||
function loadCAs(caConfig) {
|
||||
// If a ca file path has been specified, expand that here to the file's
|
||||
// contents. As a user can specify these individually, we must load them
|
||||
// one by one.
|
||||
for (var p in caConfig) {
|
||||
if (caConfig.hasOwnProperty(p)) {
|
||||
var prop = caConfig[p];
|
||||
if (Array.isArray(prop)) {
|
||||
caConfig[p] = prop.map(function(s) {
|
||||
return readCertFile(s);
|
||||
});
|
||||
} else if (prop) {
|
||||
caConfig[p] = readCertFile(prop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Config.prototype.toObject = function () {
|
||||
return lang.deepClone(this._config);
|
||||
};
|
||||
|
||||
Config.create = function (cwd) {
|
||||
return new Config(cwd);
|
||||
};
|
||||
|
||||
Config.read = function (cwd, overrides) {
|
||||
var config = Config.create(cwd);
|
||||
return config.load(overrides).toObject();
|
||||
};
|
||||
|
||||
function normalise(config) {
|
||||
config = expand(config);
|
||||
|
||||
// Some backwards compatible things..
|
||||
if (config.shorthandResolver) {
|
||||
config.shorthandResolver = config.shorthandResolver
|
||||
.replace(/\{\{\{/g, '{{')
|
||||
.replace(/\}\}\}/g, '}}');
|
||||
}
|
||||
|
||||
// Ensure that every registry endpoint does not end with /
|
||||
config.registry.search = config.registry.search.map(function (url) {
|
||||
return url.replace(/\/+$/, '');
|
||||
});
|
||||
config.registry.register = config.registry.register.replace(/\/+$/, '');
|
||||
config.registry.publish = config.registry.publish.replace(/\/+$/, '');
|
||||
config.tmp = path.resolve(config.tmp);
|
||||
|
||||
loadCAs(config.ca);
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
Config.DEFAULT_REGISTRY = require('./util/defaults').registry;
|
||||
|
||||
module.exports = Config;
|
||||
@@ -1,45 +0,0 @@
|
||||
var path = require('path');
|
||||
var paths = require('./paths');
|
||||
|
||||
// Guess proxy defined in the env
|
||||
var proxy = process.env.HTTP_PROXY
|
||||
|| process.env.http_proxy
|
||||
|| null;
|
||||
|
||||
var httpsProxy = process.env.HTTPS_PROXY
|
||||
|| process.env.https_proxy
|
||||
|| proxy;
|
||||
|
||||
var noProxy = process.env.NO_PROXY
|
||||
|| process.env.no_proxy;
|
||||
|
||||
// Use a well known user agent (in this case, curl) when using a proxy,
|
||||
// to avoid potential filtering on many corporate proxies with blank or unknown agents
|
||||
var userAgent = !proxy && !httpsProxy
|
||||
? 'node/' + process.version + ' ' + process.platform + ' ' + process.arch
|
||||
: 'curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5';
|
||||
|
||||
var defaults = {
|
||||
'directory': 'bower_components',
|
||||
'registry': 'https://registry.bower.io',
|
||||
'shorthand-resolver': 'https://github.com/{{owner}}/{{package}}.git',
|
||||
'tmp': paths.tmp,
|
||||
'proxy': proxy,
|
||||
'https-proxy': httpsProxy,
|
||||
'no-proxy': noProxy,
|
||||
'timeout': 30000,
|
||||
'ca': { search: [] },
|
||||
'strict-ssl': true,
|
||||
'user-agent': userAgent,
|
||||
'color': true,
|
||||
'interactive': null,
|
||||
'storage': {
|
||||
packages: path.join(paths.cache, 'packages'),
|
||||
links: path.join(paths.data, 'links'),
|
||||
completion: path.join(paths.data, 'completion'),
|
||||
registry: path.join(paths.cache, 'registry'),
|
||||
empty: path.join(paths.data, 'empty') // Empty dir, used in GIT_TEMPLATE_DIR among others
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = defaults;
|
||||
@@ -1,132 +0,0 @@
|
||||
var object = require('mout/object');
|
||||
var lang = require('mout/lang');
|
||||
var string = require('mout/string');
|
||||
|
||||
function camelCase(config) {
|
||||
var camelCased = {};
|
||||
|
||||
// Camel case
|
||||
object.forOwn(config, function (value, key) {
|
||||
// Ignore null values
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
key = string.camelCase(key.replace(/_/g, '-'));
|
||||
camelCased[key] = lang.isPlainObject(value) ? camelCase(value) : value;
|
||||
});
|
||||
|
||||
return camelCased;
|
||||
}
|
||||
|
||||
// Function to replace ${VAR} - style variables
|
||||
// with values set in the environment
|
||||
// This function expects to be passed a string
|
||||
function doEnvReplaceStr (f) {
|
||||
|
||||
// Un-tildify
|
||||
var untildify = require('untildify');
|
||||
f = untildify(f);
|
||||
|
||||
// replace any ${ENV} values with the appropriate environ.
|
||||
var envExpr = /(\\*)\$\{([^}]+)\}/g;
|
||||
return f.replace(envExpr, function (orig, esc, name) {
|
||||
esc = esc.length && esc.length % 2;
|
||||
if (esc) return orig;
|
||||
if (undefined === process.env[name]) {
|
||||
throw new Error('Environment variable used in .bowerrc is not defined: ' + orig);
|
||||
}
|
||||
|
||||
return process.env[name];
|
||||
});
|
||||
}
|
||||
|
||||
function envReplace(config) {
|
||||
var envReplaced = {};
|
||||
if ( lang.isArray(config) ) {
|
||||
envReplaced = [];
|
||||
}
|
||||
|
||||
object.forOwn(config, function (value, key) {
|
||||
|
||||
// Ignore null values
|
||||
if (value == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore 'scripts'
|
||||
// These hooks run within the shell
|
||||
// environment variable expansion is not required
|
||||
if ( key === 'scripts' && lang.isPlainObject(value) ){
|
||||
envReplaced[key] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform variable replacements based on var type
|
||||
if ( lang.isPlainObject(value) ) {
|
||||
envReplaced[key] = envReplace(value);
|
||||
}
|
||||
else if ( lang.isArray(value) ) {
|
||||
envReplaced[key] = envReplace(value);
|
||||
}
|
||||
else if ( lang.isString(value) ) {
|
||||
envReplaced[key] = doEnvReplaceStr(value);
|
||||
}
|
||||
else {
|
||||
envReplaced[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return envReplaced;
|
||||
}
|
||||
|
||||
function expand(config) {
|
||||
config = camelCase(config);
|
||||
config = envReplace(config);
|
||||
|
||||
if (typeof config.registry === 'string') {
|
||||
config.registry = {
|
||||
default: config.registry,
|
||||
search: [config.registry],
|
||||
register: config.registry,
|
||||
publish: config.registry
|
||||
};
|
||||
} else if (typeof config.registry === 'object') {
|
||||
config.registry.default = config.registry.default || 'https://registry.bower.io';
|
||||
|
||||
config.registry = {
|
||||
default: config.registry.default,
|
||||
search: config.registry.search || config.registry.default,
|
||||
register: config.registry.register || config.registry.default,
|
||||
publish: config.registry.publish || config.registry.default
|
||||
};
|
||||
|
||||
if (config.registry.search && !Array.isArray(config.registry.search)) {
|
||||
config.registry.search = [config.registry.search];
|
||||
}
|
||||
}
|
||||
|
||||
// CA
|
||||
if (typeof config.ca === 'string') {
|
||||
config.ca = {
|
||||
default: config.ca,
|
||||
search: [config.ca],
|
||||
register: config.ca,
|
||||
publish: config.ca
|
||||
};
|
||||
} else if (typeof config.ca === 'object') {
|
||||
if (config.ca.search && !Array.isArray(config.ca.search)) {
|
||||
config.ca.search = [config.ca.search];
|
||||
}
|
||||
|
||||
if (config.ca.default) {
|
||||
config.ca.search = config.ca.search || config.ca.default;
|
||||
config.ca.register = config.ca.register || config.ca.default;
|
||||
config.ca.publish = config.ca.publish || config.ca.default;
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
module.exports = expand;
|
||||
@@ -1,44 +0,0 @@
|
||||
var os = require('os');
|
||||
var path = require('path');
|
||||
var osenv = require('osenv');
|
||||
var crypto = require('crypto');
|
||||
|
||||
function generateFakeUser() {
|
||||
var uid = process.pid + '-' + Date.now() + '-' + Math.floor(Math.random() * 1000000);
|
||||
return crypto.createHash('md5').update(uid).digest('hex');
|
||||
}
|
||||
|
||||
// Assume XDG defaults
|
||||
// See: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
var paths = {
|
||||
config: process.env.XDG_CONFIG_HOME,
|
||||
data: process.env.XDG_DATA_HOME,
|
||||
cache: process.env.XDG_CACHE_HOME
|
||||
};
|
||||
|
||||
// Guess some needed properties based on the user OS
|
||||
var user = (osenv.user() || generateFakeUser()).replace(/\\/g, '-');
|
||||
var tmp = path.join(os.tmpdir ? os.tmpdir() : os.tmpDir(), user);
|
||||
var home = osenv.home();
|
||||
var base;
|
||||
|
||||
// Fallbacks for windows
|
||||
if (process.platform === 'win32') {
|
||||
base = path.resolve(process.env.LOCALAPPDATA || home || tmp);
|
||||
base = path.join(base, 'bower');
|
||||
|
||||
paths.config = paths.config || path.join(base, 'config');
|
||||
paths.data = paths.data || path.join(base, 'data');
|
||||
paths.cache = paths.cache || path.join(base, 'cache');
|
||||
// Fallbacks for other operating systems
|
||||
} else {
|
||||
base = path.resolve(home || tmp);
|
||||
|
||||
paths.config = paths.config || path.join(base, '.config/bower');
|
||||
paths.data = paths.data || path.join(base, '.local/share/bower');
|
||||
paths.cache = paths.cache || path.join(base, '.cache/bower');
|
||||
}
|
||||
|
||||
paths.tmp = path.resolve(path.join(tmp, 'bower'));
|
||||
|
||||
module.exports = paths;
|
||||
@@ -1,79 +0,0 @@
|
||||
// EnvProxy uses the proxy vaiables passed to it in set and sets the
|
||||
// process.env uppercase proxy variables to them with the ability
|
||||
// to restore the original values later
|
||||
var EnvProxy = function() {
|
||||
this.restoreFrom = {};
|
||||
};
|
||||
|
||||
EnvProxy.prototype.set = function (config) {
|
||||
this.config = config;
|
||||
|
||||
// Override environment defaults if proxy config options are set
|
||||
// This will make requests.js follow the proxies in config
|
||||
if (Object.prototype.hasOwnProperty.call(config, 'noProxy')) {
|
||||
this.restoreFrom.NO_PROXY = process.env.NO_PROXY;
|
||||
this.restoreFrom.no_proxy = process.env.no_proxy;
|
||||
delete process.env.no_proxy;
|
||||
process.env.NO_PROXY = config.noProxy;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(config, 'proxy')) {
|
||||
this.restoreFrom.HTTP_PROXY = process.env.HTTP_PROXY;
|
||||
this.restoreFrom.http_proxy = process.env.http_proxy;
|
||||
delete process.env.http_proxy;
|
||||
process.env.HTTP_PROXY = config.proxy;
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(config, 'httpsProxy')) {
|
||||
this.restoreFrom.HTTPS_PROXY = process.env.HTTPS_PROXY;
|
||||
this.restoreFrom.https_proxy = process.env.https_proxy;
|
||||
delete process.env.https_proxy;
|
||||
process.env.HTTPS_PROXY = config.httpsProxy;
|
||||
}
|
||||
};
|
||||
|
||||
EnvProxy.prototype.restore = function () {
|
||||
if (Object.prototype.hasOwnProperty.call(this.config, 'noProxy')) {
|
||||
if (this.restoreFrom.NO_PROXY !== undefined) {
|
||||
process.env.NO_PROXY = this.restoreFrom.NO_PROXY;
|
||||
} else {
|
||||
delete process.env.NO_PROXY;
|
||||
}
|
||||
|
||||
if (this.restoreFrom.no_proxy !== undefined) {
|
||||
process.env.no_proxy = this.restoreFrom.no_proxy;
|
||||
} else {
|
||||
delete process.env.no_proxy;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.config, 'proxy')) {
|
||||
if (this.restoreFrom.HTTP_PROXY !== undefined) {
|
||||
process.env.HTTP_PROXY = this.restoreFrom.HTTP_PROXY;
|
||||
} else {
|
||||
delete process.env.HTTP_PROXY;
|
||||
}
|
||||
|
||||
if (this.restoreFrom.http_proxy !== undefined) {
|
||||
process.env.http_proxy = this.restoreFrom.http_proxy;
|
||||
} else {
|
||||
delete process.env.http_proxy;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(this.config, 'httpsProxy')) {
|
||||
if (this.restoreFrom.HTTPS_PROXY !== undefined) {
|
||||
process.env.HTTPS_PROXY = this.restoreFrom.HTTPS_PROXY;
|
||||
} else {
|
||||
delete process.env.HTTPS_PROXY;
|
||||
}
|
||||
|
||||
if (this.restoreFrom.https_proxy !== undefined) {
|
||||
process.env.https_proxy = this.restoreFrom.https_proxy;
|
||||
} else {
|
||||
delete process.env.https_proxy;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = EnvProxy;
|
||||
@@ -1,158 +0,0 @@
|
||||
var path = require('path');
|
||||
var fs = require('graceful-fs');
|
||||
var optimist = require('optimist');
|
||||
var osenv = require('osenv');
|
||||
var object = require('mout/object');
|
||||
var string = require('mout/string');
|
||||
var paths = require('./paths');
|
||||
var defaults = require('./defaults');
|
||||
|
||||
var win = process.platform === 'win32';
|
||||
var home = osenv.home();
|
||||
|
||||
function rc(name, cwd, argv) {
|
||||
var argvConfig;
|
||||
|
||||
argv = argv || optimist.argv;
|
||||
|
||||
// Parse --config.foo=false
|
||||
argvConfig = object.map(argv.config || {}, function (value) {
|
||||
return value === 'false' ? false : value;
|
||||
});
|
||||
|
||||
// If we have specified a cwd then use this as the base for getting config.
|
||||
cwd = argvConfig.cwd ? argvConfig.cwd : cwd;
|
||||
|
||||
if (cwd) {
|
||||
return object.deepMixIn.apply(null, [
|
||||
{},
|
||||
defaults,
|
||||
{ cwd: cwd },
|
||||
win ? {} : json(path.join('/etc', name + 'rc')),
|
||||
!home ? {} : json(path.join(home, '.' + name + 'rc')),
|
||||
json(path.join(paths.config, name + 'rc')),
|
||||
json(find('.' + name + 'rc', cwd)),
|
||||
env('npm_package_config_' + name + '_'),
|
||||
env(name + '_'),
|
||||
argvConfig
|
||||
]);
|
||||
} else {
|
||||
return object.deepMixIn.apply(null, [
|
||||
{},
|
||||
defaults,
|
||||
win ? {} : json(path.join('/etc', name + 'rc')),
|
||||
!home ? {} : json(path.join(home, '.' + name + 'rc')),
|
||||
json(path.join(paths.config, name + 'rc')),
|
||||
env('npm_package_config_' + name + '_'),
|
||||
env(name + '_'),
|
||||
argvConfig
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
function parse(content, file) {
|
||||
var error;
|
||||
|
||||
if (!content.trim().length) {
|
||||
return {};
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
if (file) {
|
||||
error = new Error('Unable to parse ' + file + ': ' + e.message);
|
||||
} else {
|
||||
error = new Error('Unable to parse rc config: ' + e.message);
|
||||
}
|
||||
|
||||
error.details = content;
|
||||
error.code = 'EMALFORMED';
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function json(file) {
|
||||
var content = {};
|
||||
if (!Array.isArray(file)) {
|
||||
try {
|
||||
content = fs.readFileSync(file).toString();
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parse(content, file);
|
||||
} else {
|
||||
// This is multiple json files
|
||||
file.forEach(function(filename) {
|
||||
if (fs.statSync(filename).isDirectory()) {
|
||||
var error;
|
||||
error = new Error(filename + ' should not be a directory');
|
||||
error.code = 'EFILEISDIR';
|
||||
throw error;
|
||||
}
|
||||
var json = fs.readFileSync(filename).toString();
|
||||
json = parse(json, filename);
|
||||
content = object.merge(content, json);
|
||||
});
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
function env(prefix) {
|
||||
var obj = {};
|
||||
var prefixLength = prefix.length;
|
||||
|
||||
prefix = prefix.toLowerCase();
|
||||
|
||||
object.forOwn(process.env, function (value, key) {
|
||||
key = key.toLowerCase();
|
||||
|
||||
if (string.startsWith(key, prefix)) {
|
||||
var parsedKey = key
|
||||
.substr(prefixLength)
|
||||
.replace(/__/g, '.') // __ is used for nesting
|
||||
.replace(/_/g, '-'); // _ is used as a - separator
|
||||
|
||||
//use a convention patern to accept array from process.env
|
||||
//e.g. export bower_registry__search='["http://abc.com","http://def.com"]'
|
||||
var match = /\[([^\]]*)\]/g.exec(value);
|
||||
var targetValue;
|
||||
if (!match || match.length === 0) {
|
||||
targetValue = value;
|
||||
} else {
|
||||
targetValue = match[1].split(',')
|
||||
.map(function(m) {
|
||||
return m.trim();
|
||||
});
|
||||
}
|
||||
object.set(obj, parsedKey, targetValue);
|
||||
}
|
||||
});
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
function find(filename, dir) {
|
||||
var files = [];
|
||||
|
||||
var walk = function (filename, dir) {
|
||||
var file = path.join(dir, filename);
|
||||
var parent = path.dirname(dir);
|
||||
|
||||
if (fs.existsSync(file)) {
|
||||
files.push(file);
|
||||
}
|
||||
|
||||
if (parent !== dir) {
|
||||
walk(filename, parent);
|
||||
}
|
||||
};
|
||||
|
||||
walk(filename, dir);
|
||||
files.reverse();
|
||||
return files;
|
||||
}
|
||||
|
||||
module.exports = rc;
|
||||
@@ -1,45 +0,0 @@
|
||||
{
|
||||
"name": "bower-config",
|
||||
"version": "1.4.0",
|
||||
"description": "The Bower config reader and writer.",
|
||||
"author": "Twitter",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/bower/bower/tree/master/packages/bower-config",
|
||||
"main": "lib/Config",
|
||||
"homepage": "http://bower.io",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.3",
|
||||
"mout": "^1.0.0",
|
||||
"optimist": "^0.6.1",
|
||||
"osenv": "^0.1.3",
|
||||
"untildify": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coveralls": "^2.11.4",
|
||||
"expect.js": "^0.3.1",
|
||||
"glob": "^4.5.3",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-cli": "^0.1.13",
|
||||
"grunt-contrib-jshint": "^0.10.0",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-coveralls": "^1.0.0",
|
||||
"grunt-exec": "^0.4.6",
|
||||
"grunt-simple-mocha": "^0.4.0",
|
||||
"istanbul": "^0.4.1",
|
||||
"load-grunt-tasks": "^2.0.0",
|
||||
"mkdirp": "^0.5.0",
|
||||
"mocha": "~1.12.0",
|
||||
"node-uuid": "^1.4.3",
|
||||
"q": "^1.2.0",
|
||||
"rimraf": "^2.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"ca": "Equifax Secure CA\n=================\n-----BEGIN CERTIFICATE-----\nMIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE\nChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5\nMB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT\nB0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB\nnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR\nfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW\n8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG\nA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE\nCxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG\nA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS\nspXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB\nAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961\nzgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB\nBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95\n70+sB3c4\n-----END CERTIFICATE-----\n\nGlobalSign Root CA\n==================\n-----BEGIN CERTIFICATE-----\nMIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx\nGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds\nb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV\nBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD\nVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa\nDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc\nTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb\nKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP\nc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX\ngzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\nHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF\nAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj\nY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG\nj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH\nhm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC\nX4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n-----END CERTIFICATE-----"
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"ca": "test/assets/custom-ca/ca-bundle.crt"
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
Equifax Secure CA
|
||||
=================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE
|
||||
ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
|
||||
MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT
|
||||
B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB
|
||||
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR
|
||||
fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW
|
||||
8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG
|
||||
A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE
|
||||
CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG
|
||||
A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS
|
||||
spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB
|
||||
Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961
|
||||
zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB
|
||||
BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95
|
||||
70+sB3c4
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
GlobalSign Root CA
|
||||
==================
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx
|
||||
GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds
|
||||
b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV
|
||||
BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD
|
||||
VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa
|
||||
DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc
|
||||
THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb
|
||||
Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP
|
||||
c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX
|
||||
gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF
|
||||
AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj
|
||||
Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG
|
||||
j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH
|
||||
hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC
|
||||
X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"scripts" : {
|
||||
"postinstall" : "${_myshellvar}"
|
||||
},
|
||||
"storage" : {
|
||||
"packages" : "${_BOWERRC_MY_PACKAGES}",
|
||||
"registry" : {
|
||||
"register": "~/.bower-test/registry",
|
||||
"search": [
|
||||
"${_BOWERRC_MY_USER}:${_BOWERRC_MY_PASS}"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tmp" : "${_BOWERRC_MY_TMP}"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"proxy": "http://HTTP_PROXY",
|
||||
"https-proxy": "http://HTTPS_PROXY",
|
||||
"no-proxy": "google.com"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user