mirror of
https://github.com/bower/bower.git
synced 2026-04-24 03:00:19 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
965a53db21 | ||
|
|
1334ff67ae | ||
|
|
afb27f446a | ||
|
|
fe011b8462 | ||
|
|
096fc3da0d |
@@ -15,4 +15,5 @@ trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
node_modules
|
||||
test/assets
|
||||
test/reports
|
||||
test/sample
|
||||
test/tmp
|
||||
packages/bower-logger/test
|
||||
@@ -17,8 +17,9 @@
|
||||
"strict": 0,
|
||||
"semi": 0,
|
||||
"comma-spacing": 2,
|
||||
"quote-props": [2, "as-needed"],
|
||||
"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,
|
||||
@@ -40,7 +41,7 @@
|
||||
"no-new-wrappers": 2,
|
||||
"no-invalid-this": 0,
|
||||
"space-before-blocks": [2, "always"],
|
||||
"space-before-function-paren": [2, "never"],
|
||||
"space-before-function-paren": [2, {"anonymous": "always", "named": "never"}],
|
||||
"space-infix-ops": 2,
|
||||
"keyword-spacing": 2,
|
||||
"new-parens": 2,
|
||||
|
||||
3
.github/FUNDING.yml
vendored
3
.github/FUNDING.yml
vendored
@@ -1,3 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
open_collective: bower
|
||||
62
.github/workflows/nodejs.yml
vendored
62
.github/workflows/nodejs.yml
vendored
@@ -1,62 +0,0 @@
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Node v${{ matrix.node-version }} on ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# https://github.com/actions/setup-node/issues/27
|
||||
node-version: [0.10.x, 0.12.x, 4.x, 6.x, 8.x, 10.x, 12.x, 14.x]
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Set git config
|
||||
shell: bash
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.symlinks true
|
||||
if: runner.os == 'Windows'
|
||||
|
||||
- uses: actions/checkout@v2
|
||||
- name: install
|
||||
run: yarn && (cd packages/bower-json && yarn link) && yarn link bower-json
|
||||
- name: lint
|
||||
run: npm run lint
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: bower tests
|
||||
run: npm test
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-logger tests
|
||||
run: (cd packages/bower-logger && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-config tests
|
||||
run: (cd packages/bower-config && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-endpoint-parser tests
|
||||
run: (cd packages/bower-endpoint-parser && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-json tests
|
||||
run: (cd packages/bower-json && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
- name: bower-registry-client tests
|
||||
run: (cd packages/bower-registry-client && npm install && npm test)
|
||||
env:
|
||||
CI: true
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,16 +1,2 @@
|
||||
!lib/bin
|
||||
/node_modules
|
||||
/npm-debug.log
|
||||
|
||||
/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
|
||||
package-lock.json
|
||||
npm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
**/node_modules/**
|
||||
**/test/assets/**
|
||||
**/bower_components/**
|
||||
test/sample
|
||||
33
.travis.yml
Normal file
33
.travis.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
sudo: false
|
||||
|
||||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.11"
|
||||
- "0.12"
|
||||
- "4.0"
|
||||
- "4.1"
|
||||
- "4.2"
|
||||
- "5"
|
||||
- "6"
|
||||
|
||||
install:
|
||||
- node --version
|
||||
- npm --version
|
||||
- git --version
|
||||
- svn --version | head -n 1
|
||||
- npm install
|
||||
|
||||
os:
|
||||
- osx
|
||||
- linux
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- os: osx
|
||||
- env: "NODE_VERSION=0.11"
|
||||
|
||||
script:
|
||||
- npm test
|
||||
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,26 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## Newer releases
|
||||
|
||||
Please see: https://github.com/bower/bower/releases
|
||||
|
||||
## 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
|
||||
@@ -104,7 +83,7 @@ https://github.com/npm/npm/issues/11227
|
||||
- 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))
|
||||
- Allow for array notation in ENV variables ([#44](https://github.com/bower/config/issues/44))
|
||||
|
||||
## 1.6.9 - 2015-12-04
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Contributing to Bower
|
||||
|
||||
Bower is a large community project with many different developers contributing at all levels to the project. There is more information about [contributing](https://github.com/bower/bower/wiki/Contributor-Guidelines) in the Wiki.
|
||||
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)
|
||||
@@ -16,6 +16,12 @@ Bower is a large community project with many different developers contributing a
|
||||
* Read [Architecture doc](https://github.com/bower/bower/wiki/Rewrite-architecture)
|
||||
* Triage, close, fix and resolve [issues](https://github.com/bower/bower/issues)
|
||||
|
||||
## Team Meetings
|
||||
|
||||
We communicate through a channel on slack: https://gitter.im/bower
|
||||
|
||||
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),
|
||||
@@ -23,9 +29,12 @@ The issue tracker is the preferred channel for [bug reports](#bugs),
|
||||
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), or in serious cases
|
||||
send an e-mail to team@bower.io
|
||||
|
||||
[Stack Overflow](http://stackoverflow.com/questions/tagged/bower)
|
||||
[Gitter Channel](https://gitter.im/bower/bower)
|
||||
[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.
|
||||
|
||||
|
||||
143
README.md
143
README.md
@@ -1,10 +1,13 @@
|
||||
# Bower - A package manager for the web
|
||||
|
||||
[](https://github.com/bower/bower/actions?query=branch%3Amaster)
|
||||
[](#backers)
|
||||
[](#sponsors)
|
||||
> Bower needs your help. If you're willing to help, please say hello to team@bower.io or [donate](https://salt.bountysource.com/teams/bower)
|
||||
|
||||
> ..psst! While Bower is maintained, we recommend [yarn](https://yarnpkg.com/) and [webpack](https://webpack.js.org/) or [parcel](https://parceljs.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://issuestats.com/github/bower/bower)
|
||||
[](http://issuestats.com/github/bower/bower)
|
||||
|
||||
<img align="right" height="300" src="http://bower.io/img/bower-logo.png">
|
||||
|
||||
@@ -97,11 +100,9 @@ Bower can be configured using JSON in a `.bowerrc` file. Read over available opt
|
||||
|
||||
## Support
|
||||
|
||||
You can ask questions on following channels in order:
|
||||
|
||||
* [Discord chat](https://discord.gg/0fFM7QF0KpZRh2cY)
|
||||
* [StackOverflow](http://stackoverflow.com/questions/tagged/bower)
|
||||
* [Issue Tracker](https://github.com/bower/bower/issues)
|
||||
* team@bower.io
|
||||
* [Mailinglist](http://groups.google.com/group/twitter-bower) - twitter-bower@googlegroups.com
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -118,132 +119,8 @@ Note that on Windows for tests to pass you need to configure Git before cloning:
|
||||
git config --global core.autocrlf input
|
||||
```
|
||||
|
||||
|
||||
## Backers
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/bower#backer)]
|
||||
|
||||
<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>
|
||||
<a href="https://opencollective.com/bower/backer/30/website" target="_blank"><img src="https://opencollective.com/bower/backer/30/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/31/website" target="_blank"><img src="https://opencollective.com/bower/backer/31/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/32/website" target="_blank"><img src="https://opencollective.com/bower/backer/32/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/33/website" target="_blank"><img src="https://opencollective.com/bower/backer/33/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/34/website" target="_blank"><img src="https://opencollective.com/bower/backer/34/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/35/website" target="_blank"><img src="https://opencollective.com/bower/backer/35/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/36/website" target="_blank"><img src="https://opencollective.com/bower/backer/36/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/37/website" target="_blank"><img src="https://opencollective.com/bower/backer/37/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/38/website" target="_blank"><img src="https://opencollective.com/bower/backer/38/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/39/website" target="_blank"><img src="https://opencollective.com/bower/backer/39/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/backer/40/website" target="_blank"><img src="https://opencollective.com/bower/backer/40/avatar.svg"></a>
|
||||
|
||||
## 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/tiers/sponsors/0/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/1/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/2/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/3/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/4/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/5/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/6/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/7/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/8/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/9/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/10/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/11/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/12/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/13/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/14/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/15/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/16/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/17/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/18/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/19/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/20/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/21/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/22/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/23/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/24/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/25/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/26/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/27/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/28/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/29/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/29/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/30/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/30/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/31/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/31/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/32/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/32/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/33/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/33/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/34/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/34/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/35/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/35/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/36/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/36/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/37/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/37/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/38/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/38/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/39/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/39/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/40/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/40/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/41/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/41/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/42/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/42/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/43/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/43/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/44/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/44/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/45/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/45/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/46/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/46/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/47/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/47/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/48/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/48/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/49/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/49/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/50/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/50/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/51/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/51/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/52/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/52/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/53/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/53/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/54/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/54/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/55/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/55/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/56/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/56/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/57/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/57/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/58/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/58/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/59/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/59/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/60/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/60/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/61/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/61/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/62/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/62/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/63/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/63/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/64/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/64/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/65/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/65/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/66/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/66/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/67/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/67/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/68/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/68/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/69/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/69/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/bower/tiers/sponsors/70/website" target="_blank"><img src="https://opencollective.com/bower/tiers/sponsors/70/avatar.svg"></a>
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2012-present Twitter and [other contributors](https://github.com/bower/bower/graphs/contributors)
|
||||
Copyright (c) 2016 Twitter and [other contributors](https://github.com/bower/bower/graphs/contributors)
|
||||
|
||||
Licensed under the MIT License
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# Security Policy
|
||||
|
||||
For critical security issues, please send an e-mail to team@bower.io instead of filing issue here.
|
||||
45
appveyor.yml
Normal file
45
appveyor.yml
Normal file
@@ -0,0 +1,45 @@
|
||||
# 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.0"
|
||||
- nodejs_version: "4.1"
|
||||
- nodejs_version: "4.2"
|
||||
- nodejs_version: "5"
|
||||
- nodejs_version: "6"
|
||||
|
||||
# Finish on first failed build
|
||||
matrix:
|
||||
fast_finish: true
|
||||
|
||||
# Install node, display versions, install dependencies
|
||||
install:
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
- node --version && npm --version
|
||||
- git --version && svn --version
|
||||
- npm install
|
||||
|
||||
# Post-install test scripts.
|
||||
test_script:
|
||||
- cmd: npm test
|
||||
|
||||
# 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:
|
||||
- node_modules -> package.json
|
||||
10
lerna.json
Normal file
10
lerna.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"lerna": "2.0.0-beta.23",
|
||||
"version": "independent",
|
||||
"publishConfig": {
|
||||
"ignore": [
|
||||
"ignored-file",
|
||||
"*.md"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
var mout = require('mout');
|
||||
var fs = require('../util/fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var endpointParser = require('bower-endpoint-parser');
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
var GitHubResolver = require('../core/resolvers/GitHubResolver');
|
||||
var cmd = require('../util/cmd');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function init(logger, config) {
|
||||
var project;
|
||||
|
||||
config = config || {};
|
||||
|
||||
if (!config.cwd) {
|
||||
config.cwd = process.cwd();
|
||||
}
|
||||
|
||||
config = defaultConfig(config);
|
||||
|
||||
// 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'
|
||||
});
|
||||
}
|
||||
|
||||
project = new Project(config, logger);
|
||||
|
||||
// Start with existing JSON details
|
||||
return (
|
||||
readJson(project, logger)
|
||||
// Fill in defaults
|
||||
.then(setDefaults.bind(null, config))
|
||||
// Now prompt user to make changes
|
||||
.then(promptUser.bind(null, logger))
|
||||
// Set ignore based on the response
|
||||
.spread(setIgnore.bind(null, config))
|
||||
// Set dependencies based on the response
|
||||
.spread(setDependencies.bind(null, project))
|
||||
// All done!
|
||||
.spread(saveJson.bind(null, project, logger))
|
||||
);
|
||||
}
|
||||
|
||||
function readJson(project, logger) {
|
||||
return project.hasJson().then(function(json) {
|
||||
if (json) {
|
||||
logger.warn(
|
||||
'existing',
|
||||
'The existing ' +
|
||||
path.basename(json) +
|
||||
' file will be used and filled in'
|
||||
);
|
||||
}
|
||||
|
||||
return project.getJson();
|
||||
});
|
||||
}
|
||||
|
||||
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)) {
|
||||
delete json[key];
|
||||
}
|
||||
});
|
||||
|
||||
logger.info('json', 'Generated json', { json: json });
|
||||
|
||||
// Confirm the json with the user
|
||||
return Q.nfcall(logger.prompt.bind(logger), {
|
||||
type: 'confirm',
|
||||
message: 'Looks good?',
|
||||
default: true
|
||||
}).then(function(good) {
|
||||
if (!good) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Save json (true forces file creation)
|
||||
return project.saveJson(true);
|
||||
});
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
// Name
|
||||
if (!json.name) {
|
||||
json.name = path.basename(config.cwd);
|
||||
}
|
||||
|
||||
// Main
|
||||
if (!json.main) {
|
||||
// Remove '.js' from the end of the package name if it is there
|
||||
name = path.basename(json.name, '.js');
|
||||
|
||||
if (fs.existsSync(path.join(config.cwd, 'index.js'))) {
|
||||
json.main = 'index.js';
|
||||
} else if (fs.existsSync(path.join(config.cwd, name + '.js'))) {
|
||||
json.main = name + '.js';
|
||||
}
|
||||
}
|
||||
|
||||
// Homepage
|
||||
if (!json.homepage) {
|
||||
// Set as GitHub homepage if it's a GitHub repository
|
||||
promise = promise.then(function() {
|
||||
return cmd('git', ['config', '--get', 'remote.origin.url'])
|
||||
.spread(function(stdout) {
|
||||
var pair;
|
||||
|
||||
stdout = stdout.trim();
|
||||
if (!stdout) {
|
||||
return;
|
||||
}
|
||||
|
||||
pair = GitHubResolver.getOrgRepoPair(stdout);
|
||||
if (pair) {
|
||||
json.homepage =
|
||||
'https://github.com/' + pair.org + '/' + pair.repo;
|
||||
}
|
||||
})
|
||||
.fail(function() {});
|
||||
});
|
||||
}
|
||||
|
||||
if (!json.authors) {
|
||||
promise = promise.then(function() {
|
||||
// Get the user name configured in git
|
||||
return cmd('git', [
|
||||
'config',
|
||||
'--get',
|
||||
'--global',
|
||||
'user.name'
|
||||
]).spread(
|
||||
function(stdout) {
|
||||
var gitEmail;
|
||||
var gitName = stdout.trim();
|
||||
|
||||
// Abort if no name specified
|
||||
if (!gitName) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the user email configured in git
|
||||
return cmd('git', [
|
||||
'config',
|
||||
'--get',
|
||||
'--global',
|
||||
'user.email'
|
||||
])
|
||||
.spread(
|
||||
function(stdout) {
|
||||
gitEmail = stdout.trim();
|
||||
},
|
||||
function() {}
|
||||
)
|
||||
.then(function() {
|
||||
json.authors = gitName;
|
||||
json.authors += gitEmail
|
||||
? ' <' + gitEmail + '>'
|
||||
: '';
|
||||
});
|
||||
},
|
||||
function() {}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(function() {
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
function promptUser(logger, json) {
|
||||
var questions = [
|
||||
{
|
||||
name: 'name',
|
||||
message: 'name',
|
||||
default: json.name,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
message: 'description',
|
||||
default: json.description,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'main',
|
||||
message: 'main file',
|
||||
default: json.main,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'keywords',
|
||||
message: 'keywords',
|
||||
default: json.keywords ? json.keywords.toString() : null,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'authors',
|
||||
message: 'authors',
|
||||
default: json.authors ? json.authors.toString() : null,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'license',
|
||||
message: 'license',
|
||||
default: json.license || 'MIT',
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'homepage',
|
||||
message: 'homepage',
|
||||
default: json.homepage,
|
||||
type: 'input'
|
||||
},
|
||||
{
|
||||
name: 'dependencies',
|
||||
message: 'set currently installed components as dependencies?',
|
||||
default:
|
||||
!mout.object.size(json.dependencies) &&
|
||||
!mout.object.size(json.devDependencies),
|
||||
type: 'confirm'
|
||||
},
|
||||
{
|
||||
name: 'ignore',
|
||||
message: 'add commonly ignored files to ignore list?',
|
||||
default: true,
|
||||
type: 'confirm'
|
||||
},
|
||||
{
|
||||
name: 'private',
|
||||
message:
|
||||
'would you like to mark this package as private which prevents it from being accidentally published to the registry?',
|
||||
default: !!json.private,
|
||||
type: 'confirm'
|
||||
}
|
||||
];
|
||||
|
||||
return Q.nfcall(logger.prompt.bind(logger), questions).then(function(
|
||||
answers
|
||||
) {
|
||||
json.name = answers.name;
|
||||
json.description = answers.description;
|
||||
json.main = answers.main;
|
||||
json.keywords = toArray(answers.keywords);
|
||||
json.authors = toArray(answers.authors, ',');
|
||||
json.license = answers.license;
|
||||
json.homepage = answers.homepage;
|
||||
json.private = answers.private || null;
|
||||
|
||||
return [json, answers];
|
||||
});
|
||||
}
|
||||
|
||||
function toArray(value, splitter) {
|
||||
var arr = value.split(splitter || /[\s,]/);
|
||||
|
||||
// Trim values
|
||||
arr = arr.map(function(item) {
|
||||
return item.trim();
|
||||
});
|
||||
|
||||
// Filter empty values
|
||||
arr = arr.filter(function(item) {
|
||||
return !!item;
|
||||
});
|
||||
|
||||
return arr.length ? arr : null;
|
||||
}
|
||||
|
||||
function setIgnore(config, json, answers) {
|
||||
if (answers.ignore) {
|
||||
json.ignore = mout.array.combine(json.ignore || [], [
|
||||
'**/.*',
|
||||
'node_modules',
|
||||
'bower_components',
|
||||
config.directory,
|
||||
'test',
|
||||
'tests'
|
||||
]);
|
||||
}
|
||||
|
||||
return [json, answers];
|
||||
}
|
||||
|
||||
function setDependencies(project, json, answers) {
|
||||
if (answers.dependencies) {
|
||||
return project.getTree().spread(function(tree, flattened, extraneous) {
|
||||
if (extraneous.length) {
|
||||
json.dependencies = {};
|
||||
|
||||
// Add extraneous as dependencies
|
||||
extraneous.forEach(function(extra) {
|
||||
var jsonEndpoint;
|
||||
|
||||
// Skip linked packages
|
||||
if (extra.linked) {
|
||||
return;
|
||||
}
|
||||
|
||||
jsonEndpoint = endpointParser.decomposed2json(
|
||||
extra.endpoint
|
||||
);
|
||||
mout.object.mixIn(json.dependencies, jsonEndpoint);
|
||||
});
|
||||
}
|
||||
|
||||
return [json, answers];
|
||||
});
|
||||
}
|
||||
|
||||
return [json, answers];
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
init.readOptions = function(argv) {
|
||||
return [];
|
||||
};
|
||||
|
||||
module.exports = init;
|
||||
@@ -1,155 +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,60 +0,0 @@
|
||||
var mout = require('mout');
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function prune(logger, options, config) {
|
||||
var project;
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return clean(project, options);
|
||||
}
|
||||
|
||||
function clean(project, options, removed) {
|
||||
removed = removed || {};
|
||||
|
||||
// Continually call clean until there is no more extraneous
|
||||
// dependencies to remove
|
||||
return project
|
||||
.getTree(options)
|
||||
.spread(function(tree, flattened, extraneous) {
|
||||
var names = extraneous.map(function(extra) {
|
||||
return extra.endpoint.name;
|
||||
});
|
||||
|
||||
// Uninstall extraneous
|
||||
return project
|
||||
.uninstall(names, options)
|
||||
.then(function(uninstalled) {
|
||||
// Are we done?
|
||||
if (!mout.object.size(uninstalled)) {
|
||||
return removed;
|
||||
}
|
||||
|
||||
// Not yet, recurse!
|
||||
mout.object.mixIn(removed, uninstalled);
|
||||
return clean(project, options, removed);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
prune.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
production: { type: Boolean, shorthand: 'p' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
delete options.argv;
|
||||
|
||||
return [options];
|
||||
};
|
||||
|
||||
module.exports = prune;
|
||||
@@ -1,100 +0,0 @@
|
||||
var Q = require('q');
|
||||
var chalk = require('chalk');
|
||||
var PackageRepository = require('../core/PackageRepository');
|
||||
var createError = require('../util/createError');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function register(logger, name, source, config) {
|
||||
var repository;
|
||||
var registryClient;
|
||||
var force;
|
||||
var url;
|
||||
var githubSourceRegex = /^\w[\w-]*\/\w[\w-]*$/;
|
||||
var getGithubUrl = function(source) {
|
||||
return 'git@github.com:' + source + '.git';
|
||||
};
|
||||
|
||||
config = defaultConfig(config);
|
||||
force = config.force;
|
||||
|
||||
name = (name || '').trim();
|
||||
source = (source || '').trim();
|
||||
|
||||
url = source.match(githubSourceRegex) ? getGithubUrl(source) : source;
|
||||
|
||||
// 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'
|
||||
);
|
||||
}
|
||||
|
||||
// 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'
|
||||
);
|
||||
}
|
||||
|
||||
// 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
|
||||
});
|
||||
})
|
||||
.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
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
register.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(argv);
|
||||
var name = options.argv.remain[1];
|
||||
var url = options.argv.remain[2];
|
||||
|
||||
return [name, url];
|
||||
};
|
||||
|
||||
module.exports = register;
|
||||
@@ -1,132 +0,0 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var Project = require('../core/Project');
|
||||
var defaultConfig = require('../config');
|
||||
|
||||
function uninstall(logger, names, options, config) {
|
||||
if (!names.length) {
|
||||
return new Q();
|
||||
}
|
||||
|
||||
var project;
|
||||
|
||||
options = options || {};
|
||||
config = defaultConfig(config);
|
||||
project = new Project(config, logger);
|
||||
|
||||
return project.getTree(options).spread(function(tree, flattened) {
|
||||
// Uninstall nodes
|
||||
return (
|
||||
project
|
||||
.uninstall(names, options)
|
||||
// Clean out non-shared uninstalled dependencies
|
||||
.then(function(uninstalled) {
|
||||
var names = Object.keys(uninstalled);
|
||||
var children = [];
|
||||
|
||||
// Grab the dependencies of packages that were uninstalled
|
||||
mout.object.forOwn(flattened, function(node) {
|
||||
if (names.indexOf(node.endpoint.name) !== -1) {
|
||||
children.push.apply(
|
||||
children,
|
||||
mout.object.keys(node.dependencies)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Clean them!
|
||||
return clean(project, children, uninstalled);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function clean(project, names, removed) {
|
||||
removed = removed || {};
|
||||
|
||||
return project.getTree().spread(function(tree, flattened) {
|
||||
var nodes = [];
|
||||
var dependantsCounter = {};
|
||||
|
||||
// Grab the nodes of each specified name
|
||||
mout.object.forOwn(flattened, function(node) {
|
||||
if (names.indexOf(node.endpoint.name) !== -1) {
|
||||
nodes.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
// Walk the down the tree, gathering dependants of the packages
|
||||
project.walkTree(
|
||||
tree,
|
||||
function(node, nodeName) {
|
||||
if (names.indexOf(nodeName) !== -1) {
|
||||
dependantsCounter[nodeName] =
|
||||
dependantsCounter[nodeName] || 0;
|
||||
dependantsCounter[nodeName] += node.nrDependants;
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
// Filter out those that have no dependants
|
||||
nodes = nodes.filter(function(node) {
|
||||
return !dependantsCounter[node.endpoint.name];
|
||||
});
|
||||
|
||||
// Are we done?
|
||||
if (!nodes.length) {
|
||||
return Q.resolve(removed);
|
||||
}
|
||||
|
||||
// Grab the nodes after filtering
|
||||
names = nodes.map(function(node) {
|
||||
return node.endpoint.name;
|
||||
});
|
||||
|
||||
// Uninstall them
|
||||
return (
|
||||
project
|
||||
.uninstall(names)
|
||||
// Clean out non-shared uninstalled dependencies
|
||||
.then(function(uninstalled) {
|
||||
var children;
|
||||
|
||||
mout.object.mixIn(removed, uninstalled);
|
||||
|
||||
// Grab the dependencies of packages that were uninstalled
|
||||
children = [];
|
||||
nodes.forEach(function(node) {
|
||||
children.push.apply(
|
||||
children,
|
||||
mout.object.keys(node.dependencies)
|
||||
);
|
||||
});
|
||||
|
||||
// Recurse!
|
||||
return clean(project, children, removed);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------
|
||||
|
||||
uninstall.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = 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];
|
||||
};
|
||||
|
||||
module.exports = uninstall;
|
||||
@@ -1,90 +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,210 +0,0 @@
|
||||
var semver = require('semver');
|
||||
var which = require('../util/which');
|
||||
var fs = require('../util/fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var execFile = require('../util/childProcess').execFile;
|
||||
var defaultConfig = require('../config');
|
||||
var createError = require('../util/createError');
|
||||
|
||||
function version(logger, versionArg, options, config) {
|
||||
options = options || {};
|
||||
|
||||
config = defaultConfig(config);
|
||||
|
||||
return bump(logger, config, versionArg, options.message);
|
||||
}
|
||||
|
||||
function bump(logger, config, versionArg, message) {
|
||||
var cwd = config.cwd || process.cwd();
|
||||
var newVersion;
|
||||
|
||||
if (!versionArg) {
|
||||
throw createError('No <version> agrument provided', 'EREADOPTIONS');
|
||||
}
|
||||
|
||||
return driver
|
||||
.check(cwd)
|
||||
.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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
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(gitPath) {
|
||||
return Q.nfcall(
|
||||
execFile,
|
||||
gitPath,
|
||||
['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
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
version.readOptions = function(argv) {
|
||||
var cli = require('../util/cli');
|
||||
|
||||
var options = cli.readOptions(
|
||||
{
|
||||
message: { type: String, shorthand: 'm' }
|
||||
},
|
||||
argv
|
||||
);
|
||||
|
||||
return [options.argv.remain[1], options];
|
||||
};
|
||||
|
||||
module.exports = version;
|
||||
1284
lib/core/Manager.js
1284
lib/core/Manager.js
File diff suppressed because it is too large
Load Diff
@@ -1,304 +0,0 @@
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
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;
|
||||
this._logger = 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
|
||||
this._resolveCache = new ResolveCache(this._config);
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
PackageRepository.prototype.fetch = function(decEndpoint) {
|
||||
var logger;
|
||||
var that = this;
|
||||
var isTargetable;
|
||||
var info = {
|
||||
decEndpoint: decEndpoint
|
||||
};
|
||||
|
||||
// Create a new logger that pipes everything to ours that will be
|
||||
// used to fetch
|
||||
logger = this._logger.geminate();
|
||||
// Intercept all logs, adding additional information
|
||||
logger.intercept(function(log) {
|
||||
that._extendLog(log, info);
|
||||
});
|
||||
|
||||
return (
|
||||
this._getResolver(decEndpoint, logger)
|
||||
// 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 (that._config.force) {
|
||||
logger.action(
|
||||
'resolve',
|
||||
resolver.getSource() + '#' + resolver.getTarget()
|
||||
);
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
// Note that we use the resolver methods to query the
|
||||
// cache because transformations/normalisations can occur
|
||||
return (
|
||||
that._resolveCache
|
||||
.retrieve(resolver.getSource(), resolver.getTarget())
|
||||
// Decide if we can use the one from the resolve cache
|
||||
.spread(function(canonicalDir, pkgMeta) {
|
||||
// If there's no package in the cache
|
||||
if (!canonicalDir) {
|
||||
// And the offline flag is passed, error out
|
||||
if (that._config.offline) {
|
||||
throw createError(
|
||||
'No cached version for ' +
|
||||
resolver.getSource() +
|
||||
'#' +
|
||||
resolver.getTarget(),
|
||||
'ENOCACHE',
|
||||
{
|
||||
resolver: resolver
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, we have to resolve it
|
||||
logger.info(
|
||||
'not-cached',
|
||||
resolver.getSource() +
|
||||
(resolver.getTarget()
|
||||
? '#' + resolver.getTarget()
|
||||
: '')
|
||||
);
|
||||
logger.action(
|
||||
'resolve',
|
||||
resolver.getSource() +
|
||||
'#' +
|
||||
resolver.getTarget()
|
||||
);
|
||||
|
||||
return that._resolve(resolver, logger);
|
||||
}
|
||||
|
||||
info.canonicalDir = canonicalDir;
|
||||
info.pkgMeta = pkgMeta;
|
||||
|
||||
logger.info(
|
||||
'cached',
|
||||
resolver.getSource() +
|
||||
(pkgMeta._release
|
||||
? '#' + pkgMeta._release
|
||||
: '')
|
||||
);
|
||||
|
||||
// If offline flag is used, use directly the cached one
|
||||
if (that._config.offline) {
|
||||
return [canonicalDir, pkgMeta, isTargetable];
|
||||
}
|
||||
|
||||
// Otherwise check for new contents
|
||||
logger.action(
|
||||
'validate',
|
||||
(pkgMeta._release
|
||||
? pkgMeta._release + ' against '
|
||||
: '') +
|
||||
resolver.getSource() +
|
||||
(resolver.getTarget()
|
||||
? '#' + resolver.getTarget()
|
||||
: '')
|
||||
);
|
||||
|
||||
return resolver
|
||||
.hasNew(pkgMeta)
|
||||
.then(function(hasNew) {
|
||||
// If there are no new contents, resolve to
|
||||
// the cached one
|
||||
if (!hasNew) {
|
||||
return [
|
||||
canonicalDir,
|
||||
pkgMeta,
|
||||
isTargetable
|
||||
];
|
||||
}
|
||||
|
||||
// Otherwise resolve to the newest one
|
||||
logger.info(
|
||||
'new',
|
||||
'version for ' +
|
||||
resolver.getSource() +
|
||||
'#' +
|
||||
resolver.getTarget()
|
||||
);
|
||||
logger.action(
|
||||
'resolve',
|
||||
resolver.getSource() +
|
||||
'#' +
|
||||
resolver.getTarget()
|
||||
);
|
||||
|
||||
return that._resolve(resolver, logger);
|
||||
});
|
||||
})
|
||||
);
|
||||
})
|
||||
// If something went wrong, also extend the error
|
||||
.fail(function(err) {
|
||||
that._extendLog(err, info);
|
||||
throw err;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
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) {
|
||||
// If offline, resolve using the cached versions
|
||||
if (this._config.offline) {
|
||||
return this._resolveCache.versions(resolver.getSource());
|
||||
}
|
||||
|
||||
// Otherwise, fetch remotely
|
||||
return resolver.constructor.versions(resolver.getSource());
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.eliminate = function(pkgMeta) {
|
||||
return Q.all([
|
||||
this._resolveCache.eliminate(pkgMeta),
|
||||
Q.nfcall(
|
||||
this._registryClient.clearCache.bind(this._registryClient),
|
||||
pkgMeta.name
|
||||
)
|
||||
]);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.clear = function() {
|
||||
return Q.all([
|
||||
this._resolveCache.clear(),
|
||||
Q.nfcall(this._registryClient.clearCache.bind(this._registryClient))
|
||||
]);
|
||||
};
|
||||
|
||||
PackageRepository.prototype.reset = function() {
|
||||
this._resolveCache.reset();
|
||||
this._registryClient.resetCache();
|
||||
};
|
||||
|
||||
PackageRepository.prototype.list = function() {
|
||||
return this._resolveCache.list();
|
||||
};
|
||||
|
||||
PackageRepository.prototype.getRegistryClient = function() {
|
||||
return this._registryClient;
|
||||
};
|
||||
|
||||
PackageRepository.prototype.getResolveCache = function() {
|
||||
return this._resolveCache;
|
||||
};
|
||||
|
||||
PackageRepository.clearRuntimeCache = function() {
|
||||
ResolveCache.clearRuntimeCache();
|
||||
RegistryClient.clearRuntimeCache();
|
||||
resolverFactory.clearRuntimeCache();
|
||||
};
|
||||
|
||||
// ---------------------
|
||||
|
||||
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()
|
||||
);
|
||||
})
|
||||
// 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()];
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
PackageRepository.prototype._extendLog = function(log, info) {
|
||||
log.data = log.data || {};
|
||||
|
||||
// Store endpoint info in each log
|
||||
if (info.decEndpoint) {
|
||||
log.data.endpoint = mout.object.pick(info.decEndpoint, [
|
||||
'name',
|
||||
'source',
|
||||
'target'
|
||||
]);
|
||||
}
|
||||
|
||||
// Store the resolver info in each log
|
||||
if (info.resolver) {
|
||||
log.data.resolver = {
|
||||
name: info.resolver.getName(),
|
||||
source: info.resolver.getSource(),
|
||||
target: info.resolver.getTarget()
|
||||
};
|
||||
}
|
||||
|
||||
// Store the canonical dir and its meta in each log
|
||||
if (info.canonicalDir) {
|
||||
log.data.canonicalDir = info.canonicalDir;
|
||||
log.data.pkgMeta = info.pkgMeta;
|
||||
}
|
||||
|
||||
return log;
|
||||
};
|
||||
|
||||
module.exports = PackageRepository;
|
||||
1033
lib/core/Project.js
1033
lib/core/Project.js
File diff suppressed because it is too large
Load Diff
@@ -1,440 +0,0 @@
|
||||
var fs = require('../util/fs');
|
||||
var path = require('path');
|
||||
var mout = require('mout');
|
||||
var Q = require('q');
|
||||
var mkdirp = require('mkdirp');
|
||||
var rimraf = require('../util/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');
|
||||
|
||||
function ResolveCache(config) {
|
||||
// TODO: Make some config entries, such as:
|
||||
// - Max MB
|
||||
// - Max versions per source
|
||||
// - Max MB per source
|
||||
// - etc..
|
||||
this._config = config;
|
||||
this._dir = this._config.storage.packages;
|
||||
this._lockDir = this._config.storage.packages;
|
||||
|
||||
mkdirp.sync(this._lockDir);
|
||||
|
||||
// Cache is stored/retrieved statically to ensure singularity
|
||||
// among instances
|
||||
this._cache = this.constructor._cache.get(this._dir);
|
||||
if (!this._cache) {
|
||||
this._cache = new LRU({
|
||||
max: 100,
|
||||
maxAge: 60 * 5 * 1000 // 5 minutes
|
||||
});
|
||||
this.constructor._cache.set(this._dir, this._cache);
|
||||
}
|
||||
|
||||
// Ensure dir is created
|
||||
mkdirp.sync(this._dir);
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
ResolveCache.prototype.retrieve = function(source, target) {
|
||||
var sourceId = md5(source);
|
||||
var dir = path.join(this._dir, sourceId);
|
||||
var that = this;
|
||||
|
||||
target = target || '*';
|
||||
|
||||
return this._getVersions(sourceId)
|
||||
.spread(function(versions) {
|
||||
var suitable;
|
||||
|
||||
// If target is a semver, find a suitable version
|
||||
if (semver.validRange(target)) {
|
||||
suitable = semver.maxSatisfying(versions, target, true);
|
||||
|
||||
if (suitable) {
|
||||
return suitable;
|
||||
}
|
||||
}
|
||||
|
||||
// If target is '*' check if there's a cached '_wildcard'
|
||||
if (target === '*') {
|
||||
return mout.array.find(versions, function(version) {
|
||||
return version === '_wildcard';
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise check if there's an exact match
|
||||
return mout.array.find(versions, function(version) {
|
||||
return version === target;
|
||||
});
|
||||
})
|
||||
.then(function(version) {
|
||||
var canonicalDir;
|
||||
|
||||
if (!version) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Resolve with canonical dir and package meta
|
||||
canonicalDir = path.join(dir, encodeURIComponent(version));
|
||||
return that._readPkgMeta(canonicalDir).then(
|
||||
function(pkgMeta) {
|
||||
return [canonicalDir, pkgMeta];
|
||||
},
|
||||
function() {
|
||||
// If there was an error, invalidate the in-memory cache,
|
||||
// delete the cached package and try again
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return Q.nfcall(rimraf, canonicalDir).then(function() {
|
||||
return that.retrieve(source, target);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype.store = function(canonicalDir, pkgMeta) {
|
||||
var sourceId;
|
||||
var release;
|
||||
var dir;
|
||||
var pkgLock;
|
||||
var promise;
|
||||
var that = this;
|
||||
|
||||
promise = pkgMeta ? Q.resolve(pkgMeta) : this._readPkgMeta(canonicalDir);
|
||||
|
||||
return promise
|
||||
.then(function(pkgMeta) {
|
||||
sourceId = md5(pkgMeta._source);
|
||||
release = that._getPkgRelease(pkgMeta);
|
||||
dir = path.join(that._dir, sourceId, release);
|
||||
pkgLock = path.join(
|
||||
that._lockDir,
|
||||
sourceId + '-' + release + '.lock'
|
||||
);
|
||||
|
||||
// Check if destination directory exists to prevent issuing lock at all times
|
||||
return Q.nfcall(fs.stat, dir)
|
||||
.fail(function(err) {
|
||||
var lockParams = { wait: 250, retries: 25, stale: 60000 };
|
||||
return Q.nfcall(lockFile.lock, pkgLock, lockParams)
|
||||
.then(function() {
|
||||
// Ensure other process didn't start copying files before lock was created
|
||||
return Q.nfcall(fs.stat, dir).fail(function(err) {
|
||||
// If stat fails, it is expected to return ENOENT
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Create missing directory and copy files there
|
||||
return Q.nfcall(mkdirp, path.dirname(dir)).then(
|
||||
function() {
|
||||
return Q.nfcall(
|
||||
fs.rename,
|
||||
canonicalDir,
|
||||
dir
|
||||
).fail(function(err) {
|
||||
// If error is EXDEV it means that we are trying to rename
|
||||
// across different drives, so we copy and remove it instead
|
||||
if (err.code !== 'EXDEV') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
return copy.copyDir(
|
||||
canonicalDir,
|
||||
dir
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
.finally(function() {
|
||||
lockFile.unlockSync(pkgLock);
|
||||
});
|
||||
})
|
||||
.finally(function() {
|
||||
// Ensure no tmp dir is left on disk.
|
||||
return Q.nfcall(rimraf, canonicalDir);
|
||||
});
|
||||
})
|
||||
.then(function() {
|
||||
var versions = that._cache.get(sourceId);
|
||||
|
||||
// Add it to the in memory cache
|
||||
// and sort the versions afterwards
|
||||
if (versions && versions.indexOf(release) === -1) {
|
||||
versions.push(release);
|
||||
that._sortVersions(versions);
|
||||
}
|
||||
|
||||
// Resolve with the final location
|
||||
return dir;
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype.eliminate = function(pkgMeta) {
|
||||
var sourceId = md5(pkgMeta._source);
|
||||
var release = this._getPkgRelease(pkgMeta);
|
||||
var dir = path.join(this._dir, sourceId, release);
|
||||
var that = this;
|
||||
|
||||
return Q.nfcall(rimraf, dir).then(function() {
|
||||
var versions = that._cache.get(sourceId) || [];
|
||||
mout.array.remove(versions, release);
|
||||
|
||||
// If this was the last package in the cache,
|
||||
// delete the parent folder (source)
|
||||
// For extra security, check against the file system
|
||||
// if this was really the last package
|
||||
if (!versions.length) {
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return that._getVersions(sourceId).spread(function(versions) {
|
||||
if (!versions.length) {
|
||||
// Do not keep in-memory cache if it's completely
|
||||
// empty
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return Q.nfcall(rimraf, path.dirname(dir));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype.clear = function() {
|
||||
return Q.nfcall(rimraf, this._dir)
|
||||
.then(
|
||||
function() {
|
||||
return Q.nfcall(fs.mkdir, this._dir);
|
||||
}.bind(this)
|
||||
)
|
||||
.then(
|
||||
function() {
|
||||
this._cache.reset();
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
ResolveCache.prototype.reset = function() {
|
||||
this._cache.reset();
|
||||
return this;
|
||||
};
|
||||
|
||||
ResolveCache.prototype.versions = function(source) {
|
||||
var sourceId = md5(source);
|
||||
|
||||
return this._getVersions(sourceId).spread(function(versions) {
|
||||
return versions.filter(function(version) {
|
||||
return semver.valid(version);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype.list = function() {
|
||||
var promises;
|
||||
var dirs = [];
|
||||
var that = this;
|
||||
|
||||
// Get the list of directories
|
||||
return (
|
||||
Q.nfcall(fs.readdir, this._dir)
|
||||
.then(function(sourceIds) {
|
||||
promises = sourceIds.map(function(sourceId) {
|
||||
return Q.nfcall(
|
||||
fs.readdir,
|
||||
path.join(that._dir, sourceId)
|
||||
).then(
|
||||
function(versions) {
|
||||
versions.forEach(function(version) {
|
||||
var dir = path.join(
|
||||
that._dir,
|
||||
sourceId,
|
||||
version
|
||||
);
|
||||
dirs.push(dir);
|
||||
});
|
||||
},
|
||||
function(err) {
|
||||
// Ignore lurking files, e.g.: .DS_Store if the user
|
||||
// has navigated throughout the cache
|
||||
if (err.code === 'ENOTDIR' && err.path) {
|
||||
return Q.nfcall(rimraf, err.path);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Q.all(promises);
|
||||
})
|
||||
// Read every package meta
|
||||
.then(function() {
|
||||
promises = dirs.map(function(dir) {
|
||||
return that._readPkgMeta(dir).then(
|
||||
function(pkgMeta) {
|
||||
return {
|
||||
canonicalDir: dir,
|
||||
pkgMeta: pkgMeta
|
||||
};
|
||||
},
|
||||
function() {
|
||||
// If it fails to read, invalidate the in memory
|
||||
// cache for the source and delete the entry directory
|
||||
var sourceId = path.basename(path.dirname(dir));
|
||||
that._cache.del(sourceId);
|
||||
|
||||
return Q.nfcall(rimraf, dir);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
return Q.all(promises);
|
||||
})
|
||||
// Sort by name ASC & release ASC
|
||||
.then(function(entries) {
|
||||
// Ignore falsy entries due to errors reading
|
||||
// package metas
|
||||
entries = entries.filter(function(entry) {
|
||||
return !!entry;
|
||||
});
|
||||
|
||||
return entries.sort(function(entry1, entry2) {
|
||||
var pkgMeta1 = entry1.pkgMeta;
|
||||
var pkgMeta2 = entry2.pkgMeta;
|
||||
var comp = pkgMeta1.name.localeCompare(pkgMeta2.name);
|
||||
|
||||
// Sort by name
|
||||
if (comp) {
|
||||
return comp;
|
||||
}
|
||||
|
||||
// Sort by version
|
||||
if (pkgMeta1.version && pkgMeta2.version) {
|
||||
return semver.compare(
|
||||
pkgMeta1.version,
|
||||
pkgMeta2.version
|
||||
);
|
||||
}
|
||||
if (pkgMeta1.version) {
|
||||
return -1;
|
||||
}
|
||||
if (pkgMeta2.version) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Sort by target
|
||||
return pkgMeta1._target.localeCompare(pkgMeta2._target);
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// ------------------------
|
||||
|
||||
ResolveCache.clearRuntimeCache = function() {
|
||||
// Note that _cache refers to the static _cache variable
|
||||
// that holds other caches per dir!
|
||||
// Do not confuse it with the instance cache
|
||||
|
||||
// Clear cache of each directory
|
||||
this._cache.forEach(function(cache) {
|
||||
cache.reset();
|
||||
});
|
||||
|
||||
// Clear root cache
|
||||
this._cache.reset();
|
||||
};
|
||||
|
||||
// ------------------------
|
||||
|
||||
ResolveCache.prototype._getPkgRelease = function(pkgMeta) {
|
||||
var release =
|
||||
pkgMeta.version ||
|
||||
(pkgMeta._target === '*' ? '_wildcard' : pkgMeta._target);
|
||||
|
||||
// Encode some dangerous chars such as / and \
|
||||
release = encodeURIComponent(release);
|
||||
|
||||
return release;
|
||||
};
|
||||
|
||||
ResolveCache.prototype._readPkgMeta = function(dir) {
|
||||
var filename = path.join(dir, '.bower.json');
|
||||
|
||||
return readJson(filename).spread(function(json) {
|
||||
return json;
|
||||
});
|
||||
};
|
||||
|
||||
ResolveCache.prototype._getVersions = function(sourceId) {
|
||||
var dir;
|
||||
var versions = this._cache.get(sourceId);
|
||||
var that = this;
|
||||
|
||||
if (versions) {
|
||||
return Q.resolve([versions, true]);
|
||||
}
|
||||
|
||||
dir = path.join(this._dir, sourceId);
|
||||
return Q.nfcall(fs.readdir, dir).then(
|
||||
function(versions) {
|
||||
// Sort and cache in memory
|
||||
that._sortVersions(versions);
|
||||
versions = versions.map(decodeURIComponent);
|
||||
that._cache.set(sourceId, versions);
|
||||
return [versions, false];
|
||||
},
|
||||
function(err) {
|
||||
// If the directory does not exists, resolve
|
||||
// as an empty array
|
||||
if (err.code === 'ENOENT') {
|
||||
versions = [];
|
||||
that._cache.set(sourceId, versions);
|
||||
return [versions, false];
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
ResolveCache.prototype._sortVersions = function(versions) {
|
||||
// Sort DESC
|
||||
versions.sort(function(version1, version2) {
|
||||
var validSemver1 = semver.valid(version1);
|
||||
var validSemver2 = semver.valid(version2);
|
||||
|
||||
// If both are semvers, compare them
|
||||
if (validSemver1 && validSemver2) {
|
||||
return semver.rcompare(version1, version2);
|
||||
}
|
||||
|
||||
// If one of them are semvers, give higher priority
|
||||
if (validSemver1) {
|
||||
return -1;
|
||||
}
|
||||
if (validSemver2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Otherwise they are considered equal
|
||||
return 0;
|
||||
});
|
||||
};
|
||||
|
||||
// ------------------------
|
||||
|
||||
ResolveCache._cache = new LRU({
|
||||
max: 5,
|
||||
maxAge: 60 * 30 * 1000 // 30 minutes
|
||||
});
|
||||
|
||||
module.exports = ResolveCache;
|
||||
@@ -1,279 +0,0 @@
|
||||
var Q = require('q');
|
||||
var fs = require('../util/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, options, registryClient) {
|
||||
decEndpoint = mout.object.pick(decEndpoint, ['name', 'target', 'source']);
|
||||
|
||||
options.version = require('../version');
|
||||
|
||||
return getConstructor(decEndpoint, options, registryClient).spread(function(
|
||||
ConcreteResolver,
|
||||
decEndpoint
|
||||
) {
|
||||
return new ConcreteResolver(
|
||||
decEndpoint,
|
||||
options.config,
|
||||
options.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];
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 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 it's a GitHub repository, return the specialized resolver
|
||||
if (resolvers.GitHub.getOrgRepoPair(source)) {
|
||||
return [resolvers.GitHub, decEndpoint];
|
||||
}
|
||||
|
||||
return [resolvers.GitRemote, decEndpoint];
|
||||
}
|
||||
});
|
||||
|
||||
// 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];
|
||||
}
|
||||
});
|
||||
|
||||
// URL case
|
||||
addResolver(function() {
|
||||
if (/^https?:\/\//i.exec(source)) {
|
||||
return [resolvers.Url, decEndpoint];
|
||||
}
|
||||
});
|
||||
|
||||
// If source is ./ or ../ or an absolute path
|
||||
|
||||
addResolver(function() {
|
||||
var absolutePath = path.resolve(config.cwd, source);
|
||||
|
||||
if (
|
||||
/^\.\.?[\/\\]/.test(source) ||
|
||||
/^~\//.test(source) ||
|
||||
path.normalize(source).replace(/[\/\\]+$/, '') === absolutePath
|
||||
) {
|
||||
return (
|
||||
Q.nfcall(fs.stat, path.join(absolutePath, '.git'))
|
||||
.then(function(stats) {
|
||||
decEndpoint.source = absolutePath;
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
return Q.resolve([resolvers.GitFs, decEndpoint]);
|
||||
}
|
||||
|
||||
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]);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Check if is a shorthand and expand it
|
||||
addResolver(function() {
|
||||
// Check if the shorthandResolver is falsy
|
||||
if (!config.shorthandResolver) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip ssh and/or URL with auth
|
||||
if (/[:@]/.test(source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure exactly only one "/"
|
||||
var parts = source.split('/');
|
||||
if (parts.length === 2) {
|
||||
decEndpoint.source = mout.string.interpolate(
|
||||
config.shorthandResolver,
|
||||
{
|
||||
shorthand: source,
|
||||
owner: parts[0],
|
||||
package: parts[1]
|
||||
}
|
||||
);
|
||||
|
||||
return getConstructor(decEndpoint, options, registryClient);
|
||||
}
|
||||
});
|
||||
|
||||
// As last resort, we try the registry
|
||||
addResolver(function() {
|
||||
if (!registryClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
return Q.nfcall(
|
||||
registryClient.lookup.bind(registryClient),
|
||||
source
|
||||
).then(function(entry) {
|
||||
if (!entry) {
|
||||
throw createError(
|
||||
'Package ' + source + ' not found',
|
||||
'ENOTFOUND'
|
||||
);
|
||||
}
|
||||
|
||||
decEndpoint.registry = true;
|
||||
|
||||
if (!decEndpoint.name) {
|
||||
decEndpoint.name = decEndpoint.source;
|
||||
}
|
||||
|
||||
decEndpoint.source = entry.url;
|
||||
|
||||
return getConstructor(decEndpoint, options);
|
||||
});
|
||||
});
|
||||
|
||||
addResolver(function() {
|
||||
throw createError(
|
||||
'Could not find appropriate resolver for ' + source,
|
||||
'ENORESOLVER'
|
||||
);
|
||||
});
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function clearRuntimeCache() {
|
||||
mout.object.values(resolvers).forEach(function(ConcreteResolver) {
|
||||
ConcreteResolver.clearRuntimeCache();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = createInstance;
|
||||
module.exports.getConstructor = getConstructor;
|
||||
module.exports.clearRuntimeCache = clearRuntimeCache;
|
||||
@@ -1,102 +0,0 @@
|
||||
var util = require('util');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var path = require('path');
|
||||
var GitResolver = require('./GitResolver');
|
||||
var copy = require('../../util/copy');
|
||||
var cmd = require('../../util/cmd');
|
||||
|
||||
function GitFsResolver(decEndpoint, config, logger) {
|
||||
GitResolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
// Ensure absolute path
|
||||
this._source = path.resolve(this._config.cwd, this._source);
|
||||
}
|
||||
|
||||
util.inherits(GitFsResolver, GitResolver);
|
||||
mout.object.mixIn(GitFsResolver, GitResolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
// Override the checkout function to work with the local copy
|
||||
GitFsResolver.prototype._checkout = function() {
|
||||
var resolution = this._resolution;
|
||||
|
||||
// The checkout process could be similar to the GitRemoteResolver by prepending file:// to the source
|
||||
// But from my performance measures, it's faster to copy the folder and just checkout in there
|
||||
this._logger.action(
|
||||
'checkout',
|
||||
resolution.tag || resolution.branch || resolution.commit,
|
||||
{
|
||||
resolution: resolution,
|
||||
to: this._tempDir
|
||||
}
|
||||
);
|
||||
|
||||
// Copy files to the temporary directory first
|
||||
return (
|
||||
this._copy()
|
||||
.then(
|
||||
cmd.bind(
|
||||
cmd,
|
||||
'git',
|
||||
[
|
||||
'checkout',
|
||||
'-f',
|
||||
resolution.tag || resolution.branch || resolution.commit
|
||||
],
|
||||
{ cwd: this._tempDir }
|
||||
)
|
||||
)
|
||||
// Cleanup unstaged files
|
||||
.then(
|
||||
cmd.bind(cmd, 'git', ['clean', '-f', '-d'], {
|
||||
cwd: this._tempDir
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
GitFsResolver.prototype._copy = function() {
|
||||
return copy.copyDir(this._source, this._tempDir);
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Grab refs locally
|
||||
GitFsResolver.refs = function(source) {
|
||||
var value;
|
||||
|
||||
// TODO: Normalize source because of the various available protocols?
|
||||
value = this._cache.refs.get(source);
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('git', ['show-ref', '--tags', '--heads'], {
|
||||
cwd: source
|
||||
}).spread(
|
||||
function(stdout) {
|
||||
var refs;
|
||||
|
||||
refs = stdout
|
||||
.toString()
|
||||
.trim() // Trim trailing and leading spaces
|
||||
.replace(/[\t ]+/g, ' ') // Standardize spaces (some git versions make tabs, other spaces)
|
||||
.split(/[\r\n]+/); // Split lines into an array
|
||||
|
||||
// Update the refs with the actual refs
|
||||
this._cache.refs.set(source, refs);
|
||||
|
||||
return refs;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.refs.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
module.exports = GitFsResolver;
|
||||
@@ -1,191 +0,0 @@
|
||||
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');
|
||||
var createError = require('../../util/createError');
|
||||
|
||||
function GitHubResolver(decEndpoint, config, logger) {
|
||||
var pair;
|
||||
|
||||
GitRemoteResolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
// Grab the org/repo
|
||||
// /xxxxx/yyyyy.git or :xxxxx/yyyyy.git (.git is optional)
|
||||
pair = GitHubResolver.getOrgRepoPair(this._source);
|
||||
if (!pair) {
|
||||
throw createError('Invalid GitHub URL', 'EINVEND', {
|
||||
details: this._source + ' does not seem to be a valid GitHub URL'
|
||||
});
|
||||
}
|
||||
|
||||
this._org = pair.org;
|
||||
this._repo = pair.repo;
|
||||
|
||||
// Ensure trailing for all protocols
|
||||
if (!mout.string.endsWith(this._source, '.git')) {
|
||||
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);
|
||||
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';
|
||||
|
||||
var file = path.join(this._tempDir, 'archive.tar.gz');
|
||||
var reqHeaders = {};
|
||||
var that = this;
|
||||
|
||||
if (this._config.userAgent) {
|
||||
reqHeaders['User-Agent'] = this._config.userAgent;
|
||||
}
|
||||
|
||||
this._logger.action('download', tarballUrl, {
|
||||
url: that._source,
|
||||
to: file
|
||||
});
|
||||
|
||||
// Download tarball
|
||||
return download(tarballUrl, file, {
|
||||
ca: this._config.ca.default,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
headers: reqHeaders
|
||||
})
|
||||
.progress(function(state) {
|
||||
// Retry?
|
||||
if (state.retry) {
|
||||
msg =
|
||||
'Download of ' +
|
||||
tarballUrl +
|
||||
' failed with ' +
|
||||
state.error.code +
|
||||
', ';
|
||||
msg += 'retrying in ' + (state.delay / 1000).toFixed(1) + 's';
|
||||
that._logger.debug('error', state.error.message, {
|
||||
error: state.error
|
||||
});
|
||||
return that._logger.warn('retry', msg);
|
||||
}
|
||||
|
||||
// Progress
|
||||
msg =
|
||||
'received ' + (state.received / 1024 / 1024).toFixed(1) + 'MB';
|
||||
if (state.total) {
|
||||
msg +=
|
||||
' of ' +
|
||||
(state.total / 1024 / 1024).toFixed(1) +
|
||||
'MB downloaded, ';
|
||||
msg += state.percent + '%';
|
||||
}
|
||||
that._logger.info('progress', msg);
|
||||
})
|
||||
.then(
|
||||
function() {
|
||||
// Extract archive
|
||||
that._logger.action('extract', path.basename(file), {
|
||||
archive: file,
|
||||
to: that._tempDir
|
||||
});
|
||||
|
||||
return (
|
||||
extract(file, that._tempDir)
|
||||
// Fallback to standard git clone if extraction failed
|
||||
.fail(function(err) {
|
||||
msg =
|
||||
'Decompression of ' +
|
||||
path.basename(file) +
|
||||
' failed' +
|
||||
(err.code ? ' with ' + err.code : '') +
|
||||
', ';
|
||||
msg += 'trying with git..';
|
||||
that._logger.debug('error', err.message, {
|
||||
error: err
|
||||
});
|
||||
that._logger.warn('retry', msg);
|
||||
|
||||
return that
|
||||
._cleanTempDir()
|
||||
.then(
|
||||
GitRemoteResolver.prototype._checkout.bind(
|
||||
that
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
// Fallback to standard git clone if download failed
|
||||
},
|
||||
function(err) {
|
||||
msg =
|
||||
'Download of ' +
|
||||
tarballUrl +
|
||||
' failed' +
|
||||
(err.code ? ' with ' + err.code : '') +
|
||||
', ';
|
||||
msg += 'trying with git..';
|
||||
that._logger.debug('error', err.message, { error: err });
|
||||
that._logger.warn('retry', msg);
|
||||
|
||||
return that
|
||||
._cleanTempDir()
|
||||
.then(GitRemoteResolver.prototype._checkout.bind(that));
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
GitHubResolver.prototype._savePkgMeta = function(meta) {
|
||||
// Set homepage if not defined
|
||||
if (!meta.homepage) {
|
||||
meta.homepage = 'https://github.com/' + this._org + '/' + this._repo;
|
||||
}
|
||||
|
||||
return GitRemoteResolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
// ----------------
|
||||
|
||||
GitHubResolver.getOrgRepoPair = function(url) {
|
||||
var match;
|
||||
|
||||
match = url.match(
|
||||
/(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i
|
||||
);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
org: match[1],
|
||||
repo: match[2]
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = GitHubResolver;
|
||||
@@ -1,434 +0,0 @@
|
||||
var util = require('util');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var rimraf = require('../../util/rimraf');
|
||||
var mkdirp = require('mkdirp');
|
||||
var which = require('../../util/which');
|
||||
var LRU = require('lru-cache');
|
||||
var mout = require('mout');
|
||||
var Resolver = require('./Resolver');
|
||||
var semver = require('../../util/semver');
|
||||
var createError = require('../../util/createError');
|
||||
|
||||
var hasGit;
|
||||
|
||||
// Check if git is installed
|
||||
try {
|
||||
which.sync('git');
|
||||
hasGit = true;
|
||||
} catch (ex) {
|
||||
hasGit = false;
|
||||
}
|
||||
|
||||
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) {
|
||||
throw createError('git is not installed or not in the PATH', 'ENOGIT');
|
||||
}
|
||||
}
|
||||
|
||||
util.inherits(GitResolver, Resolver);
|
||||
mout.object.mixIn(GitResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
GitResolver.prototype._hasNew = function(pkgMeta) {
|
||||
var oldResolution = pkgMeta._resolution || {};
|
||||
|
||||
return this._findResolution().then(function(resolution) {
|
||||
// Check if resolution types are different
|
||||
if (oldResolution.type !== resolution.type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If resolved to a version, there is new content if the tags are not equal
|
||||
if (
|
||||
resolution.type === 'version' &&
|
||||
semver.neq(resolution.tag, oldResolution.tag)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// As last check, we compare both commit hashes
|
||||
return resolution.commit !== oldResolution.commit;
|
||||
});
|
||||
};
|
||||
|
||||
GitResolver.prototype._resolve = function() {
|
||||
var that = this;
|
||||
|
||||
return this._findResolution().then(function() {
|
||||
return (
|
||||
that
|
||||
._checkout()
|
||||
// Always run cleanup after checkout to ensure that .git 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();
|
||||
})
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
// Abstract functions that should be implemented by concrete git resolvers
|
||||
GitResolver.prototype._checkout = function() {
|
||||
throw new Error('_checkout not implemented');
|
||||
};
|
||||
|
||||
GitResolver.refs = function(source) {
|
||||
throw new Error('refs not implemented');
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
GitResolver.prototype._findResolution = function(target) {
|
||||
var err;
|
||||
var self = this.constructor;
|
||||
var that = this;
|
||||
|
||||
target = target || this._target || '*';
|
||||
|
||||
// Target is a commit, so it's a stale target (not a moving target)
|
||||
// There's nothing to do in this case
|
||||
if (/^[a-f0-9]{40}$/.test(target)) {
|
||||
this._resolution = { type: 'commit', commit: target };
|
||||
return Q.resolve(this._resolution);
|
||||
}
|
||||
|
||||
// Target is a range/version
|
||||
if (semver.validRange(target)) {
|
||||
return self.versions(this._source, true).then(function(versions) {
|
||||
var versionsArr, version, index;
|
||||
|
||||
// If there are no tags and target is *,
|
||||
// fallback to the latest commit on master
|
||||
if (!versions.length && target === '*') {
|
||||
return that._findResolution('master');
|
||||
}
|
||||
|
||||
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) {
|
||||
version = versions[index];
|
||||
return (that._resolution = {
|
||||
type: 'version',
|
||||
tag: version.tag,
|
||||
commit: version.commit
|
||||
});
|
||||
}
|
||||
|
||||
// Check if there's an exact branch/tag with this name as last resort
|
||||
return Q.all([
|
||||
self.branches(that._source),
|
||||
self.tags(that._source)
|
||||
]).spread(function(branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'tag',
|
||||
tag: target,
|
||||
commit: tags[target]
|
||||
});
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'branch',
|
||||
branch: target,
|
||||
commit: branches[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(', ')
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise, target is either a tag or a branch
|
||||
return Q.all([self.branches(that._source), self.tags(that._source)]).spread(
|
||||
function(branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'tag',
|
||||
tag: target,
|
||||
commit: tags[target]
|
||||
});
|
||||
}
|
||||
if (mout.object.hasOwn(branches, 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);
|
||||
|
||||
err = createError(
|
||||
'Tag/branch ' + target + ' does not exist',
|
||||
'ENORESTARGET'
|
||||
);
|
||||
err.details = !tags.length
|
||||
? 'No tags found in ' + that._source
|
||||
: 'Available tags: ' + tags.join(', ');
|
||||
err.details += '\n';
|
||||
err.details += !branches.length
|
||||
? 'No branches found in ' + that._source
|
||||
: 'Available branches: ' + branches.join(', ');
|
||||
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
GitResolver.prototype._cleanup = function() {
|
||||
var gitFolder = path.join(this._tempDir, '.git');
|
||||
|
||||
return Q.nfcall(rimraf, gitFolder);
|
||||
};
|
||||
|
||||
GitResolver.prototype._savePkgMeta = function(meta) {
|
||||
var version;
|
||||
|
||||
if (this._resolution.type === 'version') {
|
||||
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)
|
||||
) {
|
||||
this._logger.warn(
|
||||
'mismatch',
|
||||
'Version declared in the json (' +
|
||||
meta.version +
|
||||
') is different than the resolved one (' +
|
||||
version +
|
||||
')',
|
||||
{
|
||||
resolution: this._resolution,
|
||||
pkgMeta: meta
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure package meta version is the same as the resolution
|
||||
meta.version = version;
|
||||
} else {
|
||||
// If resolved to a target that is not a version,
|
||||
// remove the version from the meta
|
||||
delete meta.version;
|
||||
}
|
||||
|
||||
// Save version/tag/commit in the release
|
||||
// Note that we can't store branches because _release is supposed to be
|
||||
// an unique id of this ref.
|
||||
meta._release =
|
||||
version ||
|
||||
this._resolution.tag ||
|
||||
this._resolution.commit.substr(0, 10);
|
||||
|
||||
// Save resolution to be used in hasNew later
|
||||
meta._resolution = this._resolution;
|
||||
|
||||
return Resolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
GitResolver.versions = function(source, extra) {
|
||||
var value = this._cache.versions.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value).then(
|
||||
function() {
|
||||
var versions = this._cache.versions.get(source);
|
||||
|
||||
// If no extra information was requested,
|
||||
// resolve simply with the versions
|
||||
if (!extra) {
|
||||
versions = versions.map(function(version) {
|
||||
return version.version;
|
||||
});
|
||||
}
|
||||
|
||||
return versions;
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
value = this.tags(source).then(
|
||||
function(tags) {
|
||||
var tag;
|
||||
var version;
|
||||
var versions = [];
|
||||
|
||||
// For each tag
|
||||
for (tag in tags) {
|
||||
version = semver.clean(tag);
|
||||
if (version) {
|
||||
versions.push({
|
||||
version: version,
|
||||
tag: tag,
|
||||
commit: tags[tag]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort them by DESC order
|
||||
versions.sort(function(a, b) {
|
||||
return semver.rcompare(a.version, b.version);
|
||||
});
|
||||
|
||||
this._cache.versions.set(source, versions);
|
||||
|
||||
// Call the function again to keep it DRY
|
||||
return this.versions(source, extra);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.versions.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
GitResolver.tags = function(source) {
|
||||
var value = this._cache.tags.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = this.refs(source).then(
|
||||
function(refs) {
|
||||
var tags = {};
|
||||
|
||||
// For each line in the refs, match only the tags
|
||||
refs.forEach(function(line) {
|
||||
var match = line.match(/^([a-f0-9]{40})\s+refs\/tags\/(\S+)/);
|
||||
|
||||
if (match && !mout.string.endsWith(match[2], '^{}')) {
|
||||
tags[match[2]] = match[1];
|
||||
}
|
||||
});
|
||||
|
||||
this._cache.tags.set(source, tags);
|
||||
|
||||
return tags;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.tags.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
GitResolver.branches = function(source) {
|
||||
var value = this._cache.branches.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = this.refs(source).then(
|
||||
function(refs) {
|
||||
var branches = {};
|
||||
|
||||
// For each line in the refs, extract only the heads
|
||||
// Organize them in an object where keys are branches and values
|
||||
// the commit hashes
|
||||
refs.forEach(function(line) {
|
||||
var match = line.match(/^([a-f0-9]{40})\s+refs\/heads\/(\S+)/);
|
||||
|
||||
if (match) {
|
||||
branches[match[2]] = match[1];
|
||||
}
|
||||
});
|
||||
|
||||
this._cache.branches.set(source, branches);
|
||||
|
||||
return branches;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.branches.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
GitResolver.clearRuntimeCache = function() {
|
||||
// Reset cache for branches, tags, etc
|
||||
mout.object.forOwn(GitResolver._cache, function(lru) {
|
||||
lru.reset();
|
||||
});
|
||||
};
|
||||
|
||||
GitResolver._cache = {
|
||||
branches: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
tags: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
versions: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
refs: new LRU({ max: 50, maxAge: 5 * 60 * 1000 })
|
||||
};
|
||||
|
||||
module.exports = GitResolver;
|
||||
@@ -1,491 +0,0 @@
|
||||
var util = require('util');
|
||||
var Q = require('q');
|
||||
var which = require('../../util/which');
|
||||
var LRU = require('lru-cache');
|
||||
var mout = require('mout');
|
||||
var Resolver = require('./Resolver');
|
||||
var semver = require('../../util/semver');
|
||||
var createError = require('../../util/createError');
|
||||
var cmd = require('../../util/cmd');
|
||||
|
||||
var hasSvn;
|
||||
|
||||
// Check if svn is installed
|
||||
try {
|
||||
which.sync('svn');
|
||||
hasSvn = true;
|
||||
} catch (ex) {
|
||||
hasSvn = false;
|
||||
}
|
||||
|
||||
function SvnResolver(decEndpoint, config, logger) {
|
||||
Resolver.call(this, decEndpoint, config, logger);
|
||||
|
||||
if (!hasSvn) {
|
||||
throw createError('svn is not installed or not in the PATH', 'ENOSVN');
|
||||
}
|
||||
}
|
||||
|
||||
util.inherits(SvnResolver, Resolver);
|
||||
mout.object.mixIn(SvnResolver, Resolver);
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.getSource = function(source) {
|
||||
var uri = this._source || source;
|
||||
|
||||
return uri
|
||||
.replace(/^svn\+(https?|file):\/\//i, '$1://') // Change svn+http or svn+https or svn+file to http(s), file respectively
|
||||
.replace('svn://', 'http://') // Change svn to http
|
||||
.replace(/\/+$/, ''); // Remove trailing slashes
|
||||
};
|
||||
|
||||
SvnResolver.prototype._hasNew = function(pkgMeta) {
|
||||
var oldResolution = pkgMeta._resolution || {};
|
||||
|
||||
return this._findResolution().then(function(resolution) {
|
||||
// Check if resolution types are different
|
||||
if (oldResolution.type !== resolution.type) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If resolved to a version, there is new content if the tags are not equal
|
||||
if (
|
||||
resolution.type === 'version' &&
|
||||
semver.neq(resolution.tag, oldResolution.tag)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// As last check, we compare both commit hashes
|
||||
return resolution.commit !== oldResolution.commit;
|
||||
});
|
||||
};
|
||||
|
||||
SvnResolver.prototype._resolve = function() {
|
||||
var that = this;
|
||||
|
||||
return this._findResolution().then(function() {
|
||||
return that._export();
|
||||
});
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.prototype._export = function() {
|
||||
var promise;
|
||||
var timer;
|
||||
var reporter;
|
||||
var that = this;
|
||||
var resolution = this._resolution;
|
||||
|
||||
this.source = SvnResolver.getSource(this._source);
|
||||
|
||||
this._logger.action(
|
||||
'export',
|
||||
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
|
||||
]);
|
||||
} else if (resolution.type === 'branch' && resolution.branch === 'trunk') {
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/trunk',
|
||||
this._tempDir
|
||||
]);
|
||||
} else if (resolution.type === 'branch') {
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/branches/' + resolution.branch,
|
||||
this._tempDir
|
||||
]);
|
||||
} else {
|
||||
promise = cmd('svn', [
|
||||
'export',
|
||||
'--force',
|
||||
'--non-interactive',
|
||||
this._source + '/tags/' + resolution.tag,
|
||||
this._tempDir
|
||||
]);
|
||||
}
|
||||
|
||||
// Throttle the progress reporter to 1 time each sec
|
||||
reporter = mout.fn.throttle(function(data) {
|
||||
var lines;
|
||||
|
||||
lines = data.split(/[\r\n]+/);
|
||||
lines.forEach(function(line) {
|
||||
if (/\d{1,3}\%/.test(line)) {
|
||||
// TODO: There are some strange chars that appear once in a while (\u001b[K)
|
||||
// Trim also those?
|
||||
that._logger.info('progress', line.trim());
|
||||
}
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
// Start reporting progress after a few seconds
|
||||
timer = setTimeout(function() {
|
||||
promise.progress(reporter);
|
||||
}, 8000);
|
||||
|
||||
return (
|
||||
promise
|
||||
// Add additional proxy information to the error if necessary
|
||||
.fail(function(err) {
|
||||
throw err;
|
||||
})
|
||||
// Clear timer at the end
|
||||
.fin(function() {
|
||||
clearTimeout(timer);
|
||||
reporter.cancel();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// -----------------
|
||||
|
||||
SvnResolver.prototype._findResolution = function(target) {
|
||||
var err;
|
||||
var self = this.constructor;
|
||||
var that = this;
|
||||
|
||||
target = target || this._target || '*';
|
||||
|
||||
this._source = SvnResolver.getSource(this._source);
|
||||
|
||||
// Target is a revision, so it's a stale target (not a moving target)
|
||||
// There's nothing to do in this case
|
||||
if (/^r\d+/.test(target)) {
|
||||
target = target.split('r');
|
||||
|
||||
this._resolution = { type: 'commit', commit: target[1] };
|
||||
return Q.resolve(this._resolution);
|
||||
}
|
||||
|
||||
// Target is a range/version
|
||||
if (semver.validRange(target)) {
|
||||
return self.versions(this._source, true).then(function(versions) {
|
||||
var versionsArr, version, index;
|
||||
|
||||
versionsArr = versions.map(function(obj) {
|
||||
return obj.version;
|
||||
});
|
||||
|
||||
// If there are no tags and target is *,
|
||||
// fallback to the latest commit on trunk
|
||||
if (!versions.length && target === '*') {
|
||||
return that._findResolution('trunk');
|
||||
}
|
||||
|
||||
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) {
|
||||
version = versions[index];
|
||||
return (that._resolution = {
|
||||
type: 'version',
|
||||
tag: version.tag,
|
||||
commit: version.commit
|
||||
});
|
||||
}
|
||||
|
||||
// Check if there's an exact branch/tag with this name as last resort
|
||||
return Q.all([
|
||||
self.branches(that._source),
|
||||
self.tags(that._source)
|
||||
]).spread(function(branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'tag',
|
||||
tag: target,
|
||||
commit: tags[target]
|
||||
});
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'branch',
|
||||
branch: target,
|
||||
commit: branches[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(', ')
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise, target is either a tag or a branch
|
||||
return Q.all([self.branches(that._source), self.tags(that._source)]).spread(
|
||||
function(branches, tags) {
|
||||
// Use hasOwn because a branch/tag could have a name like "hasOwnProperty"
|
||||
if (mout.object.hasOwn(tags, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'tag',
|
||||
tag: target,
|
||||
commit: tags[target]
|
||||
});
|
||||
}
|
||||
if (mout.object.hasOwn(branches, target)) {
|
||||
return (that._resolution = {
|
||||
type: 'branch',
|
||||
branch: target,
|
||||
commit: branches[target]
|
||||
});
|
||||
}
|
||||
|
||||
branches = Object.keys(branches);
|
||||
tags = Object.keys(tags);
|
||||
|
||||
err = createError(
|
||||
'target ' + target + ' does not exist',
|
||||
'ENORESTARGET'
|
||||
);
|
||||
err.details = !tags.length
|
||||
? 'No tags found in ' + that._source
|
||||
: 'Available tags: ' + tags.join(', ');
|
||||
err.details += '\n';
|
||||
err.details += !branches.length
|
||||
? 'No branches found in ' + that._source
|
||||
: 'Available branches: ' + branches.join(', ');
|
||||
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
SvnResolver.prototype._savePkgMeta = function(meta) {
|
||||
var version;
|
||||
|
||||
if (this._resolution.type === 'version') {
|
||||
version = semver.clean(this._resolution.tag);
|
||||
|
||||
// Warn if the package meta version is different than the resolved one
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure package meta version is the same as the resolution
|
||||
meta.version = version;
|
||||
} else {
|
||||
// If resolved to a target that is not a version,
|
||||
// remove the version from the meta
|
||||
delete meta.version;
|
||||
}
|
||||
|
||||
// Save version/tag/commit in the release
|
||||
// Note that we can't store branches because _release is supposed to be
|
||||
// an unique id of this ref.
|
||||
meta._release = version || this._resolution.tag || this._resolution.commit;
|
||||
|
||||
// Save resolution to be used in hasNew later
|
||||
meta._resolution = this._resolution;
|
||||
|
||||
return Resolver.prototype._savePkgMeta.call(this, meta);
|
||||
};
|
||||
|
||||
// ------------------------------
|
||||
|
||||
SvnResolver.versions = function(source, extra) {
|
||||
source = SvnResolver.getSource(source);
|
||||
|
||||
var value = this._cache.versions.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value).then(
|
||||
function() {
|
||||
var versions = this._cache.versions.get(source);
|
||||
|
||||
// If no extra information was requested,
|
||||
// resolve simply with the versions
|
||||
if (!extra) {
|
||||
versions = versions.map(function(version) {
|
||||
return version.version;
|
||||
});
|
||||
}
|
||||
|
||||
return versions;
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
value = this.tags(source).then(
|
||||
function(tags) {
|
||||
var tag;
|
||||
var version;
|
||||
var versions = [];
|
||||
|
||||
// For each tag
|
||||
for (tag in tags) {
|
||||
version = semver.clean(tag);
|
||||
if (version) {
|
||||
versions.push({
|
||||
version: version,
|
||||
tag: tag,
|
||||
commit: tags[tag]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort them by DESC order
|
||||
versions.sort(function(a, b) {
|
||||
return semver.rcompare(a.version, b.version);
|
||||
});
|
||||
|
||||
this._cache.versions.set(source, versions);
|
||||
|
||||
// Call the function again to keep it DRY
|
||||
return this.versions(source, extra);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.versions.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.tags = function(source) {
|
||||
source = SvnResolver.getSource(source);
|
||||
|
||||
var value = this._cache.tags.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('svn', [
|
||||
'list',
|
||||
source + '/tags',
|
||||
'--verbose',
|
||||
'--non-interactive'
|
||||
]).spread(
|
||||
function(stout) {
|
||||
var tags = SvnResolver.parseSubversionListOutput(stout.toString());
|
||||
|
||||
this._cache.tags.set(source, tags);
|
||||
return tags;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.tags.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.branches = function(source) {
|
||||
source = SvnResolver.getSource(source);
|
||||
|
||||
var value = this._cache.branches.get(source);
|
||||
|
||||
if (value) {
|
||||
return Q.resolve(value);
|
||||
}
|
||||
|
||||
value = cmd('svn', [
|
||||
'list',
|
||||
source + '/branches',
|
||||
'--verbose',
|
||||
'--non-interactive'
|
||||
]).spread(
|
||||
function(stout) {
|
||||
var branches = SvnResolver.parseSubversionListOutput(
|
||||
stout.toString()
|
||||
);
|
||||
|
||||
// trunk is a branch!
|
||||
branches.trunk = '*';
|
||||
|
||||
this._cache.branches.set(source, branches);
|
||||
return branches;
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// Store the promise to be reused until it resolves
|
||||
// to a specific value
|
||||
this._cache.branches.set(source, value);
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
SvnResolver.parseSubversionListOutput = function(stout) {
|
||||
var entries = {};
|
||||
var lines = stout.trim().split(/[\r\n]+/);
|
||||
|
||||
// For each line in the refs, match only the branches
|
||||
lines.forEach(function(line) {
|
||||
var match = line.match(/\s+([0-9]+)\s.+\s([\w.$-]+)\//i);
|
||||
|
||||
if (match && match[2] !== '.') {
|
||||
entries[match[2]] = match[1];
|
||||
}
|
||||
});
|
||||
|
||||
return entries;
|
||||
};
|
||||
|
||||
SvnResolver.clearRuntimeCache = function() {
|
||||
// Reset cache for branches, tags, etc
|
||||
mout.object.forOwn(SvnResolver._cache, function(lru) {
|
||||
lru.reset();
|
||||
});
|
||||
};
|
||||
|
||||
SvnResolver._cache = {
|
||||
branches: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
tags: new LRU({ max: 50, maxAge: 5 * 60 * 1000 }),
|
||||
versions: new LRU({ max: 50, maxAge: 5 * 60 * 1000 })
|
||||
};
|
||||
|
||||
module.exports = SvnResolver;
|
||||
@@ -1,385 +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 whether 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;
|
||||
@@ -1,13 +0,0 @@
|
||||
var chalk = require('chalk');
|
||||
|
||||
var templateColors = ['yellow', 'green', 'cyan', 'red', 'white', 'magenta'];
|
||||
|
||||
function colors(Handlebars) {
|
||||
templateColors.forEach(function(color) {
|
||||
Handlebars.registerHelper(color, function(context) {
|
||||
return chalk[color](context.fn(this));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = colors;
|
||||
@@ -1,21 +0,0 @@
|
||||
var childProcess = require('child_process');
|
||||
var which = require('./which');
|
||||
|
||||
function execFile(cmd, args, opt, cb) {
|
||||
try {
|
||||
cmd = which.sync(cmd);
|
||||
} catch (e) {
|
||||
cb(e);
|
||||
}
|
||||
return childProcess.execFile(cmd, args, opt, cb);
|
||||
}
|
||||
|
||||
function spawn(cmd, args, opt) {
|
||||
cmd = which.sync(cmd);
|
||||
return childProcess.spawn(cmd, args, opt);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
execFile: execFile,
|
||||
spawn: spawn
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
var fs = require('./fs');
|
||||
var path = require('path');
|
||||
var Q = require('q');
|
||||
var mkdirp = require('mkdirp');
|
||||
var createError = require('./createError');
|
||||
|
||||
var isWin = process.platform === 'win32';
|
||||
|
||||
function createLink(src, dst, type) {
|
||||
var dstDir = path.dirname(dst);
|
||||
|
||||
// Create directory
|
||||
return (
|
||||
Q.nfcall(mkdirp, dstDir)
|
||||
// Check if source exists
|
||||
.then(function() {
|
||||
return Q.nfcall(fs.stat, src).fail(function(error) {
|
||||
if (error.code === 'ENOENT') {
|
||||
throw createError(
|
||||
'Failed to create link to ' + path.basename(src),
|
||||
'ENOENT',
|
||||
{
|
||||
details:
|
||||
src +
|
||||
' does not exist or points to a non-existent file'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
})
|
||||
// Create symlink
|
||||
.then(function(stat) {
|
||||
type = type || (stat.isDirectory() ? 'dir' : 'file');
|
||||
|
||||
return Q.nfcall(fs.symlink, src, dst, type).fail(function(err) {
|
||||
if (!isWin || err.code !== 'EPERM') {
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Try with type "junction" on Windows
|
||||
// Junctions behave equally to true symlinks and can be created in
|
||||
// non elevated terminal (well, not always..)
|
||||
return Q.nfcall(fs.symlink, src, dst, 'junction').fail(
|
||||
function(err) {
|
||||
throw createError(
|
||||
'Unable to create link to ' +
|
||||
path.basename(src),
|
||||
err.code,
|
||||
{
|
||||
details:
|
||||
err.message.trim() +
|
||||
'\n\nTry running this command in an elevated terminal (run as root/administrator).'
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = createLink;
|
||||
@@ -1,153 +0,0 @@
|
||||
var progress = require('request-progress');
|
||||
var request = require('request');
|
||||
var Q = require('q');
|
||||
var mout = require('mout');
|
||||
var retry = require('retry');
|
||||
var createError = require('./createError');
|
||||
var createWriteStream = require('fs-write-stream-atomic');
|
||||
var destroy = require('destroy');
|
||||
|
||||
var errorCodes = [
|
||||
'EADDRINFO',
|
||||
'ETIMEDOUT',
|
||||
'ECONNRESET',
|
||||
'ESOCKETTIMEDOUT',
|
||||
'ENOTFOUND'
|
||||
];
|
||||
|
||||
function download(url, file, options) {
|
||||
var operation;
|
||||
var deferred = Q.defer();
|
||||
var progressDelay = 8000;
|
||||
|
||||
options = mout.object.mixIn(
|
||||
{
|
||||
retries: 5,
|
||||
factor: 2,
|
||||
minTimeout: 1000,
|
||||
maxTimeout: 35000,
|
||||
randomize: true,
|
||||
progressDelay: progressDelay,
|
||||
gzip: 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);
|
||||
})
|
||||
.progress(function(status) {
|
||||
deferred.notify(status);
|
||||
})
|
||||
.fail(function(error) {
|
||||
// Save timeout before retrying to report
|
||||
var timeout = operation._timeouts[0];
|
||||
|
||||
// Reject if error is not a network error
|
||||
if (errorCodes.indexOf(error.code) === -1) {
|
||||
return deferred.reject(error);
|
||||
}
|
||||
|
||||
// Next attempt will start reporting download progress immediately
|
||||
progressDelay = 0;
|
||||
|
||||
// This will schedule next retry or return false
|
||||
if (operation.retry(error)) {
|
||||
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);
|
||||
});
|
||||
|
||||
writeStream.on('finish', function() {
|
||||
if (!errored) {
|
||||
destroy(req);
|
||||
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;
|
||||
}
|
||||
|
||||
module.exports = download;
|
||||
@@ -1,278 +0,0 @@
|
||||
var path = require('path');
|
||||
var fs = require('./fs');
|
||||
var zlib = require('zlib');
|
||||
var DecompressZip = require('decompress-zip');
|
||||
var tar = require('tar-fs');
|
||||
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
|
||||
zlib.Z_DEFAULT_CHUNK = 1024 * 8;
|
||||
|
||||
var extractors;
|
||||
var extractorTypes;
|
||||
|
||||
extractors = {
|
||||
'.zip': extractZip,
|
||||
'.tar': extractTar,
|
||||
'.tar.gz': extractTarGz,
|
||||
'.tgz': extractTarGz,
|
||||
'.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
|
||||
};
|
||||
|
||||
extractorTypes = Object.keys(extractors);
|
||||
|
||||
function extractZip(archive, dst) {
|
||||
var deferred = Q.defer();
|
||||
|
||||
new DecompressZip(archive)
|
||||
.on('error', deferred.reject)
|
||||
.on('extract', deferred.resolve.bind(deferred, dst))
|
||||
.extract({
|
||||
path: dst,
|
||||
follow: false, // Do not follow symlinks (#699)
|
||||
filter: filterSymlinks // Filter symlink files
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
)
|
||||
.on('error', reject)
|
||||
.on('finish', function(result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
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)
|
||||
.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', reject)
|
||||
.on('finish', function(result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
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)
|
||||
.pipe(zlib.createGunzip())
|
||||
.on('error', reject)
|
||||
.pipe(createWriteStream(dst))
|
||||
.on('error', reject)
|
||||
.on('finish', function(result) {
|
||||
destroy(stream);
|
||||
deferred.resolve(dst);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function isSymlink(_, entry) {
|
||||
return entry.type === 'symlink';
|
||||
}
|
||||
|
||||
function filterSymlinks(entry) {
|
||||
return entry.type !== 'SymbolicLink';
|
||||
}
|
||||
|
||||
function getExtractor(archive) {
|
||||
// Make the archive lower case to match against the types
|
||||
// This ensures that upper-cased extensions work
|
||||
archive = archive.toLowerCase();
|
||||
|
||||
var type = mout.array.find(extractorTypes, function(type) {
|
||||
return mout.string.endsWith(archive, type);
|
||||
});
|
||||
|
||||
return type ? extractors[type] : null;
|
||||
}
|
||||
|
||||
function isSingleDir(dir) {
|
||||
return Q.nfcall(fs.readdir, dir).then(function(files) {
|
||||
var singleDir;
|
||||
|
||||
// Remove any OS specific files from the files array
|
||||
// before checking its length
|
||||
files = files.filter(junk.isnt);
|
||||
|
||||
if (files.length !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
singleDir = path.join(dir, files[0]);
|
||||
|
||||
return Q.nfcall(fs.stat, singleDir).then(function(stat) {
|
||||
return stat.isDirectory() ? singleDir : false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function moveDirectory(srcDir, destDir) {
|
||||
return Q.nfcall(fs.readdir, srcDir)
|
||||
.then(function(files) {
|
||||
var promises = files.map(function(file) {
|
||||
var src = path.join(srcDir, file);
|
||||
var dst = path.join(destDir, file);
|
||||
|
||||
return Q.nfcall(fs.rename, src, dst);
|
||||
});
|
||||
|
||||
return Q.all(promises);
|
||||
})
|
||||
.then(function() {
|
||||
return Q.nfcall(fs.rmdir, srcDir);
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
|
||||
function canExtract(src, mimeType) {
|
||||
if (mimeType && mimeType !== 'application/octet-stream') {
|
||||
return !!getExtractor(mimeType);
|
||||
}
|
||||
|
||||
return !!getExtractor(src);
|
||||
}
|
||||
|
||||
// Available options:
|
||||
// - keepArchive: true to keep the archive afterwards (defaults to false)
|
||||
// - keepStructure: true to keep the extracted structure unchanged (defaults to false)
|
||||
function extract(src, dst, opts) {
|
||||
var extractor;
|
||||
var promise;
|
||||
|
||||
opts = opts || {};
|
||||
extractor = getExtractor(src);
|
||||
|
||||
// Try to get extractor from mime type
|
||||
if (!extractor && opts.mimeType) {
|
||||
extractor = getExtractor(opts.mimeType);
|
||||
}
|
||||
|
||||
// If extractor is null, then the archive type is unknown
|
||||
if (!extractor) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
// Move contents from the temporary directory
|
||||
// If the contents are a single directory (and we're not preserving structure),
|
||||
// move its contents directly instead.
|
||||
promise = promise
|
||||
.then(function() {
|
||||
return isSingleDir(tempDir);
|
||||
})
|
||||
.then(function(singleDir) {
|
||||
if (singleDir && !opts.keepStructure) {
|
||||
return moveDirectory(singleDir, dst);
|
||||
} else {
|
||||
return moveDirectory(tempDir, dst);
|
||||
}
|
||||
});
|
||||
|
||||
// Resolve promise to the dst dir
|
||||
return promise.then(function() {
|
||||
return dst;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = extract;
|
||||
module.exports.canExtract = canExtract;
|
||||
@@ -1,55 +0,0 @@
|
||||
var path = require('path');
|
||||
var bowerJson = require('bower-json');
|
||||
var Q = require('q');
|
||||
|
||||
// The valid options are the same as bower-json#readFile.
|
||||
// If the "assume" option is passed, it will be used if no json file was found
|
||||
|
||||
// This promise is resolved with [json, deprecated, assumed]
|
||||
// - json: The read json
|
||||
// - deprecated: The deprecated filename being used or false otherwise
|
||||
// - assumed: True if a dummy json was returned if no json file was found, false otherwise
|
||||
function readJson(file, options) {
|
||||
options = options || {};
|
||||
|
||||
// Read
|
||||
return Q.nfcall(bowerJson.read, 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;
|
||||
|
||||
return [json, deprecated, false];
|
||||
},
|
||||
function(err) {
|
||||
// No json file was found, assume one
|
||||
if (err.code === 'ENOENT' && options.assume) {
|
||||
return [bowerJson.parse(options.assume, options), false, true];
|
||||
}
|
||||
|
||||
err.details = err.message;
|
||||
|
||||
if (err.file) {
|
||||
err.message = 'Failed to read ' + err.file;
|
||||
err.data = { filename: err.file };
|
||||
} else {
|
||||
err.message = 'Failed to read json from ' + file;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = readJson;
|
||||
@@ -1,12 +0,0 @@
|
||||
var version = require('../version');
|
||||
|
||||
module.exports =
|
||||
'node/' +
|
||||
process.version +
|
||||
' ' +
|
||||
process.platform +
|
||||
' ' +
|
||||
process.arch +
|
||||
' ' +
|
||||
';Bower ' +
|
||||
version;
|
||||
@@ -1,22 +0,0 @@
|
||||
var Q = require('q');
|
||||
var fs = require('./fs');
|
||||
|
||||
function validLink(file) {
|
||||
// Ensures that a file is a symlink that points
|
||||
// to a valid file
|
||||
return Q.nfcall(fs.lstat, file)
|
||||
.then(function(lstat) {
|
||||
if (!lstat.isSymbolicLink()) {
|
||||
return [false];
|
||||
}
|
||||
|
||||
return Q.nfcall(fs.stat, file).then(function(stat) {
|
||||
return [stat];
|
||||
});
|
||||
})
|
||||
.fail(function(err) {
|
||||
return [false, err];
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = validLink;
|
||||
@@ -1,51 +0,0 @@
|
||||
var join = require('path').join;
|
||||
var execFileSync = require('child_process').execFileSync;
|
||||
var cache = {};
|
||||
var originalWhich = require('which');
|
||||
|
||||
var isWin = process.platform === 'win32';
|
||||
|
||||
function which(name, opt, cb) {
|
||||
if (typeof opt === 'function') {
|
||||
cb = opt;
|
||||
opt = {};
|
||||
}
|
||||
|
||||
if (isWin) {
|
||||
var result = whichSync(name);
|
||||
if (result) {
|
||||
cb(null, result);
|
||||
} else {
|
||||
cb(new Error('Could not find ' + name + ' in PATH'));
|
||||
}
|
||||
} else {
|
||||
originalWhich(name, opt, cb);
|
||||
}
|
||||
}
|
||||
|
||||
function whichSync(name, opt) {
|
||||
if (name in cache) {
|
||||
return cache[name];
|
||||
}
|
||||
if (isWin) {
|
||||
var WHERE_PATH = join(process.env.WINDIR, 'System32', 'where.exe');
|
||||
var stdout = execFileSync(WHERE_PATH, ['$PATH:' + name], {
|
||||
stdio: ['pipe', 'pipe', 'ignore']
|
||||
}).toString();
|
||||
var matches = stdout.split('\r\n');
|
||||
if (matches.length === 0) {
|
||||
throw new Error('Could not find ' + name + ' in PATH');
|
||||
}
|
||||
var result = matches[0].trim();
|
||||
cache[name] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
var result = originalWhich.sync(name, opt);
|
||||
cache[name] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
which.sync = whichSync;
|
||||
|
||||
module.exports = which;
|
||||
107
package.json
107
package.json
@@ -1,109 +1,10 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "bower",
|
||||
"version": "1.8.13",
|
||||
"description": "The browser package manager",
|
||||
"author": "Twitter",
|
||||
"license": "MIT",
|
||||
"repository": "bower/bower",
|
||||
"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",
|
||||
"cardinal": "0.4.4",
|
||||
"chalk": "^1.0.0",
|
||||
"chmodr": "1.0.2",
|
||||
"configstore": "^2.0.0",
|
||||
"decompress-zip": "^0.2.2",
|
||||
"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.5.3",
|
||||
"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.67.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"
|
||||
},
|
||||
"devDependencies": {
|
||||
"arr-diff": "^2.0.0",
|
||||
"chai": "^3.5.0",
|
||||
"eslint": "^4.18.2",
|
||||
"expect.js": "^0.3.1",
|
||||
"husky": "^0.14.3",
|
||||
"in-publish": "^2.0.0",
|
||||
"lint-staged": "^9.5.0",
|
||||
"mocha": "^3.5.3",
|
||||
"multiline": "^1.0.2",
|
||||
"nock": "^11.7.0",
|
||||
"nock-legacy": "npm:nock@9.2.3",
|
||||
"node-uuid": "^1.4.7",
|
||||
"prettier": "^1.19.1",
|
||||
"proxyquire": "^1.7.9",
|
||||
"spawn-sync": "1.0.15",
|
||||
"wrench": "^1.5.8"
|
||||
"lerna": "2.0.0-beta.23"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"test": "node test/packages.js && node test/packages-svn.js && mocha --timeout 15000 --reporter spec",
|
||||
"prepublishOnly": "in-publish && echo 'You need to use \"node publish.js\" to publish bower' && false || not-in-publish",
|
||||
"format": "prettier --write --single-quote --tab-width 4 '**/*.js'",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"prettier --single-quote --tab-width 4"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"lib"
|
||||
],
|
||||
"resolutions": {
|
||||
"deep-extend": "0.5.1",
|
||||
"minimist": "0.2.1"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/bower-config",
|
||||
"packages/bower-endpoint-parser",
|
||||
"packages/bower-json",
|
||||
"packages/bower-logger",
|
||||
"packages/bower-registry-client"
|
||||
]
|
||||
"postinstall": "lerna bootstrap",
|
||||
"test": "lerna run test"
|
||||
}
|
||||
}
|
||||
|
||||
61
packages/bower-config/.jshintrc
Normal file
61
packages/bower-config/.jshintrc
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
9
packages/bower-config/.travis.yml
Normal file
9
packages/bower-config/.travis.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- '5'
|
||||
- '4'
|
||||
- '0.12'
|
||||
- '0.10'
|
||||
script:
|
||||
- grunt travis
|
||||
@@ -1,13 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 1.4.2
|
||||
|
||||
- Prevent errors when expanded env variable does not exist
|
||||
|
||||
## 1.4.2
|
||||
|
||||
- Update minimist to 0.2.1 to fix security issue
|
||||
|
||||
## 1.4.0
|
||||
|
||||
- Change default shorthand resolver from git:// to https://
|
||||
|
||||
47
packages/bower-config/Gruntfile.js
Normal file
47
packages/bower-config/Gruntfile.js
Normal file
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
module.exports = function (grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.initConfig({
|
||||
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: [
|
||||
'Gruntfile.js',
|
||||
'bin/*',
|
||||
'lib/**/*.js',
|
||||
'test/**/*.js',
|
||||
'!test/assets/**/*',
|
||||
'!test/reports/**/*',
|
||||
'!test/tmp/**/*'
|
||||
],
|
||||
tasks: ['simplemocha:short']
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('test', ['simplemocha:full']);
|
||||
grunt.registerTask('cover', 'exec:cover');
|
||||
grunt.registerTask('travis', ['exec:cover', 'exec:coveralls']);
|
||||
grunt.registerTask('default', 'test');
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
# bower-config
|
||||
# bower-config [](http://travis-ci.org/bower/config)[](https://coveralls.io/github/bower/config?branch=master)
|
||||
|
||||
> The Bower config (`.bowerrc`) reader and writer.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"registry": {
|
||||
"search": [
|
||||
"http://localhost:8000",
|
||||
"https://registry.bower.io"
|
||||
"https://bower.herokuapp.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,12 @@ function Config(cwd) {
|
||||
this._config = {};
|
||||
}
|
||||
|
||||
Config.prototype.load = function(overwrites) {
|
||||
Config.prototype.load = function (overwrites) {
|
||||
this._config = rc('bower', this._cwd);
|
||||
|
||||
this._config = object.merge(
|
||||
expand(this._config || {}),
|
||||
expand(overwrites || {})
|
||||
expand(this._config || {}),
|
||||
expand(overwrites || {})
|
||||
);
|
||||
|
||||
this._config = normalise(this._config);
|
||||
@@ -27,8 +27,8 @@ Config.prototype.load = function(overwrites) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Config.prototype.restore = function() {
|
||||
this._proxy.restore();
|
||||
Config.prototype.restore = function () {
|
||||
this._proxy.restore();
|
||||
};
|
||||
|
||||
function readCertFile(path) {
|
||||
@@ -44,14 +44,10 @@ function readCertFile(path) {
|
||||
certificates = path;
|
||||
}
|
||||
|
||||
return certificates
|
||||
.split(sep)
|
||||
.filter(function(s) {
|
||||
return !s.match(/^\s*$/);
|
||||
})
|
||||
.map(function(s) {
|
||||
return s + sep;
|
||||
});
|
||||
return certificates.
|
||||
split(sep).
|
||||
filter(function(s) { return !s.match(/^\s*$/); }).
|
||||
map(function(s) { return s + sep; });
|
||||
}
|
||||
|
||||
function loadCAs(caConfig) {
|
||||
@@ -72,15 +68,15 @@ function loadCAs(caConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
Config.prototype.toObject = function() {
|
||||
Config.prototype.toObject = function () {
|
||||
return lang.deepClone(this._config);
|
||||
};
|
||||
|
||||
Config.create = function(cwd) {
|
||||
Config.create = function (cwd) {
|
||||
return new Config(cwd);
|
||||
};
|
||||
|
||||
Config.read = function(cwd, overrides) {
|
||||
Config.read = function (cwd, overrides) {
|
||||
var config = Config.create(cwd);
|
||||
return config.load(overrides).toObject();
|
||||
};
|
||||
@@ -90,13 +86,13 @@ function normalise(config) {
|
||||
|
||||
// Some backwards compatible things..
|
||||
if (config.shorthandResolver) {
|
||||
config.shorthandResolver = config.shorthandResolver
|
||||
.replace(/\{\{\{/g, '{{')
|
||||
.replace(/\}\}\}/g, '}}');
|
||||
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) {
|
||||
config.registry.search = config.registry.search.map(function (url) {
|
||||
return url.replace(/\/+$/, '');
|
||||
});
|
||||
config.registry.register = config.registry.register.replace(/\/+$/, '');
|
||||
|
||||
@@ -2,44 +2,43 @@ 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 proxy = process.env.HTTP_PROXY
|
||||
|| process.env.http_proxy
|
||||
|| null;
|
||||
|
||||
var httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy || proxy;
|
||||
var httpsProxy = process.env.HTTPS_PROXY
|
||||
|| process.env.https_proxy
|
||||
|| proxy;
|
||||
|
||||
var noProxy = process.env.NO_PROXY || process.env.no_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 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',
|
||||
'directory': 'bower_components',
|
||||
'registry': 'https://bower.herokuapp.com',
|
||||
'shorthand-resolver': 'https://github.com/{{owner}}/{{package}}.git',
|
||||
tmp: paths.tmp,
|
||||
proxy: proxy,
|
||||
'tmp': paths.tmp,
|
||||
'proxy': proxy,
|
||||
'https-proxy': httpsProxy,
|
||||
'no-proxy': noProxy,
|
||||
timeout: 30000,
|
||||
ca: { search: [] },
|
||||
'timeout': 30000,
|
||||
'ca': { search: [] },
|
||||
'strict-ssl': true,
|
||||
'user-agent': userAgent,
|
||||
color: true,
|
||||
interactive: null,
|
||||
storage: {
|
||||
'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
|
||||
empty: path.join(paths.data, 'empty') // Empty dir, used in GIT_TEMPLATE_DIR among others
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ function camelCase(config) {
|
||||
var camelCased = {};
|
||||
|
||||
// Camel case
|
||||
object.forOwn(config, function(value, key) {
|
||||
object.forOwn(config, function (value, key) {
|
||||
// Ignore null values
|
||||
if (value == null) {
|
||||
return;
|
||||
@@ -22,31 +22,30 @@ function camelCase(config) {
|
||||
// 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);
|
||||
function doEnvReplaceStr (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]) {
|
||||
return '${' + name + '}';
|
||||
}
|
||||
// Un-tildify
|
||||
var untildify = require('untildify');
|
||||
f = untildify(f);
|
||||
|
||||
return process.env[name];
|
||||
});
|
||||
// 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) {
|
||||
object.forOwn(config, function (value, key) {
|
||||
|
||||
// Ignore null values
|
||||
if (value == null) {
|
||||
return;
|
||||
@@ -55,19 +54,19 @@ function envReplace(config) {
|
||||
// Ignore 'scripts'
|
||||
// These hooks run within the shell
|
||||
// environment variable expansion is not required
|
||||
if (key === 'scripts' && lang.isPlainObject(value)) {
|
||||
if ( key === 'scripts' && lang.isPlainObject(value) ){
|
||||
envReplaced[key] = value;
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform variable replacements based on var type
|
||||
if (lang.isPlainObject(value)) {
|
||||
if ( lang.isPlainObject(value) ) {
|
||||
envReplaced[key] = envReplace(value);
|
||||
} else if (lang.isArray(value)) {
|
||||
envReplaced[key] = envReplace(value);
|
||||
} else if (lang.isString(value)) {
|
||||
}
|
||||
else if ( lang.isString(value) ) {
|
||||
envReplaced[key] = doEnvReplaceStr(value);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
envReplaced[key] = value;
|
||||
}
|
||||
});
|
||||
@@ -87,8 +86,7 @@ function expand(config) {
|
||||
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 || 'https://bower.herokuapp.com';
|
||||
|
||||
config.registry = {
|
||||
default: config.registry.default,
|
||||
|
||||
@@ -4,16 +4,8 @@ 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');
|
||||
var uid = process.pid + '-' + Date.now() + '-' + Math.floor(Math.random() * 1000000);
|
||||
return crypto.createHash('md5').update(uid).digest('hex');
|
||||
}
|
||||
|
||||
// Assume XDG defaults
|
||||
@@ -38,7 +30,7 @@ if (process.platform === 'win32') {
|
||||
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
|
||||
// Fallbacks for other operating systems
|
||||
} else {
|
||||
base = path.resolve(home || tmp);
|
||||
|
||||
|
||||
@@ -2,78 +2,78 @@
|
||||
// process.env uppercase proxy variables to them with the ability
|
||||
// to restore the original values later
|
||||
var EnvProxy = function() {
|
||||
this.restoreFrom = {};
|
||||
this.restoreFrom = {};
|
||||
};
|
||||
|
||||
EnvProxy.prototype.set = function(config) {
|
||||
this.config = config;
|
||||
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;
|
||||
}
|
||||
// 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, '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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 (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.no_proxy !== undefined) {
|
||||
process.env.no_proxy = this.restoreFrom.no_proxy;
|
||||
} else {
|
||||
delete process.env.no_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, '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;
|
||||
}
|
||||
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,6 +1,6 @@
|
||||
var path = require('path');
|
||||
var fs = require('graceful-fs');
|
||||
var optimist = require('../vendor/optimist');
|
||||
var optimist = require('optimist');
|
||||
var osenv = require('osenv');
|
||||
var object = require('mout/object');
|
||||
var string = require('mout/string');
|
||||
@@ -16,7 +16,7 @@ function rc(name, cwd, argv) {
|
||||
argv = argv || optimist.argv;
|
||||
|
||||
// Parse --config.foo=false
|
||||
argvConfig = object.map(argv.config || {}, function(value) {
|
||||
argvConfig = object.map(argv.config || {}, function (value) {
|
||||
return value === 'false' ? false : value;
|
||||
});
|
||||
|
||||
@@ -106,14 +106,14 @@ function env(prefix) {
|
||||
|
||||
prefix = prefix.toLowerCase();
|
||||
|
||||
object.forOwn(process.env, function(value, key) {
|
||||
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
|
||||
.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"]'
|
||||
@@ -122,9 +122,10 @@ function env(prefix) {
|
||||
if (!match || match.length === 0) {
|
||||
targetValue = value;
|
||||
} else {
|
||||
targetValue = match[1].split(',').map(function(m) {
|
||||
return m.trim();
|
||||
});
|
||||
targetValue = match[1].split(',')
|
||||
.map(function(m) {
|
||||
return m.trim();
|
||||
});
|
||||
}
|
||||
object.set(obj, parsedKey, targetValue);
|
||||
}
|
||||
@@ -136,7 +137,7 @@ function env(prefix) {
|
||||
function find(filename, dir) {
|
||||
var files = [];
|
||||
|
||||
var walk = function(filename, dir) {
|
||||
var walk = function (filename, dir) {
|
||||
var file = path.join(dir, filename);
|
||||
var parent = path.dirname(dir);
|
||||
|
||||
|
||||
358
packages/bower-config/lib/vendor/optimist.js
vendored
358
packages/bower-config/lib/vendor/optimist.js
vendored
@@ -1,358 +0,0 @@
|
||||
var path = require('path');
|
||||
var minimist = require('minimist');
|
||||
var wordwrap = require('wordwrap');
|
||||
|
||||
/* Hack an instance of Argv with process.argv into Argv
|
||||
so people can do
|
||||
require('optimist')(['--beeble=1','-z','zizzle']).argv
|
||||
to parse a list of args and
|
||||
require('optimist').argv
|
||||
to get a parsed version of process.argv.
|
||||
*/
|
||||
|
||||
var inst = Argv(process.argv.slice(2));
|
||||
Object.keys(inst).forEach(function(key) {
|
||||
Argv[key] =
|
||||
typeof inst[key] == 'function' ? inst[key].bind(inst) : inst[key];
|
||||
});
|
||||
|
||||
var exports = (module.exports = Argv);
|
||||
function Argv(processArgs, cwd) {
|
||||
var self = {};
|
||||
if (!cwd) cwd = process.cwd();
|
||||
|
||||
self.$0 = process.argv
|
||||
.slice(0, 2)
|
||||
.map(function(x) {
|
||||
var b = rebase(cwd, x);
|
||||
return x.match(/^\//) && b.length < x.length ? b : x;
|
||||
})
|
||||
.join(' ');
|
||||
|
||||
if (process.env._ != undefined && process.argv[1] == process.env._) {
|
||||
self.$0 = process.env._.replace(
|
||||
path.dirname(process.execPath) + '/',
|
||||
''
|
||||
);
|
||||
}
|
||||
|
||||
var options = {
|
||||
boolean: [],
|
||||
string: [],
|
||||
alias: {},
|
||||
default: []
|
||||
};
|
||||
|
||||
self.boolean = function(bools) {
|
||||
options.boolean.push.apply(options.boolean, [].concat(bools));
|
||||
return self;
|
||||
};
|
||||
|
||||
self.string = function(strings) {
|
||||
options.string.push.apply(options.string, [].concat(strings));
|
||||
return self;
|
||||
};
|
||||
|
||||
self.default = function(key, value) {
|
||||
if (typeof key === 'object') {
|
||||
Object.keys(key).forEach(function(k) {
|
||||
self.default(k, key[k]);
|
||||
});
|
||||
} else {
|
||||
options.default[key] = value;
|
||||
}
|
||||
return self;
|
||||
};
|
||||
|
||||
self.alias = function(x, y) {
|
||||
if (typeof x === 'object') {
|
||||
Object.keys(x).forEach(function(key) {
|
||||
self.alias(key, x[key]);
|
||||
});
|
||||
} else {
|
||||
options.alias[x] = (options.alias[x] || []).concat(y);
|
||||
}
|
||||
return self;
|
||||
};
|
||||
|
||||
var demanded = {};
|
||||
self.demand = function(keys) {
|
||||
if (typeof keys == 'number') {
|
||||
if (!demanded._) demanded._ = 0;
|
||||
demanded._ += keys;
|
||||
} else if (Array.isArray(keys)) {
|
||||
keys.forEach(function(key) {
|
||||
self.demand(key);
|
||||
});
|
||||
} else {
|
||||
demanded[keys] = true;
|
||||
}
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
var usage;
|
||||
self.usage = function(msg, opts) {
|
||||
if (!opts && typeof msg === 'object') {
|
||||
opts = msg;
|
||||
msg = null;
|
||||
}
|
||||
|
||||
usage = msg;
|
||||
|
||||
if (opts) self.options(opts);
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
function fail(msg) {
|
||||
self.showHelp();
|
||||
if (msg) console.error(msg);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var checks = [];
|
||||
self.check = function(f) {
|
||||
checks.push(f);
|
||||
return self;
|
||||
};
|
||||
|
||||
var descriptions = {};
|
||||
self.describe = function(key, desc) {
|
||||
if (typeof key === 'object') {
|
||||
Object.keys(key).forEach(function(k) {
|
||||
self.describe(k, key[k]);
|
||||
});
|
||||
} else {
|
||||
descriptions[key] = desc;
|
||||
}
|
||||
return self;
|
||||
};
|
||||
|
||||
self.parse = function(args) {
|
||||
return parseArgs(args);
|
||||
};
|
||||
|
||||
self.option = self.options = function(key, opt) {
|
||||
if (typeof key === 'object') {
|
||||
Object.keys(key).forEach(function(k) {
|
||||
self.options(k, key[k]);
|
||||
});
|
||||
} else {
|
||||
if (opt.alias) self.alias(key, opt.alias);
|
||||
if (opt.demand) self.demand(key);
|
||||
if (typeof opt.default !== 'undefined') {
|
||||
self.default(key, opt.default);
|
||||
}
|
||||
|
||||
if (opt.boolean || opt.type === 'boolean') {
|
||||
self.boolean(key);
|
||||
}
|
||||
if (opt.string || opt.type === 'string') {
|
||||
self.string(key);
|
||||
}
|
||||
|
||||
var desc = opt.describe || opt.description || opt.desc;
|
||||
if (desc) {
|
||||
self.describe(key, desc);
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
var wrap = null;
|
||||
self.wrap = function(cols) {
|
||||
wrap = cols;
|
||||
return self;
|
||||
};
|
||||
|
||||
self.showHelp = function(fn) {
|
||||
if (!fn) fn = console.error;
|
||||
fn(self.help());
|
||||
};
|
||||
|
||||
self.help = function() {
|
||||
var keys = Object.keys(
|
||||
Object.keys(descriptions)
|
||||
.concat(Object.keys(demanded))
|
||||
.concat(Object.keys(options.default))
|
||||
.reduce(function(acc, key) {
|
||||
if (key !== '_') acc[key] = true;
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
|
||||
var help = keys.length ? ['Options:'] : [];
|
||||
|
||||
if (usage) {
|
||||
help.unshift(usage.replace(/\$0/g, self.$0), '');
|
||||
}
|
||||
|
||||
var switches = keys.reduce(function(acc, key) {
|
||||
acc[key] = [key]
|
||||
.concat(options.alias[key] || [])
|
||||
.map(function(sw) {
|
||||
return (sw.length > 1 ? '--' : '-') + sw;
|
||||
})
|
||||
.join(', ');
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
var switchlen = longest(
|
||||
Object.keys(switches).map(function(s) {
|
||||
return switches[s] || '';
|
||||
})
|
||||
);
|
||||
|
||||
var desclen = longest(
|
||||
Object.keys(descriptions).map(function(d) {
|
||||
return descriptions[d] || '';
|
||||
})
|
||||
);
|
||||
|
||||
keys.forEach(function(key) {
|
||||
var kswitch = switches[key];
|
||||
var desc = descriptions[key] || '';
|
||||
|
||||
if (wrap) {
|
||||
desc = wordwrap(switchlen + 4, wrap)(desc).slice(switchlen + 4);
|
||||
}
|
||||
|
||||
var spadding = new Array(
|
||||
Math.max(switchlen - kswitch.length + 3, 0)
|
||||
).join(' ');
|
||||
|
||||
var dpadding = new Array(
|
||||
Math.max(desclen - desc.length + 1, 0)
|
||||
).join(' ');
|
||||
|
||||
var type = null;
|
||||
|
||||
if (options.boolean[key]) type = '[boolean]';
|
||||
if (options.string[key]) type = '[string]';
|
||||
|
||||
if (!wrap && dpadding.length > 0) {
|
||||
desc += dpadding;
|
||||
}
|
||||
|
||||
var prelude = ' ' + kswitch + spadding;
|
||||
var extra = [
|
||||
type,
|
||||
demanded[key] ? '[required]' : null,
|
||||
options.default[key] !== undefined
|
||||
? '[default: ' + JSON.stringify(options.default[key]) + ']'
|
||||
: null
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' ');
|
||||
|
||||
var body = [desc, extra].filter(Boolean).join(' ');
|
||||
|
||||
if (wrap) {
|
||||
var dlines = desc.split('\n');
|
||||
var dlen =
|
||||
dlines.slice(-1)[0].length +
|
||||
(dlines.length === 1 ? prelude.length : 0);
|
||||
|
||||
body =
|
||||
desc +
|
||||
(dlen + extra.length > wrap - 2
|
||||
? '\n' +
|
||||
new Array(wrap - extra.length + 1).join(' ') +
|
||||
extra
|
||||
: new Array(wrap - extra.length - dlen + 1).join(' ') +
|
||||
extra);
|
||||
}
|
||||
|
||||
help.push(prelude + body);
|
||||
});
|
||||
|
||||
help.push('');
|
||||
return help.join('\n');
|
||||
};
|
||||
|
||||
Object.defineProperty(self, 'argv', {
|
||||
get: function() {
|
||||
return parseArgs(processArgs);
|
||||
},
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
function parseArgs(args) {
|
||||
var argv = minimist(args, options);
|
||||
argv.$0 = self.$0;
|
||||
|
||||
if (demanded._ && argv._.length < demanded._) {
|
||||
fail(
|
||||
'Not enough non-option arguments: got ' +
|
||||
argv._.length +
|
||||
', need at least ' +
|
||||
demanded._
|
||||
);
|
||||
}
|
||||
|
||||
var missing = [];
|
||||
Object.keys(demanded).forEach(function(key) {
|
||||
if (!argv[key]) missing.push(key);
|
||||
});
|
||||
|
||||
if (missing.length) {
|
||||
fail('Missing required arguments: ' + missing.join(', '));
|
||||
}
|
||||
|
||||
checks.forEach(function(f) {
|
||||
try {
|
||||
if (f(argv) === false) {
|
||||
fail('Argument check failed: ' + f.toString());
|
||||
}
|
||||
} catch (err) {
|
||||
fail(err);
|
||||
}
|
||||
});
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
function longest(xs) {
|
||||
return Math.max.apply(
|
||||
null,
|
||||
xs.map(function(x) {
|
||||
return x.length;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
// rebase an absolute path to a relative one with respect to a base directory
|
||||
// exported for tests
|
||||
exports.rebase = rebase;
|
||||
function rebase(base, dir) {
|
||||
var ds = path
|
||||
.normalize(dir)
|
||||
.split('/')
|
||||
.slice(1);
|
||||
var bs = path
|
||||
.normalize(base)
|
||||
.split('/')
|
||||
.slice(1);
|
||||
|
||||
for (var i = 0; ds[i] && ds[i] == bs[i]; i++);
|
||||
ds.splice(0, i);
|
||||
bs.splice(0, i);
|
||||
|
||||
var p = path
|
||||
.normalize(
|
||||
bs
|
||||
.map(function() {
|
||||
return '..';
|
||||
})
|
||||
.concat(ds)
|
||||
.join('/')
|
||||
)
|
||||
.replace(/\/$/, '')
|
||||
.replace(/^$/, '.');
|
||||
return p.match(/^[.\/]/) ? p : './' + p;
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "bower-config",
|
||||
"version": "1.4.3",
|
||||
"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",
|
||||
"repository": "bower/config",
|
||||
"main": "lib/Config",
|
||||
"homepage": "http://bower.io",
|
||||
"engines": {
|
||||
@@ -12,23 +12,32 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.1.3",
|
||||
"minimist": "^0.2.1",
|
||||
"mout": "^1.0.0",
|
||||
"optimist": "^0.6.1",
|
||||
"osenv": "^0.1.3",
|
||||
"untildify": "^2.1.0",
|
||||
"wordwrap": "^0.0.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": "^3.5.3",
|
||||
"mocha": "~1.12.0",
|
||||
"node-uuid": "^1.4.3",
|
||||
"q": "^1.2.0",
|
||||
"rimraf": "^2.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test"
|
||||
"test": "grunt test"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
||||
@@ -4,12 +4,7 @@
|
||||
},
|
||||
"storage" : {
|
||||
"packages" : "${_BOWERRC_MY_PACKAGES}",
|
||||
"registry" : {
|
||||
"register": "~/.bower-test/registry",
|
||||
"search": [
|
||||
"${_BOWERRC_MY_USER}:${_BOWERRC_MY_PASS}"
|
||||
]
|
||||
}
|
||||
"registry" : "~/.bower-test/registry"
|
||||
},
|
||||
"tmp" : "${_BOWERRC_MY_TMP}"
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var path = require('path');
|
||||
var bowerConfig = require('..');
|
||||
|
||||
var config = bowerConfig.read(path.join(__dirname + '/assets/env-variables'), {
|
||||
foo: 'bar'
|
||||
});
|
||||
|
||||
console.log(config);
|
||||
@@ -17,17 +17,17 @@ var tmpLocation = path.join(
|
||||
uuid.v4().slice(0, 8)
|
||||
);
|
||||
|
||||
after(function() {
|
||||
after(function () {
|
||||
rimraf.sync(tmpLocation);
|
||||
});
|
||||
|
||||
exports.TempDir = (function() {
|
||||
function TempDir(defaults) {
|
||||
function TempDir (defaults) {
|
||||
this.path = path.join(tmpLocation, uuid.v4());
|
||||
this.defaults = defaults;
|
||||
}
|
||||
|
||||
TempDir.prototype.create = function(files, defaults) {
|
||||
TempDir.prototype.create = function (files, defaults) {
|
||||
var that = this;
|
||||
|
||||
defaults = defaults || this.defaults || {};
|
||||
@@ -42,7 +42,7 @@ exports.TempDir = (function() {
|
||||
};
|
||||
|
||||
if (files) {
|
||||
object.forOwn(files, function(contents, filepath) {
|
||||
object.forOwn(files, function (contents, filepath) {
|
||||
if (typeof contents === 'object') {
|
||||
contents = JSON.stringify(contents, null, ' ') + '\n';
|
||||
}
|
||||
@@ -56,7 +56,7 @@ exports.TempDir = (function() {
|
||||
return this;
|
||||
};
|
||||
|
||||
TempDir.prototype.prepare = function(files) {
|
||||
TempDir.prototype.prepare = function (files) {
|
||||
rimraf.sync(this.path);
|
||||
mkdirp.sync(this.path);
|
||||
this.create(files);
|
||||
@@ -65,7 +65,7 @@ exports.TempDir = (function() {
|
||||
};
|
||||
|
||||
// TODO: Rewrite to synchronous form
|
||||
TempDir.prototype.prepareGit = function(revisions) {
|
||||
TempDir.prototype.prepareGit = function (revisions) {
|
||||
var that = this;
|
||||
|
||||
revisions = object.merge(revisions || {}, this.defaults);
|
||||
@@ -76,56 +76,51 @@ exports.TempDir = (function() {
|
||||
|
||||
var promise = new Q();
|
||||
|
||||
object.forOwn(revisions, function(files, tag) {
|
||||
promise = promise
|
||||
.then(function() {
|
||||
return that.git('init');
|
||||
})
|
||||
.then(function() {
|
||||
that.glob('./!(.git)').map(function(removePath) {
|
||||
var fullPath = path.join(that.path, removePath);
|
||||
object.forOwn(revisions, function (files, tag) {
|
||||
promise = promise.then(function () {
|
||||
return that.git('init');
|
||||
}).then(function () {
|
||||
that.glob('./!(.git)').map(function (removePath) {
|
||||
var fullPath = path.join(that.path, removePath);
|
||||
|
||||
rimraf.sync(fullPath);
|
||||
});
|
||||
|
||||
that.create(files, {});
|
||||
})
|
||||
.then(function() {
|
||||
return that.git('add', '-A');
|
||||
})
|
||||
.then(function() {
|
||||
return that.git('commit', '-m"commit"');
|
||||
})
|
||||
.then(function() {
|
||||
return that.git('tag', tag);
|
||||
rimraf.sync(fullPath);
|
||||
});
|
||||
|
||||
that.create(files, {});
|
||||
}).then(function () {
|
||||
return that.git('add', '-A');
|
||||
}).then(function () {
|
||||
return that.git('commit', '-m"commit"');
|
||||
}).then(function () {
|
||||
return that.git('tag', tag);
|
||||
});
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
TempDir.prototype.glob = function(pattern) {
|
||||
TempDir.prototype.glob = function (pattern) {
|
||||
return glob.sync(pattern, {
|
||||
cwd: this.path,
|
||||
dot: true
|
||||
});
|
||||
};
|
||||
|
||||
TempDir.prototype.getPath = function(name) {
|
||||
TempDir.prototype.getPath = function (name) {
|
||||
return path.join(this.path, name);
|
||||
};
|
||||
|
||||
TempDir.prototype.read = function(name) {
|
||||
TempDir.prototype.read = function (name) {
|
||||
return fs.readFileSync(this.getPath(name), 'utf8');
|
||||
};
|
||||
|
||||
TempDir.prototype.readJson = function(name) {
|
||||
TempDir.prototype.readJson = function (name) {
|
||||
return JSON.parse(this.read(name));
|
||||
};
|
||||
|
||||
TempDir.prototype.exists = function(name) {
|
||||
TempDir.prototype.exists = function (name) {
|
||||
return fs.existsSync(path.join(this.path, name));
|
||||
};
|
||||
|
||||
return TempDir;
|
||||
})();
|
||||
})();
|
||||
@@ -1,64 +1,66 @@
|
||||
var assert = require('assert');
|
||||
var path = require('path');
|
||||
|
||||
describe('NPM Config on package.json', function() {
|
||||
beforeEach(function() {
|
||||
describe('NPM Config on package.json', function () {
|
||||
beforeEach(function () {
|
||||
delete process.env.npm_package_config_bower_directory;
|
||||
delete process.env.npm_package_config_bower_colors;
|
||||
delete process.env.npm_package_config_bower_resolvers;
|
||||
});
|
||||
|
||||
it('defaults registry entries to default registry', function() {
|
||||
it('defaults registry entries to default registry', function () {
|
||||
var config = require('../lib/Config').read(null, {});
|
||||
|
||||
assert.deepEqual(config.registry, {
|
||||
default: 'https://registry.bower.io',
|
||||
search: ['https://registry.bower.io'],
|
||||
register: 'https://registry.bower.io',
|
||||
publish: 'https://registry.bower.io'
|
||||
'default': 'https://bower.herokuapp.com',
|
||||
'search': [
|
||||
'https://bower.herokuapp.com'
|
||||
],
|
||||
'register': 'https://bower.herokuapp.com',
|
||||
'publish': 'https://bower.herokuapp.com'
|
||||
});
|
||||
});
|
||||
|
||||
it('can change default registry', function() {
|
||||
var config = require('../lib/Config').read(null, {
|
||||
registry: 'https://foobar'
|
||||
});
|
||||
it('can change default registry', function () {
|
||||
var config = require('../lib/Config').read(null, { registry: 'https://foobar' });
|
||||
|
||||
assert.deepEqual(config.registry, {
|
||||
default: 'https://foobar',
|
||||
search: ['https://foobar'],
|
||||
register: 'https://foobar',
|
||||
publish: 'https://foobar'
|
||||
'default': 'https://foobar',
|
||||
'search': [
|
||||
'https://foobar',
|
||||
],
|
||||
'register': 'https://foobar',
|
||||
'publish': 'https://foobar'
|
||||
});
|
||||
});
|
||||
|
||||
it('can override single entries in registry configuration', function() {
|
||||
var config = require('../lib/Config').read(null, {
|
||||
registry: { search: 'https://foobar' }
|
||||
});
|
||||
it('can override single entries in registry configuration', function () {
|
||||
var config = require('../lib/Config').read(null, { registry: { search: 'https://foobar' } });
|
||||
|
||||
assert.deepEqual(config.registry, {
|
||||
default: 'https://registry.bower.io',
|
||||
search: ['https://foobar'],
|
||||
register: 'https://registry.bower.io',
|
||||
publish: 'https://registry.bower.io'
|
||||
'default': 'https://bower.herokuapp.com',
|
||||
'search': [
|
||||
'https://foobar',
|
||||
],
|
||||
'register': 'https://bower.herokuapp.com',
|
||||
'publish': 'https://bower.herokuapp.com'
|
||||
});
|
||||
});
|
||||
|
||||
it('can override single entries in registry configuration and defaults', function() {
|
||||
var config = require('../lib/Config').read(null, {
|
||||
registry: { default: 'https://fizfuz', search: 'https://foobar' }
|
||||
});
|
||||
it('can override single entries in registry configuration and defaults', function () {
|
||||
var config = require('../lib/Config').read(null, { registry: { default: 'https://fizfuz', search: 'https://foobar' } });
|
||||
|
||||
assert.deepEqual(config.registry, {
|
||||
default: 'https://fizfuz',
|
||||
search: ['https://foobar'],
|
||||
register: 'https://fizfuz',
|
||||
publish: 'https://fizfuz'
|
||||
'default': 'https://fizfuz',
|
||||
'search': [
|
||||
'https://foobar',
|
||||
],
|
||||
'register': 'https://fizfuz',
|
||||
'publish': 'https://fizfuz'
|
||||
});
|
||||
});
|
||||
|
||||
it('allows for not providing cwd', function() {
|
||||
it('allows for not providing cwd', function () {
|
||||
var config = require('../lib/Config').read();
|
||||
|
||||
config.tmp = '/foo/bar';
|
||||
@@ -66,22 +68,24 @@ describe('NPM Config on package.json', function() {
|
||||
delete config.storage;
|
||||
|
||||
assert.deepEqual(config, {
|
||||
directory: 'bower_components',
|
||||
registry: {
|
||||
default: 'https://registry.bower.io',
|
||||
search: ['https://registry.bower.io'],
|
||||
register: 'https://registry.bower.io',
|
||||
publish: 'https://registry.bower.io'
|
||||
},
|
||||
shorthandResolver: 'https://github.com/{{owner}}/{{package}}.git',
|
||||
tmp: '/foo/bar',
|
||||
timeout: 30000,
|
||||
ca: {
|
||||
search: []
|
||||
},
|
||||
strictSsl: true,
|
||||
userAgent: 'firefox',
|
||||
color: true
|
||||
'directory': 'bower_components',
|
||||
'registry': {
|
||||
'default': 'https://bower.herokuapp.com',
|
||||
'search': [
|
||||
'https://bower.herokuapp.com'
|
||||
],
|
||||
'register': 'https://bower.herokuapp.com',
|
||||
'publish': 'https://bower.herokuapp.com'
|
||||
},
|
||||
'shorthandResolver': 'https://github.com/{{owner}}/{{package}}.git',
|
||||
'tmp': '/foo/bar',
|
||||
'timeout': 30000,
|
||||
'ca': {
|
||||
'search': []
|
||||
},
|
||||
'strictSsl': true,
|
||||
'userAgent': 'firefox',
|
||||
'color': true
|
||||
});
|
||||
});
|
||||
|
||||
@@ -92,49 +96,41 @@ describe('NPM Config on package.json', function() {
|
||||
assert(Array.isArray(caData), name + ' should be an array');
|
||||
assert.equal(2, caData.length);
|
||||
caData.forEach(function(c, i) {
|
||||
assert(
|
||||
c.match(r),
|
||||
name +
|
||||
'[' +
|
||||
i +
|
||||
'] should contain a certificate. Given: ' +
|
||||
JSON.stringify(c)
|
||||
);
|
||||
assert(c.match(r),
|
||||
name + '[' + i + '] should contain a certificate. Given: ' + JSON.stringify(c));
|
||||
});
|
||||
}
|
||||
|
||||
describe('Setting process.env.npm_package_config', function() {
|
||||
describe('Setting process.env.npm_package_config', function () {
|
||||
process.env.npm_package_config_bower_directory = 'npm-path';
|
||||
process.env.npm_package_config_bower_colors = 'false';
|
||||
process.env.npm_package_config_bower_resolvers = '[foo,bar,baz]';
|
||||
|
||||
var config = require('../lib/Config').read();
|
||||
|
||||
it('should return "npm-path" for "bower_directory"', function() {
|
||||
it('should return "npm-path" for "bower_directory"', function () {
|
||||
assert.equal('npm-path', config.directory);
|
||||
});
|
||||
it('should return "false" for "bower_colors"', function() {
|
||||
it('should return "false" for "bower_colors"', function () {
|
||||
assert.equal('false', config.colors);
|
||||
});
|
||||
it('should expand array "false" for "bower_resolvers"', function() {
|
||||
it('should expand array "false" for "bower_resolvers"', function () {
|
||||
assert.deepEqual(['foo', 'bar', 'baz'], config.resolvers);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Specifying custom CA', function() {
|
||||
it('should read the CA file', function() {
|
||||
var config = require('../lib/Config').read(
|
||||
path.resolve('test/assets/custom-ca')
|
||||
);
|
||||
|
||||
['register', 'publish', 'default'].forEach(function(p) {
|
||||
it('should read the CA file', function() {
|
||||
var config = require('../lib/Config')
|
||||
.read(path.resolve('test/assets/custom-ca'));
|
||||
|
||||
['register', 'publish', 'default'].forEach(function (p) {
|
||||
assertCAContents(config.ca[p], 'config.ca.' + p);
|
||||
});
|
||||
|
||||
assert(
|
||||
Array.isArray(config.ca.search),
|
||||
'ca property search should be an array'
|
||||
);
|
||||
assert(Array.isArray(config.ca.search),
|
||||
'ca property search should be an array');
|
||||
|
||||
config.ca.search.forEach(function(c, i) {
|
||||
assertCAContents(c, 'config.ca.search[' + i + ']');
|
||||
@@ -142,26 +138,23 @@ describe('NPM Config on package.json', function() {
|
||||
});
|
||||
|
||||
it('should backward-support certificate inside .bowerrc', function() {
|
||||
var config = require('../lib/Config').read(
|
||||
path.resolve('test/assets/custom-ca-embed')
|
||||
);
|
||||
var config = require('../lib/Config')
|
||||
.read(path.resolve('test/assets/custom-ca-embed'));
|
||||
|
||||
['register', 'publish', 'default'].forEach(function(p) {
|
||||
['register', 'publish', 'default'].forEach(function (p) {
|
||||
assertCAContents(config.ca[p], 'config.ca.' + p);
|
||||
});
|
||||
|
||||
assert(
|
||||
Array.isArray(config.ca.search),
|
||||
'ca property search should be an array'
|
||||
);
|
||||
assert(Array.isArray(config.ca.search),
|
||||
'ca property search should be an array');
|
||||
config.ca.search.forEach(function(c, i) {
|
||||
assertCAContents(c, 'config.ca.search[' + i + ']');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('setting ENV variables', function() {
|
||||
beforeEach(function() {
|
||||
describe('setting ENV variables', function () {
|
||||
beforeEach(function () {
|
||||
delete process.env.no_proxy;
|
||||
delete process.env.http_proxy;
|
||||
delete process.env.https_proxy;
|
||||
@@ -170,58 +163,40 @@ describe('NPM Config on package.json', function() {
|
||||
delete process.env.HTTPS_PROXY;
|
||||
});
|
||||
|
||||
it('sets env variables', function() {
|
||||
it('sets env variables', function () {
|
||||
require('../lib/Config').read('test/assets/env-variables');
|
||||
|
||||
assert.equal(process.env.HTTP_PROXY, 'http://HTTP_PROXY');
|
||||
assert.equal(process.env.HTTPS_PROXY, 'http://HTTPS_PROXY');
|
||||
assert.equal(process.env.NO_PROXY, 'google.com');
|
||||
|
||||
// On windows env is case insensitive
|
||||
if (process.platform === 'win32') {
|
||||
assert.equal(process.env.http_proxy, 'http://HTTP_PROXY');
|
||||
assert.equal(process.env.https_proxy, 'http://HTTPS_PROXY');
|
||||
assert.equal(process.env.no_proxy, 'google.com');
|
||||
} else {
|
||||
assert.equal(process.env.http_proxy, undefined);
|
||||
assert.equal(process.env.https_proxy, undefined);
|
||||
assert.equal(process.env.no_proxy, undefined);
|
||||
}
|
||||
assert.equal(process.env.http_proxy, undefined);
|
||||
assert.equal(process.env.https_proxy, undefined);
|
||||
assert.equal(process.env.no_proxy, undefined);
|
||||
});
|
||||
|
||||
it('restores env variables', function() {
|
||||
process.env.http_proxy = 'd';
|
||||
process.env.https_proxy = 'e';
|
||||
process.env.no_proxy = 'f';
|
||||
it('restores env variables', function () {
|
||||
process.env.HTTP_PROXY = 'a';
|
||||
process.env.HTTPS_PROXY = 'b';
|
||||
process.env.NO_PROXY = 'c';
|
||||
process.env.http_proxy = 'd';
|
||||
process.env.https_proxy = 'e';
|
||||
process.env.no_proxy = 'f';
|
||||
|
||||
var config = require('../lib/Config')
|
||||
.create('test/assets/env-variables')
|
||||
.load();
|
||||
var config = require('../lib/Config').create('test/assets/env-variables').load();
|
||||
config.restore();
|
||||
|
||||
assert.equal(process.env.HTTP_PROXY, 'a');
|
||||
assert.equal(process.env.HTTPS_PROXY, 'b');
|
||||
assert.equal(process.env.NO_PROXY, 'c');
|
||||
|
||||
// On windows precedence for restoring is for capital case
|
||||
if (process.platform === 'win32') {
|
||||
assert.equal(process.env.http_proxy, 'a');
|
||||
assert.equal(process.env.https_proxy, 'b');
|
||||
assert.equal(process.env.no_proxy, 'c');
|
||||
} else {
|
||||
assert.equal(process.env.http_proxy, 'd');
|
||||
assert.equal(process.env.https_proxy, 'e');
|
||||
assert.equal(process.env.no_proxy, 'f');
|
||||
}
|
||||
assert.equal(process.env.http_proxy, 'd');
|
||||
assert.equal(process.env.https_proxy, 'e');
|
||||
assert.equal(process.env.no_proxy, 'f');
|
||||
});
|
||||
|
||||
it('restores env variables if they are undefined', function() {
|
||||
var config = require('../lib/Config')
|
||||
.create('test/assets/env-variables')
|
||||
.load();
|
||||
it('restores env variables if they are undefined', function () {
|
||||
var config = require('../lib/Config').create('test/assets/env-variables').load();
|
||||
config.restore();
|
||||
|
||||
assert.equal(process.env.HTTP_PROXY, undefined);
|
||||
@@ -233,7 +208,7 @@ describe('NPM Config on package.json', function() {
|
||||
assert.equal(process.env.no_proxy, undefined);
|
||||
});
|
||||
|
||||
it('allows for overriding options', function() {
|
||||
it('allows for overriding options', function () {
|
||||
require('../lib/Config').read('test/assets/env-variables', {
|
||||
httpsProxy: 'http://other-proxy.local'
|
||||
});
|
||||
@@ -245,33 +220,25 @@ describe('NPM Config on package.json', function() {
|
||||
});
|
||||
|
||||
describe('Allow ${ENV} variables in .bowerrc', function() {
|
||||
|
||||
it('sets values from process.env', function() {
|
||||
process.env._BOWERRC_MY_PACKAGES = 'a';
|
||||
process.env._BOWERRC_MY_TMP = '/tmp/b';
|
||||
process.env._BOWERRC_MY_USER = 'username';
|
||||
process.env._BOWERRC_MY_PASS = 'password';
|
||||
|
||||
var config = require('../lib/Config').read(
|
||||
'test/assets/env-variables-values'
|
||||
);
|
||||
var config = require('../lib/Config').read('test/assets/env-variables-values');
|
||||
assert.equal('a', config.storage.packages);
|
||||
assert.equal(path.resolve('/tmp/b'), config.tmp);
|
||||
assert.equal('username:password', config.storage.registry.search[0]);
|
||||
assert.equal('/tmp/b', config.tmp);
|
||||
assert.equal('${_myshellvar}', config.scripts.postinstall);
|
||||
});
|
||||
});
|
||||
|
||||
describe('untildify paths in .bowerrc', function() {
|
||||
|
||||
it('resolve ~/ in .bowerrc', function() {
|
||||
var config = require('../lib/Config').read(
|
||||
'test/assets/env-variables-values'
|
||||
);
|
||||
var config = require('../lib/Config').read('test/assets/env-variables-values');
|
||||
var untildify = require('untildify');
|
||||
|
||||
assert.equal(
|
||||
untildify('~/.bower-test/registry'),
|
||||
config.storage.registry.register
|
||||
);
|
||||
assert.equal(untildify('~/.bower-test/registry') , config.storage.registry);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
describe('util', function() {
|
||||
describe('util', function () {
|
||||
require('./rc');
|
||||
});
|
||||
});
|
||||
@@ -29,6 +29,7 @@ describe('rc', function() {
|
||||
'.bowerrc/foo': {
|
||||
key: 'bar'
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it('correctly reads .bowerrc files', function() {
|
||||
@@ -59,21 +60,22 @@ describe('rc', function() {
|
||||
expect(config.key2).to.eql(undefined);
|
||||
});
|
||||
|
||||
it('loads the .bowerrc file from the cwd specified on the command line', function() {
|
||||
it('loads the .bowerrc file from the cwd specified on the command line', function(){
|
||||
var argv = {
|
||||
config: {
|
||||
cwd: tempDir.path + '/other_dir/'
|
||||
'config': {
|
||||
'cwd': tempDir.path + '/other_dir/'
|
||||
}
|
||||
};
|
||||
|
||||
var config = rc('bower', tempDir.path, argv);
|
||||
|
||||
expect(config.key).to.eql('othervalue');
|
||||
|
||||
});
|
||||
|
||||
it('throws an easy to understand error if .bowerrc is a dir', function() {
|
||||
// Gotta wrap this to catch the error
|
||||
var config = function() {
|
||||
var config = function () {
|
||||
rc('bower', tempDirBowerrc.path);
|
||||
};
|
||||
|
||||
|
||||
61
packages/bower-endpoint-parser/.jshintrc
Normal file
61
packages/bower-endpoint-parser/.jshintrc
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"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,
|
||||
"camelcase": true,
|
||||
|
||||
"asi": false,
|
||||
"boss": true,
|
||||
"debug": false,
|
||||
"eqnull": true,
|
||||
"esnext": false,
|
||||
"evil": false,
|
||||
"expr": true,
|
||||
"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
|
||||
}
|
||||
4
packages/bower-endpoint-parser/.travis.yml
Normal file
4
packages/bower-endpoint-parser/.travis.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.10"
|
||||
- "0.8"
|
||||
@@ -1,4 +1,4 @@
|
||||
# bower-endpoint-parser
|
||||
# endpoint-parser [](http://travis-ci.org/bower/endpoint-parser)
|
||||
|
||||
Little module that helps with endpoints parsing.
|
||||
|
||||
|
||||
@@ -59,10 +59,10 @@ function json2decomposed(key, value) {
|
||||
// If # was found, the source was specified
|
||||
if (split.length > 1) {
|
||||
endpoint += (split[0] || key) + '#' + split[1];
|
||||
// Check if value looks like a source
|
||||
// Check if value looks like a source
|
||||
} else if (isSource(value)) {
|
||||
endpoint += value + '#*';
|
||||
// Otherwise use the key as the source
|
||||
// Otherwise use the key as the source
|
||||
} else {
|
||||
endpoint += key + '#' + split[0];
|
||||
}
|
||||
@@ -85,7 +85,7 @@ function decomposed2json(decEndpoint) {
|
||||
}
|
||||
|
||||
// Add source only if different than the name
|
||||
if (source !== name) {
|
||||
if (source !== name) {
|
||||
value += source;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ function decomposed2json(decEndpoint) {
|
||||
value += target;
|
||||
}
|
||||
}
|
||||
// Otherwise append only if not a wildcard or source does not look like a source
|
||||
// Otherwise append only if not a wildcard or source does not look like a source
|
||||
} else if (!isWildcard(target) || !isSource(source)) {
|
||||
value += '#' + (target || '*');
|
||||
}
|
||||
@@ -119,7 +119,7 @@ function isWildcard(target) {
|
||||
}
|
||||
|
||||
function isSource(value) {
|
||||
return /[\/\\@]/.test(value);
|
||||
return (/[\/\\@]/).test(value);
|
||||
}
|
||||
|
||||
module.exports.decompose = decompose;
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
"url": "https://github.com/bower/endpoint-parser/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"repository": "https://github.com/bower/bower/tree/master/packages/bower-endpoint-parser",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/bower/endpoint-parser.git"
|
||||
},
|
||||
"main": "index.js",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expect.js": "~0.2.0",
|
||||
"mocha": "^3.5.3",
|
||||
"mocha": "~1.12.0",
|
||||
"mout": "~0.9.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -3,68 +3,29 @@ var lang = require('mout/lang');
|
||||
var object = require('mout/object');
|
||||
var endpointParser = require('../');
|
||||
|
||||
describe('endpoint-parser', function() {
|
||||
describe('.decompose', function() {
|
||||
it('should decompose endpoints correctly', function() {
|
||||
describe('endpoint-parser', function () {
|
||||
describe('.decompose', function () {
|
||||
it('should decompose endpoints correctly', function () {
|
||||
var suite = {
|
||||
'jquery#~2.0.0': {
|
||||
name: '',
|
||||
source: 'jquery',
|
||||
target: '~2.0.0'
|
||||
},
|
||||
'jquery#~2.0.0': { name: '', source: 'jquery', target: '~2.0.0' },
|
||||
'jquery#*': { name: '', source: 'jquery', target: '*' },
|
||||
'jquery#latest': { name: '', source: 'jquery', target: '*' },
|
||||
'jquery#3dc50c62fe2d2d01afc58e7ad42236a35acff4d8': {
|
||||
name: '',
|
||||
source: 'jquery',
|
||||
target: '3dc50c62fe2d2d01afc58e7ad42236a35acff4d8'
|
||||
},
|
||||
'jquery#master': {
|
||||
name: '',
|
||||
source: 'jquery',
|
||||
target: 'master'
|
||||
},
|
||||
'backbone=backbone-amd#~1.0.0': {
|
||||
name: 'backbone',
|
||||
source: 'backbone-amd',
|
||||
target: '~1.0.0'
|
||||
},
|
||||
'backbone=backbone-amd#latest': {
|
||||
name: 'backbone',
|
||||
source: 'backbone-amd',
|
||||
target: '*'
|
||||
},
|
||||
'backbone=backbone-amd#*': {
|
||||
name: 'backbone',
|
||||
source: 'backbone-amd',
|
||||
target: '*'
|
||||
},
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip': {
|
||||
name: '',
|
||||
source:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip',
|
||||
target: '*'
|
||||
},
|
||||
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip': {
|
||||
name: 'bootstrap',
|
||||
source:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip',
|
||||
target: '*'
|
||||
},
|
||||
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip#latest': {
|
||||
name: 'bootstrap',
|
||||
source:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip',
|
||||
target: '*'
|
||||
}
|
||||
'jquery#3dc50c62fe2d2d01afc58e7ad42236a35acff4d8': { name: '', source: 'jquery', target: '3dc50c62fe2d2d01afc58e7ad42236a35acff4d8' },
|
||||
'jquery#master': { name: '', source: 'jquery', target: 'master' },
|
||||
'backbone=backbone-amd#~1.0.0': { name: 'backbone', source: 'backbone-amd', target: '~1.0.0' },
|
||||
'backbone=backbone-amd#latest': { name: 'backbone', source: 'backbone-amd', target: '*' },
|
||||
'backbone=backbone-amd#*': { name: 'backbone', source: 'backbone-amd', target: '*' },
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip': { name: '', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' },
|
||||
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip': { name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' },
|
||||
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip#latest': { name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' }
|
||||
};
|
||||
|
||||
object.forOwn(suite, function(decEndpoint, endpoint) {
|
||||
object.forOwn(suite, function (decEndpoint, endpoint) {
|
||||
expect(endpointParser.decompose(endpoint)).to.eql(decEndpoint);
|
||||
});
|
||||
});
|
||||
|
||||
it('should trim sources and targets', function() {
|
||||
it('should trim sources and targets', function () {
|
||||
var decEndpoint = endpointParser.decompose('foo= source # ~1.0.2 ');
|
||||
expect(decEndpoint.source).to.equal('source');
|
||||
expect(decEndpoint.target).to.equal('~1.0.2');
|
||||
@@ -79,135 +40,77 @@ describe('endpoint-parser', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.compose', function() {
|
||||
it('should compose endpoints correctly', function() {
|
||||
describe('.compose', function () {
|
||||
it('should compose endpoints correctly', function () {
|
||||
var suite = {
|
||||
'jquery#~2.0.0': {
|
||||
name: '',
|
||||
source: 'jquery',
|
||||
target: '~2.0.0'
|
||||
},
|
||||
jquery: [
|
||||
{ name: '', source: 'jquery', target: '*' },
|
||||
{ name: '', source: 'jquery', target: 'latest' },
|
||||
{ name: '', source: 'jquery', target: '' }
|
||||
],
|
||||
'jquery#3dc50c62fe2d2d01afc58e7ad42236a35acff4d8': {
|
||||
name: '',
|
||||
source: 'jquery',
|
||||
target: '3dc50c62fe2d2d01afc58e7ad42236a35acff4d8'
|
||||
},
|
||||
'jquery#master': {
|
||||
name: '',
|
||||
source: 'jquery',
|
||||
target: 'master'
|
||||
},
|
||||
'backbone=backbone-amd#~1.0.0': {
|
||||
name: 'backbone',
|
||||
source: 'backbone-amd',
|
||||
target: '~1.0.0'
|
||||
},
|
||||
'backbone=backbone-amd': [
|
||||
{ name: 'backbone', source: 'backbone-amd', target: '*' },
|
||||
{ name: 'backbone', source: 'backbone-amd', target: '*' },
|
||||
{ name: 'backbone', source: 'backbone-amd', target: '' }
|
||||
],
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip': {
|
||||
name: '',
|
||||
source:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip',
|
||||
target: '*'
|
||||
},
|
||||
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip': {
|
||||
name: 'bootstrap',
|
||||
source:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip',
|
||||
target: '*'
|
||||
}
|
||||
'jquery#~2.0.0': { name: '', source: 'jquery', target: '~2.0.0' },
|
||||
'jquery': [{ name: '', source: 'jquery', target: '*' }, { name: '', source: 'jquery', target: 'latest' }, { name: '', source: 'jquery', target: '' }],
|
||||
'jquery#3dc50c62fe2d2d01afc58e7ad42236a35acff4d8': { name: '', source: 'jquery', target: '3dc50c62fe2d2d01afc58e7ad42236a35acff4d8' },
|
||||
'jquery#master': { name: '', source: 'jquery', target: 'master' },
|
||||
'backbone=backbone-amd#~1.0.0': { name: 'backbone', source: 'backbone-amd', target: '~1.0.0' },
|
||||
'backbone=backbone-amd': [{ name: 'backbone', source: 'backbone-amd', target: '*' }, { name: 'backbone', source: 'backbone-amd', target: '*' }, { name: 'backbone', source: 'backbone-amd', target: '' }],
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap.zip': { name: '', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' },
|
||||
'bootstrap=http://twitter.github.io/bootstrap/assets/bootstrap.zip': { name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap.zip', target: '*' }
|
||||
};
|
||||
|
||||
object.forOwn(suite, function(decEndpoints, endpoint) {
|
||||
object.forOwn(suite, function (decEndpoints, endpoint) {
|
||||
decEndpoints = lang.toArray(decEndpoints);
|
||||
decEndpoints.forEach(function(decEndpoint) {
|
||||
expect(endpointParser.compose(decEndpoint)).to.equal(
|
||||
endpoint
|
||||
);
|
||||
decEndpoints.forEach(function (decEndpoint) {
|
||||
expect(endpointParser.compose(decEndpoint)).to.equal(endpoint);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should trim values', function() {
|
||||
expect(
|
||||
endpointParser.compose({
|
||||
name: ' foo ',
|
||||
source: ' bar ',
|
||||
target: ' ~1.0.2 '
|
||||
})
|
||||
).to.equal('foo=bar#~1.0.2');
|
||||
it('should trim values', function () {
|
||||
expect(endpointParser.compose({
|
||||
name: ' foo ',
|
||||
source: ' bar ',
|
||||
target: ' ~1.0.2 '
|
||||
})).to.equal('foo=bar#~1.0.2');
|
||||
|
||||
expect(
|
||||
endpointParser.compose({
|
||||
name: ' foo ',
|
||||
source: ' foo ',
|
||||
target: ' ~1.0.2 '
|
||||
})
|
||||
).to.equal('foo=foo#~1.0.2');
|
||||
expect(endpointParser.compose({
|
||||
name: ' foo ',
|
||||
source: ' foo ',
|
||||
target: ' ~1.0.2 '
|
||||
})).to.equal('foo=foo#~1.0.2');
|
||||
|
||||
expect(
|
||||
endpointParser.compose({
|
||||
name: ' foo ',
|
||||
source: ' foo ',
|
||||
target: ' * '
|
||||
})
|
||||
).to.equal('foo=foo');
|
||||
expect(endpointParser.compose({
|
||||
name: ' foo ',
|
||||
source: ' foo ',
|
||||
target: ' * '
|
||||
})).to.equal('foo=foo');
|
||||
|
||||
expect(
|
||||
endpointParser.compose({
|
||||
name: ' foo ',
|
||||
source: ' foo ',
|
||||
target: ' * '
|
||||
})
|
||||
).to.equal('foo=foo');
|
||||
expect(endpointParser.compose({
|
||||
name: ' foo ',
|
||||
source: ' foo ',
|
||||
target: ' * '
|
||||
})).to.equal('foo=foo');
|
||||
|
||||
expect(
|
||||
endpointParser.compose({
|
||||
name: ' ',
|
||||
source: ' foo ',
|
||||
target: ''
|
||||
})
|
||||
).to.equal('foo');
|
||||
expect(endpointParser.compose({
|
||||
name: ' ',
|
||||
source: ' foo ',
|
||||
target: ''
|
||||
})).to.equal('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.json2decomposed', function() {
|
||||
describe('.json2decomposed', function () {
|
||||
var expected = [
|
||||
{ name: 'jquery', source: 'jquery', target: '~1.9.1' },
|
||||
{ name: 'foo', source: 'foo', target: '*' },
|
||||
{ name: 'bar', source: 'bar', target: '*' },
|
||||
{ name: 'baz', source: 'baz', target: '~0.2.0' },
|
||||
{ name: 'backbone', source: 'backbone-amd', target: '~1.0.0' },
|
||||
{
|
||||
name: 'backbone2',
|
||||
source: 'backbone=backbone-amd',
|
||||
target: '~1.0.0'
|
||||
},
|
||||
{
|
||||
name: 'bootstrap',
|
||||
source: 'http://twitter.github.io/bootstrap/assets/bootstrap',
|
||||
target: '*'
|
||||
},
|
||||
{
|
||||
name: 'bootstrap2',
|
||||
source: 'http://twitter.github.io/bootstrap/assets/bootstrap',
|
||||
target: '*'
|
||||
},
|
||||
{ name: 'backbone2', source: 'backbone=backbone-amd', target: '~1.0.0' },
|
||||
{ name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' },
|
||||
{ name: 'bootstrap2', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' },
|
||||
{ name: 'ssh', source: 'git@example.com', target: '*' },
|
||||
{ name: 'git', source: 'git://example.com', target: '*' },
|
||||
{ name: 'path', source: '/foo', target: '*' },
|
||||
{ name: 'winpath', source: 'c:\\foo', target: '*' }
|
||||
];
|
||||
|
||||
it('should decompose json endpoints correctly', function() {
|
||||
it('should decompose json endpoints correctly', function () {
|
||||
var dependencies = {
|
||||
jquery: '~1.9.1',
|
||||
foo: 'latest',
|
||||
@@ -215,10 +118,8 @@ describe('endpoint-parser', function() {
|
||||
baz: '#~0.2.0',
|
||||
backbone: 'backbone-amd#~1.0.0',
|
||||
backbone2: 'backbone=backbone-amd#~1.0.0',
|
||||
bootstrap:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap',
|
||||
bootstrap2:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap#*',
|
||||
bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap',
|
||||
bootstrap2: 'http://twitter.github.io/bootstrap/assets/bootstrap#*',
|
||||
ssh: 'git@example.com',
|
||||
git: 'git://example.com',
|
||||
path: '/foo',
|
||||
@@ -226,15 +127,13 @@ describe('endpoint-parser', function() {
|
||||
};
|
||||
var x = 0;
|
||||
|
||||
object.forOwn(dependencies, function(value, key) {
|
||||
expect(endpointParser.json2decomposed(key, value)).to.eql(
|
||||
expected[x]
|
||||
);
|
||||
object.forOwn(dependencies, function (value, key) {
|
||||
expect(endpointParser.json2decomposed(key, value)).to.eql(expected[x]);
|
||||
x += 1;
|
||||
});
|
||||
});
|
||||
|
||||
it('should trim values', function() {
|
||||
it('should trim values', function () {
|
||||
var dependencies = {
|
||||
' jquery ': ' ~1.9.1 ',
|
||||
' foo ': ' latest ',
|
||||
@@ -242,10 +141,8 @@ describe('endpoint-parser', function() {
|
||||
' baz ': '# ~0.2.0 ',
|
||||
' backbone ': ' backbone-amd#~1.0.0 ',
|
||||
' backbone2 ': ' backbone=backbone-amd # ~1.0.0 ',
|
||||
' bootstrap ':
|
||||
' http://twitter.github.io/bootstrap/assets/bootstrap',
|
||||
' bootstrap2 ':
|
||||
' http://twitter.github.io/bootstrap/assets/bootstrap # *',
|
||||
' bootstrap ': ' http://twitter.github.io/bootstrap/assets/bootstrap',
|
||||
' bootstrap2 ': ' http://twitter.github.io/bootstrap/assets/bootstrap # *',
|
||||
' ssh ': ' git@example.com ',
|
||||
' git ': ' git://example.com ',
|
||||
' path ': ' /foo ',
|
||||
@@ -253,15 +150,13 @@ describe('endpoint-parser', function() {
|
||||
};
|
||||
var x = 0;
|
||||
|
||||
object.forOwn(dependencies, function(value, key) {
|
||||
expect(endpointParser.json2decomposed(key, value)).to.eql(
|
||||
expected[x]
|
||||
);
|
||||
object.forOwn(dependencies, function (value, key) {
|
||||
expect(endpointParser.json2decomposed(key, value)).to.eql(expected[x]);
|
||||
x += 1;
|
||||
});
|
||||
});
|
||||
|
||||
it('should error out if key is not specified', function() {
|
||||
it('should error out if key is not specified', function () {
|
||||
try {
|
||||
endpointParser.json2decomposed(null);
|
||||
throw new Error('Should have failed');
|
||||
@@ -280,7 +175,7 @@ describe('endpoint-parser', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.decomposed2json', function() {
|
||||
describe('.decomposed2json', function () {
|
||||
var expected = [
|
||||
{ jquery: '~1.9.1' },
|
||||
{ foo: '*' },
|
||||
@@ -290,19 +185,15 @@ describe('endpoint-parser', function() {
|
||||
{ jqueryy: 'jquery-x#*' },
|
||||
{ jqueryy: 'jquery-x#*' },
|
||||
{ backbone: 'backbone-amd#~1.0.0' },
|
||||
{ backbone: 'backbone=backbone-amd#~1.0.0' },
|
||||
{
|
||||
bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap'
|
||||
},
|
||||
{
|
||||
bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap'
|
||||
},
|
||||
{ backbone : 'backbone=backbone-amd#~1.0.0' },
|
||||
{ bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap' },
|
||||
{ bootstrap: 'http://twitter.github.io/bootstrap/assets/bootstrap' },
|
||||
{ ssh: 'git@example.com' },
|
||||
{ git: 'git://example.com' },
|
||||
{ ckeditor: '#full/4.3.3' }
|
||||
];
|
||||
|
||||
it('should compose endpoints to json correctly', function() {
|
||||
it('should compose endpoints to json correctly', function () {
|
||||
var decEndpoints = [
|
||||
{ name: 'jquery', source: 'jquery', target: '~1.9.1' },
|
||||
{ name: 'foo', source: 'foo', target: 'latest' },
|
||||
@@ -312,38 +203,22 @@ describe('endpoint-parser', function() {
|
||||
{ name: 'jqueryy', source: 'jquery-x', target: '' },
|
||||
{ name: 'jqueryy', source: 'jquery-x', target: '*' },
|
||||
{ name: 'backbone', source: 'backbone-amd', target: '~1.0.0' },
|
||||
{
|
||||
name: 'backbone',
|
||||
source: 'backbone=backbone-amd',
|
||||
target: '~1.0.0'
|
||||
},
|
||||
{
|
||||
name: 'bootstrap',
|
||||
source:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap',
|
||||
target: ''
|
||||
},
|
||||
{
|
||||
name: 'bootstrap',
|
||||
source:
|
||||
'http://twitter.github.io/bootstrap/assets/bootstrap',
|
||||
target: '*'
|
||||
},
|
||||
{ name: 'backbone', source: 'backbone=backbone-amd', target: '~1.0.0' },
|
||||
{ name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '' },
|
||||
{ name: 'bootstrap', source: 'http://twitter.github.io/bootstrap/assets/bootstrap', target: '*' },
|
||||
{ name: 'ssh', source: 'git@example.com', target: '*' },
|
||||
{ name: 'git', source: 'git://example.com', target: '*' },
|
||||
{ name: 'ckeditor', source: 'ckeditor', target: 'full/4.3.3' }
|
||||
];
|
||||
var x = 0;
|
||||
|
||||
decEndpoints.forEach(function(decEndpoint) {
|
||||
expect(endpointParser.decomposed2json(decEndpoint)).to.eql(
|
||||
expected[x]
|
||||
);
|
||||
decEndpoints.forEach(function (decEndpoint) {
|
||||
expect(endpointParser.decomposed2json(decEndpoint)).to.eql(expected[x]);
|
||||
x += 1;
|
||||
});
|
||||
});
|
||||
|
||||
it('should trim values', function() {
|
||||
it('should trim values', function () {
|
||||
var decEndpoints = [
|
||||
{ name: ' jquery ', source: ' jquery ', target: ' ~1.9.1 ' },
|
||||
{ name: 'foo', source: ' foo', target: ' latest ' },
|
||||
@@ -352,48 +227,24 @@ describe('endpoint-parser', function() {
|
||||
{ name: ' jqueryx ', source: ' jquery ', target: ' ~1.9.1 ' },
|
||||
{ name: ' jqueryy ', source: ' jquery-x ', target: ' ' },
|
||||
{ name: ' jqueryy ', source: ' jquery-x ', target: ' * ' },
|
||||
{
|
||||
name: ' backbone ',
|
||||
source: ' backbone-amd ',
|
||||
target: ' ~1.0.0 '
|
||||
},
|
||||
{
|
||||
name: ' backbone ',
|
||||
source: ' backbone=backbone-amd ',
|
||||
target: ' ~1.0.0 '
|
||||
},
|
||||
{
|
||||
name: ' bootstrap ',
|
||||
source:
|
||||
' http://twitter.github.io/bootstrap/assets/bootstrap ',
|
||||
target: ' '
|
||||
},
|
||||
{
|
||||
name: ' bootstrap ',
|
||||
source:
|
||||
' http://twitter.github.io/bootstrap/assets/bootstrap ',
|
||||
target: ' * '
|
||||
},
|
||||
{ name: ' backbone ', source: ' backbone-amd ', target: ' ~1.0.0 ' },
|
||||
{ name: ' backbone ', source: ' backbone=backbone-amd ', target: ' ~1.0.0 ' },
|
||||
{ name: ' bootstrap ', source: ' http://twitter.github.io/bootstrap/assets/bootstrap ', target: ' ' },
|
||||
{ name: ' bootstrap ', source: ' http://twitter.github.io/bootstrap/assets/bootstrap ', target: ' * ' },
|
||||
{ name: ' ssh ', source: ' git@example.com ', target: ' * ' },
|
||||
{ name: ' git ', source: ' git://example.com ', target: ' * ' }
|
||||
];
|
||||
var x = 0;
|
||||
|
||||
decEndpoints.forEach(function(decEndpoint) {
|
||||
expect(endpointParser.decomposed2json(decEndpoint)).to.eql(
|
||||
expected[x]
|
||||
);
|
||||
decEndpoints.forEach(function (decEndpoint) {
|
||||
expect(endpointParser.decomposed2json(decEndpoint)).to.eql(expected[x]);
|
||||
x += 1;
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw an error if name is empty', function() {
|
||||
it('should throw an error if name is empty', function () {
|
||||
try {
|
||||
endpointParser.decomposed2json({
|
||||
name: '',
|
||||
source: 'jquery',
|
||||
target: '*'
|
||||
});
|
||||
endpointParser.decomposed2json({ name: '', source: 'jquery', target: '*' });
|
||||
throw new Error('Should have failed');
|
||||
} catch (e) {
|
||||
expect(e.code).to.equal('EINVEND');
|
||||
@@ -401,11 +252,7 @@ describe('endpoint-parser', function() {
|
||||
}
|
||||
|
||||
try {
|
||||
endpointParser.decomposed2json({
|
||||
name: ' ',
|
||||
source: 'jquery',
|
||||
target: '*'
|
||||
});
|
||||
endpointParser.decomposed2json({ name: ' ', source: 'jquery', target: '*' });
|
||||
throw new Error('Should have failed');
|
||||
} catch (e) {
|
||||
expect(e.code).to.equal('EINVEND');
|
||||
|
||||
48
packages/bower-json/.jshintrc
Normal file
48
packages/bower-json/.jshintrc
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"indent": 4,
|
||||
"node": true,
|
||||
"devel": true,
|
||||
"mocha": 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,
|
||||
"camelcase": 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,
|
||||
"shadow": false,
|
||||
"sub": false,
|
||||
"supernew": true,
|
||||
"validthis": false
|
||||
}
|
||||
10
packages/bower-json/.travis.yml
Normal file
10
packages/bower-json/.travis.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- '5'
|
||||
- '4'
|
||||
- '0.12'
|
||||
- '0.10'
|
||||
|
||||
script:
|
||||
- grunt travis
|
||||
@@ -1,15 +1,3 @@
|
||||
# 0.8.4
|
||||
|
||||
- Update deep-extend (security fix)
|
||||
|
||||
# 0.8.3
|
||||
|
||||
- Fix requires
|
||||
|
||||
# 0.8.2
|
||||
|
||||
- Drop dependency on meow (vendor ext-name)
|
||||
|
||||
# 0.8.1
|
||||
|
||||
- Revert strict name validations and allow @, spaces and slashes
|
||||
|
||||
60
packages/bower-json/Gruntfile.js
Normal file
60
packages/bower-json/Gruntfile.js
Normal file
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function (grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
|
||||
jshint: {
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
'lib/**/*.js',
|
||||
'test/**/*.js',
|
||||
'!test/reports/**/*'
|
||||
],
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
}
|
||||
},
|
||||
|
||||
simplemocha: {
|
||||
options: {
|
||||
reporter: 'spec'
|
||||
},
|
||||
full: { src: ['test/test.js'] },
|
||||
short: {
|
||||
options: {
|
||||
reporter: 'dot'
|
||||
},
|
||||
src: ['test/test.js']
|
||||
},
|
||||
build: {
|
||||
options: {
|
||||
reporter: 'tap'
|
||||
},
|
||||
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']
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Default task.
|
||||
grunt.registerTask('test', ['simplemocha:full']);
|
||||
grunt.registerTask('default', ['jshint', 'test']);
|
||||
grunt.registerTask('travis', ['jshint', 'exec:cover', 'exec:coveralls']);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2012-present Twitter and other contributors
|
||||
Copyright (c) 2016 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# bower-json
|
||||
# bower-json [](http://travis-ci.org/bower/json) [](https://coveralls.io/github/bower/json?branch=master)
|
||||
|
||||
Read `bower.json` files with semantics, normalisation, defaults and validation.
|
||||
|
||||
|
||||
@@ -14,14 +14,14 @@ function read(file, options, callback) {
|
||||
}
|
||||
|
||||
// Check if file is a directory
|
||||
fs.stat(file, function(err, stat) {
|
||||
fs.stat(file, function (err, stat) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// It's a directory, so we find the json inside it
|
||||
if (stat.isDirectory()) {
|
||||
return find(file, function(err, file) {
|
||||
return find(file, function (err, file) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -31,7 +31,7 @@ function read(file, options, callback) {
|
||||
}
|
||||
|
||||
// Otherwise read it
|
||||
fs.readFile(file, function(err, contents) {
|
||||
fs.readFile(file, function (err, contents) {
|
||||
var json;
|
||||
|
||||
if (err) {
|
||||
@@ -99,14 +99,11 @@ function readSync(file, options) {
|
||||
}
|
||||
|
||||
function parse(json, options) {
|
||||
options = deepExtend(
|
||||
{
|
||||
normalize: false,
|
||||
validate: true,
|
||||
clone: false
|
||||
},
|
||||
options || {}
|
||||
);
|
||||
options = deepExtend({
|
||||
normalize: false,
|
||||
validate: true,
|
||||
clone: false
|
||||
}, options || {});
|
||||
|
||||
// Clone
|
||||
if (options.clone) {
|
||||
@@ -140,9 +137,7 @@ function getIssues(json) {
|
||||
errors.push('No "name" property set');
|
||||
} else {
|
||||
if (!/^[a-zA-Z0-9_@][a-zA-Z0-9_@\.\- \/]*$/.test(json.name)) {
|
||||
errors.push(
|
||||
'Name must be lowercase, can contain digits, dots, dashes, "@" or spaces'
|
||||
);
|
||||
errors.push('Name must be lowercase, can contain digits, dots, dashes, "@" or spaces');
|
||||
}
|
||||
|
||||
if (json.name.length > 50) {
|
||||
@@ -150,9 +145,7 @@ function getIssues(json) {
|
||||
}
|
||||
|
||||
if (!/^[a-z0-9_][a-z0-9_\.\-]*$/.test(json.name)) {
|
||||
warnings.push(
|
||||
'The "name" is recommended to be lowercase, can contain digits, dots, dashes'
|
||||
);
|
||||
warnings.push('The "name" is recommended to be lowercase, can contain digits, dots, dashes');
|
||||
}
|
||||
|
||||
if (/^[\.-]/.test(json.name)) {
|
||||
@@ -165,9 +158,7 @@ function getIssues(json) {
|
||||
}
|
||||
|
||||
if (json.description && json.description.length > 140) {
|
||||
warnings.push(
|
||||
'The "description" is too long, the limit is 140 characters'
|
||||
);
|
||||
warnings.push('The "description" is too long, the limit is 140 characters');
|
||||
}
|
||||
|
||||
if (json.main !== undefined) {
|
||||
@@ -176,29 +167,21 @@ function getIssues(json) {
|
||||
main = [main];
|
||||
}
|
||||
if (!(main instanceof Array)) {
|
||||
errors.push(
|
||||
'The "main" field has to be either an Array or a String'
|
||||
);
|
||||
errors.push('The "main" field has to be either an Array or a String');
|
||||
} else {
|
||||
var ext2files = {};
|
||||
main.forEach(function(filename) {
|
||||
main.forEach(function (filename) {
|
||||
if (typeof filename !== 'string') {
|
||||
errors.push('The "main" Array has to contain only Strings');
|
||||
}
|
||||
if (/[*]/.test(filename)) {
|
||||
warnings.push(
|
||||
'The "main" field cannot contain globs (example: "*.js")'
|
||||
);
|
||||
warnings.push('The "main" field cannot contain globs (example: "*.js")');
|
||||
}
|
||||
if (/[.]min[.][^/]+$/.test(filename)) {
|
||||
warnings.push(
|
||||
'The "main" field cannot contain minified files'
|
||||
);
|
||||
warnings.push('The "main" field cannot contain minified files');
|
||||
}
|
||||
if (isAsset(filename)) {
|
||||
warnings.push(
|
||||
'The "main" field cannot contain font, image, audio, or video files'
|
||||
);
|
||||
warnings.push('The "main" field cannot contain font, image, audio, or video files');
|
||||
}
|
||||
var ext = path.extname(filename);
|
||||
if (ext.length >= 2) {
|
||||
@@ -209,15 +192,10 @@ function getIssues(json) {
|
||||
files.push(filename);
|
||||
}
|
||||
});
|
||||
Object.keys(ext2files).forEach(function(ext) {
|
||||
Object.keys(ext2files).forEach(function (ext) {
|
||||
var files = ext2files[ext];
|
||||
if (files.length > 1) {
|
||||
warnings.push(
|
||||
'The "main" field has to contain only 1 file per filetype; found multiple ' +
|
||||
ext +
|
||||
' files: ' +
|
||||
JSON.stringify(files)
|
||||
);
|
||||
warnings.push('The "main" field has to contain only 1 file per filetype; found multiple ' + ext + ' files: ' + JSON.stringify(files));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -256,15 +234,12 @@ function find(folder, files, callback) {
|
||||
}
|
||||
|
||||
if (!files.length) {
|
||||
err = createError(
|
||||
'None of ' + possibleJsons.join(', ') + ' were found in ' + folder,
|
||||
'ENOENT'
|
||||
);
|
||||
err = createError('None of ' + possibleJsons.join(', ') + ' were found in ' + folder, 'ENOENT');
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
file = path.resolve(path.join(folder, files[0]));
|
||||
fs.exists(file, function(exists) {
|
||||
fs.exists(file, function (exists) {
|
||||
if (!exists) {
|
||||
return find(folder, files.slice(1), callback);
|
||||
}
|
||||
@@ -275,7 +250,7 @@ function find(folder, files, callback) {
|
||||
|
||||
// If the file is component.json, check it it's a component(1) file
|
||||
// If it is, we ignore it and keep searching
|
||||
isComponent(file, function(is) {
|
||||
isComponent(file, function (is) {
|
||||
if (is) {
|
||||
return find(folder, files.slice(1), callback);
|
||||
}
|
||||
@@ -294,16 +269,14 @@ function findSync(folder, files) {
|
||||
}
|
||||
|
||||
if (!files.length) {
|
||||
return createError(
|
||||
'None of ' + possibleJsons.join(', ') + ' were found in ' + folder,
|
||||
'ENOENT'
|
||||
);
|
||||
return createError('None of ' + possibleJsons.join(', ') + ' were found in ' + folder, 'ENOENT');
|
||||
}
|
||||
|
||||
file = path.resolve(path.join(folder, files[0]));
|
||||
try {
|
||||
try{
|
||||
exists = fs.statSync(file);
|
||||
} catch (err) {
|
||||
}
|
||||
catch (err) {
|
||||
exists = false;
|
||||
}
|
||||
if (exists && exists.isFile()) {
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
var extName = require('../vendor/ext-name');
|
||||
var extName = require('ext-name');
|
||||
|
||||
function isAsset(filename) {
|
||||
var info = extName(filename);
|
||||
|
||||
return (
|
||||
info &&
|
||||
info.mime &&
|
||||
(/^((image)|(audio)|(video)|(font))\//.test(info.mime) ||
|
||||
/application\/((x[-]font[-])|(font[-]woff(\d?))|(vnd[.]ms[-]fontobject))/.test(
|
||||
info.mime
|
||||
))
|
||||
|
||||
return info && info.mime && (
|
||||
/^((image)|(audio)|(video)|(font))\//.test(info.mime) ||
|
||||
/application\/((x[-]font[-])|(font[-]woff(\d?))|(vnd[.]ms[-]fontobject))/.test(info.mime)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ var intersect = require('intersect');
|
||||
|
||||
// Function to check if a file is a component(1) file
|
||||
function isComponent(file, callback) {
|
||||
fs.readFile(file, function(err, contents) {
|
||||
fs.readFile(file, function (err, contents) {
|
||||
var json;
|
||||
var keys;
|
||||
var common;
|
||||
|
||||
19
packages/bower-json/lib/vendor/ext-name.js
vendored
19
packages/bower-json/lib/vendor/ext-name.js
vendored
@@ -1,19 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var endsWith = require('ends-with');
|
||||
var extList = require('ext-list');
|
||||
var sortKeysLength = require('sort-keys-length');
|
||||
|
||||
module.exports = function(str) {
|
||||
var obj = sortKeysLength.desc(extList());
|
||||
var ext = Object.keys(obj).filter(endsWith.bind(null, str));
|
||||
|
||||
if (!ext.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
ext: ext[0],
|
||||
mime: obj[ext[0]]
|
||||
};
|
||||
};
|
||||
@@ -1,30 +1,38 @@
|
||||
{
|
||||
"name": "bower-json",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.1",
|
||||
"description": "Read bower.json files with semantics, normalisation, defaults and validation",
|
||||
"author": "Twitter",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/bower/bower/tree/master/packages/bower-json",
|
||||
"repository": "bower/json",
|
||||
"main": "lib/json",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"deep-extend": "^0.5.1",
|
||||
"ends-with": "^0.2.0",
|
||||
"ext-list": "^2.0.0",
|
||||
"sort-keys-length": "^1.0.0",
|
||||
"deep-extend": "^0.4.0",
|
||||
"ext-name": "^3.0.0",
|
||||
"graceful-fs": "^4.1.3",
|
||||
"intersect": "^1.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coveralls": "^2.11.2",
|
||||
"expect.js": "^0.3.1",
|
||||
"mocha": "^3.5.3",
|
||||
"grunt": "^0.4.4",
|
||||
"grunt-cli": "^0.1.13",
|
||||
"grunt-contrib-jshint": "^0.11.2",
|
||||
"grunt-contrib-watch": "^0.6.1",
|
||||
"grunt-coveralls": "^1.0.0",
|
||||
"grunt-exec": "^0.4.6",
|
||||
"grunt-simple-mocha": "^0.4.0",
|
||||
"istanbul": "^0.3.5",
|
||||
"load-grunt-tasks": "^3.3.0",
|
||||
"mocha": "*",
|
||||
"request": "^2.64.0",
|
||||
"underscore.string": "^3.0.3"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
"test": "grunt test"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
|
||||
@@ -4,142 +4,114 @@ var _s = require('underscore.string');
|
||||
var bowerJson = require('../lib/json');
|
||||
var request = require('request');
|
||||
|
||||
describe('.find', function() {
|
||||
it('should find the bower.json file', function(done) {
|
||||
bowerJson.find(__dirname + '/pkg-bower-json', function(err, file) {
|
||||
describe('.find', function () {
|
||||
it('should find the bower.json file', function (done) {
|
||||
bowerJson.find(__dirname + '/pkg-bower-json', function (err, file) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-bower-json/bower.json')
|
||||
);
|
||||
expect(file).to.equal(path.resolve(__dirname + '/pkg-bower-json/bower.json'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fallback to the component.json file', function(done) {
|
||||
bowerJson.find(__dirname + '/pkg-component-json', function(err, file) {
|
||||
it('should fallback to the component.json file', function (done) {
|
||||
bowerJson.find(__dirname + '/pkg-component-json', function (err, file) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-component-json/component.json')
|
||||
);
|
||||
expect(file).to.equal(path.resolve(__dirname + '/pkg-component-json/component.json'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should not fallback to the component.json file if it's a component(1) file", function(done) {
|
||||
bowerJson.find(__dirname + '/pkg-component(1)-json', function(err) {
|
||||
it('should not fallback to the component.json file if it\'s a component(1) file', function (done) {
|
||||
bowerJson.find(__dirname + '/pkg-component(1)-json', function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.equal('ENOENT');
|
||||
expect(err.message).to.equal(
|
||||
'None of bower.json, component.json, .bower.json were found in ' +
|
||||
__dirname +
|
||||
'/pkg-component(1)-json'
|
||||
);
|
||||
expect(err.message).to.equal('None of bower.json, component.json, .bower.json were found in ' + __dirname + '/pkg-component(1)-json');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fallback to the .bower.json file', function(done) {
|
||||
bowerJson.find(__dirname + '/pkg-dot-bower-json', function(err, file) {
|
||||
it('should fallback to the .bower.json file', function (done) {
|
||||
bowerJson.find(__dirname + '/pkg-dot-bower-json', function (err, file) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-dot-bower-json/.bower.json')
|
||||
);
|
||||
expect(file).to.equal(path.resolve(__dirname + '/pkg-dot-bower-json/.bower.json'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if no component.json / bower.json / .bower.json is found', function(done) {
|
||||
bowerJson.find(__dirname, function(err) {
|
||||
it('should error if no component.json / bower.json / .bower.json is found', function (done) {
|
||||
bowerJson.find(__dirname, function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.equal('ENOENT');
|
||||
expect(err.message).to.equal(
|
||||
'None of bower.json, component.json, .bower.json were found in ' +
|
||||
__dirname
|
||||
);
|
||||
expect(err.message).to.equal('None of bower.json, component.json, .bower.json were found in ' + __dirname);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.findSync', function() {
|
||||
it('should find the bower.json file', function(done) {
|
||||
describe('.findSync', function () {
|
||||
|
||||
it('should find the bower.json file', function (done) {
|
||||
var file = bowerJson.findSync(__dirname + '/pkg-bower-json');
|
||||
|
||||
expect(file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-bower-json/bower.json')
|
||||
);
|
||||
expect(file).to.equal(path.resolve(__dirname + '/pkg-bower-json/bower.json'));
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fallback to the component.json file', function(done) {
|
||||
it('should fallback to the component.json file', function (done) {
|
||||
var file = bowerJson.findSync(__dirname + '/pkg-component-json');
|
||||
|
||||
expect(file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-component-json/component.json')
|
||||
);
|
||||
expect(file).to.equal(path.resolve(__dirname + '/pkg-component-json/component.json'));
|
||||
done();
|
||||
});
|
||||
|
||||
it('should fallback to the .bower.json file', function(done) {
|
||||
it('should fallback to the .bower.json file', function (done) {
|
||||
var file = bowerJson.findSync(__dirname + '/pkg-dot-bower-json');
|
||||
|
||||
expect(file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-dot-bower-json/.bower.json')
|
||||
);
|
||||
expect(file).to.equal(path.resolve(__dirname + '/pkg-dot-bower-json/.bower.json'));
|
||||
done();
|
||||
});
|
||||
|
||||
it('should error if no component.json / bower.json / .bower.json is found', function(done) {
|
||||
it('should error if no component.json / bower.json / .bower.json is found', function (done) {
|
||||
var err = bowerJson.findSync(__dirname);
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.equal('ENOENT');
|
||||
expect(err.message).to.equal(
|
||||
'None of bower.json, component.json, .bower.json were found in ' +
|
||||
__dirname
|
||||
);
|
||||
expect(err.message).to.equal('None of bower.json, component.json, .bower.json were found in ' + __dirname);
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('.read', function() {
|
||||
it('should give error if file does not exists', function(done) {
|
||||
bowerJson.read(__dirname + '/willneverexist', function(err) {
|
||||
describe('.read', function () {
|
||||
it('should give error if file does not exists', function (done) {
|
||||
bowerJson.read(__dirname + '/willneverexist', function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.equal('ENOENT');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should give error if when reading an invalid json', function(done) {
|
||||
bowerJson.read(
|
||||
__dirname + '/pkg-bower-json-malformed/bower.json',
|
||||
function(err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.equal('EMALFORMED');
|
||||
expect(err.file).to.equal(
|
||||
path.resolve(
|
||||
__dirname + '/pkg-bower-json-malformed/bower.json'
|
||||
)
|
||||
);
|
||||
done();
|
||||
}
|
||||
);
|
||||
it('should give error if when reading an invalid json', function (done) {
|
||||
bowerJson.read(__dirname + '/pkg-bower-json-malformed/bower.json', function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.equal('EMALFORMED');
|
||||
expect(err.file).to.equal(path.resolve(__dirname + '/pkg-bower-json-malformed/bower.json'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should read the file and give an object', function(done) {
|
||||
bowerJson.read(__dirname + '/pkg-bower-json/bower.json', function(
|
||||
err,
|
||||
json
|
||||
) {
|
||||
it('should read the file and give an object', function (done) {
|
||||
bowerJson.read(__dirname + '/pkg-bower-json/bower.json', function (err, json) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@@ -152,29 +124,20 @@ describe('.read', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should give the json file that was read', function(done) {
|
||||
bowerJson.read(__dirname + '/pkg-bower-json', function(
|
||||
err,
|
||||
json,
|
||||
file
|
||||
) {
|
||||
it('should give the json file that was read', function (done) {
|
||||
bowerJson.read(__dirname + '/pkg-bower-json', function (err, json, file) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-bower-json/bower.json')
|
||||
);
|
||||
|
||||
expect(file).to.equal(__dirname + '/pkg-bower-json/bower.json');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should find for a json file if a directory is given', function(done) {
|
||||
bowerJson.read(__dirname + '/pkg-component-json', function(
|
||||
err,
|
||||
json,
|
||||
file
|
||||
) {
|
||||
it('should find for a json file if a directory is given', function (done) {
|
||||
bowerJson.read(__dirname + '/pkg-component-json', function (err, json, file) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
@@ -182,84 +145,60 @@ describe('.read', function() {
|
||||
expect(json).to.be.an('object');
|
||||
expect(json.name).to.equal('some-pkg');
|
||||
expect(json.version).to.equal('0.0.0');
|
||||
expect(file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-component-json/component.json')
|
||||
);
|
||||
expect(file).to.equal(path.resolve(__dirname + '/pkg-component-json/component.json'));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate the returned object unless validate is false', function(done) {
|
||||
bowerJson.read(
|
||||
__dirname + '/pkg-bower-json-invalid/bower.json',
|
||||
function(err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.message).to.contain('name');
|
||||
expect(err.file).to.equal(
|
||||
path.resolve(
|
||||
__dirname + '/pkg-bower-json-invalid/bower.json'
|
||||
)
|
||||
);
|
||||
it('should validate the returned object unless validate is false', function (done) {
|
||||
bowerJson.read(__dirname + '/pkg-bower-json-invalid/bower.json', function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.message).to.contain('name');
|
||||
expect(err.file).to.equal(path.resolve(__dirname + '/pkg-bower-json-invalid/bower.json'));
|
||||
|
||||
bowerJson.read(
|
||||
__dirname + '/pkg-bower-json-invalid/bower.json',
|
||||
{ validate: false },
|
||||
function(err) {
|
||||
done(err);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
bowerJson.read(__dirname + '/pkg-bower-json-invalid/bower.json', { validate: false }, function (err) {
|
||||
done(err);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should normalize the returned object if normalize is true', function(done) {
|
||||
bowerJson.read(__dirname + '/pkg-bower-json/bower.json', function(
|
||||
err,
|
||||
json
|
||||
) {
|
||||
it('should normalize the returned object if normalize is true', function (done) {
|
||||
bowerJson.read(__dirname + '/pkg-bower-json/bower.json', function (err, json) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(json.main).to.equal('foo.js');
|
||||
|
||||
bowerJson.read(
|
||||
__dirname + '/pkg-bower-json/bower.json',
|
||||
{ normalize: true },
|
||||
function(err, json) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
expect(json.main).to.eql(['foo.js']);
|
||||
done();
|
||||
bowerJson.read(__dirname + '/pkg-bower-json/bower.json', { normalize: true }, function (err, json) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
);
|
||||
|
||||
expect(json.main).to.eql(['foo.js']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.readSync', function() {
|
||||
it('should give error if file does not exists', function(done) {
|
||||
describe('.readSync', function () {
|
||||
it('should give error if file does not exists', function (done) {
|
||||
var err = bowerJson.readSync(__dirname + '/willneverexist');
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.equal('ENOENT');
|
||||
done();
|
||||
});
|
||||
|
||||
it('should give error if when reading an invalid json', function(done) {
|
||||
var err = bowerJson.readSync(
|
||||
__dirname + '/pkg-bower-json-malformed/bower.json'
|
||||
);
|
||||
it('should give error if when reading an invalid json', function (done) {
|
||||
var err = bowerJson.readSync(__dirname + '/pkg-bower-json-malformed/bower.json');
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.equal('EMALFORMED');
|
||||
expect(err.file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-bower-json-malformed/bower.json')
|
||||
);
|
||||
expect(err.file).to.equal(path.resolve(__dirname + '/pkg-bower-json-malformed/bower.json'));
|
||||
done();
|
||||
});
|
||||
|
||||
it('should read the file and give an object', function(done) {
|
||||
it('should read the file and give an object', function (done) {
|
||||
var json = bowerJson.readSync(__dirname + '/pkg-bower-json/bower.json');
|
||||
|
||||
expect(json).to.be.an('object');
|
||||
@@ -269,7 +208,7 @@ describe('.readSync', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should find for a json file if a directory is given', function(done) {
|
||||
it('should find for a json file if a directory is given', function (done) {
|
||||
var json = bowerJson.readSync(__dirname + '/pkg-component-json');
|
||||
|
||||
expect(json).to.be.an('object');
|
||||
@@ -278,39 +217,32 @@ describe('.readSync', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should validate the returned object unless validate is false', function(done) {
|
||||
var err = bowerJson.readSync(
|
||||
__dirname + '/pkg-bower-json-invalid/bower.json'
|
||||
);
|
||||
it('should validate the returned object unless validate is false', function (done) {
|
||||
var err = bowerJson.readSync(__dirname + '/pkg-bower-json-invalid/bower.json');
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.message).to.contain('name');
|
||||
expect(err.file).to.equal(
|
||||
path.resolve(__dirname + '/pkg-bower-json-invalid/bower.json')
|
||||
);
|
||||
expect(err.file).to.equal(path.resolve(__dirname + '/pkg-bower-json-invalid/bower.json'));
|
||||
|
||||
err = bowerJson.readSync(
|
||||
__dirname + '/pkg-bower-json-invalid/bower.json',
|
||||
{ validate: false }
|
||||
);
|
||||
err = bowerJson.readSync(__dirname + '/pkg-bower-json-invalid/bower.json', { validate: false });
|
||||
expect(err).to.not.be.an(Error);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should normalize the returned object if normalize is true', function(done) {
|
||||
var json = bowerJson.readSync(__dirname + '/pkg-bower-json/bower.json');
|
||||
it('should normalize the returned object if normalize is true', function (done) {
|
||||
var json = bowerJson.readSync(__dirname + '/pkg-bower-json/bower.json');
|
||||
expect(json.main).to.equal('foo.js');
|
||||
|
||||
json = bowerJson.readSync(__dirname + '/pkg-bower-json/bower.json', {
|
||||
normalize: true
|
||||
});
|
||||
json = bowerJson.readSync(__dirname + '/pkg-bower-json/bower.json', { normalize: true });
|
||||
|
||||
expect(json.main).to.eql(['foo.js']);
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('.parse', function() {
|
||||
it('should return the same object, unless clone is true', function() {
|
||||
describe('.parse', function () {
|
||||
it('should return the same object, unless clone is true', function () {
|
||||
var json = { name: 'foo' };
|
||||
|
||||
expect(bowerJson.parse(json)).to.equal(json);
|
||||
@@ -318,17 +250,17 @@ describe('.parse', function() {
|
||||
expect(bowerJson.parse(json, { clone: true })).to.eql(json);
|
||||
});
|
||||
|
||||
it('should validate the passed object, unless validate is false', function() {
|
||||
expect(function() {
|
||||
it('should validate the passed object, unless validate is false', function () {
|
||||
expect(function () {
|
||||
bowerJson.parse({});
|
||||
}).to.throwException(/name/);
|
||||
|
||||
expect(function() {
|
||||
expect(function () {
|
||||
bowerJson.parse({}, { validate: false });
|
||||
}).to.not.throwException();
|
||||
});
|
||||
|
||||
it('should not normalize the passed object unless normalize is true', function() {
|
||||
it('should not normalize the passed object unless normalize is true', function () {
|
||||
var json = { name: 'foo', main: 'foo.js' };
|
||||
|
||||
bowerJson.parse(json);
|
||||
@@ -339,24 +271,22 @@ describe('.parse', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getIssues', function() {
|
||||
it('should print no errors even for weird package names', function() {
|
||||
describe('.getIssues', function () {
|
||||
it('should print no errors even for weird package names', function () {
|
||||
var json = { name: '@gruNt/my dependency' };
|
||||
|
||||
expect(bowerJson.getIssues(json).errors).to.be.empty();
|
||||
});
|
||||
|
||||
it('should validate the name length', function() {
|
||||
var json = {
|
||||
name: 'a_123456789_123456789_123456789_123456789_123456789_z'
|
||||
};
|
||||
it('should validate the name length', function () {
|
||||
var json = { name: 'a_123456789_123456789_123456789_123456789_123456789_z' };
|
||||
|
||||
expect(bowerJson.getIssues(json).warnings).to.contain(
|
||||
'The "name" is too long, the limit is 50 characters'
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate the name is lowercase', function() {
|
||||
it('should validate the name is lowercase', function () {
|
||||
var json = { name: 'gruNt' };
|
||||
|
||||
expect(bowerJson.getIssues(json).warnings).to.contain(
|
||||
@@ -364,7 +294,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate the name starts with lowercase', function() {
|
||||
it('should validate the name starts with lowercase', function () {
|
||||
var json = { name: '-runt' };
|
||||
|
||||
expect(bowerJson.getIssues(json).warnings).to.contain(
|
||||
@@ -372,7 +302,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate the name starts with lowercase', function() {
|
||||
it('should validate the name starts with lowercase', function () {
|
||||
var json = { name: '.grunt' };
|
||||
|
||||
expect(bowerJson.getIssues(json).warnings).to.contain(
|
||||
@@ -380,7 +310,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate the name ends with lowercase', function() {
|
||||
it('should validate the name ends with lowercase', function () {
|
||||
var json = { name: 'grun-' };
|
||||
|
||||
expect(bowerJson.getIssues(json).warnings).to.contain(
|
||||
@@ -388,7 +318,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate the name ends with lowercase', function() {
|
||||
it('should validate the name ends with lowercase', function () {
|
||||
var json = { name: 'grun.' };
|
||||
|
||||
expect(bowerJson.getIssues(json).warnings).to.contain(
|
||||
@@ -396,13 +326,13 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate the name is valid', function() {
|
||||
it('should validate the name is valid', function () {
|
||||
var json = { name: 'gru.n-t' };
|
||||
|
||||
expect(bowerJson.getIssues(json).warnings).to.eql([]);
|
||||
});
|
||||
|
||||
it('should validate the description length', function() {
|
||||
it('should validate the description length', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
description: _s.repeat('æ', 141)
|
||||
@@ -413,7 +343,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate the description is valid', function() {
|
||||
it('should validate the description is valid', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
description: _s.repeat('æ', 140)
|
||||
@@ -422,7 +352,7 @@ describe('.getIssues', function() {
|
||||
expect(bowerJson.getIssues(json).warnings).to.eql([]);
|
||||
});
|
||||
|
||||
it('should validate that main does not contain globs', function() {
|
||||
it('should validate that main does not contain globs', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
main: ['js/*.js']
|
||||
@@ -433,7 +363,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate that main does not contain minified files', function() {
|
||||
it('should validate that main does not contain minified files', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
main: ['foo.min.css']
|
||||
@@ -444,7 +374,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate that main does not contain fonts', function() {
|
||||
it('should validate that main does not contain fonts', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
main: ['foo.woff']
|
||||
@@ -455,7 +385,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate that main does not contain images', function() {
|
||||
it('should validate that main does not contain images', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
main: ['foo.png']
|
||||
@@ -466,7 +396,7 @@ describe('.getIssues', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should validate that main does not contain multiple files of the same filetype', function() {
|
||||
it('should validate that main does not contain multiple files of the same filetype', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
main: ['foo.js', 'bar.js']
|
||||
@@ -478,35 +408,35 @@ describe('.getIssues', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.validate', function() {
|
||||
it('should validate the name property', function() {
|
||||
expect(function() {
|
||||
describe('.validate', function () {
|
||||
it('should validate the name property', function () {
|
||||
expect(function () {
|
||||
bowerJson.validate({});
|
||||
}).to.throwException(/name/);
|
||||
});
|
||||
|
||||
it('should validate the type of main', function() {
|
||||
it('should validate the type of main', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
main: {}
|
||||
};
|
||||
expect(function() {
|
||||
expect(function () {
|
||||
bowerJson.validate(json);
|
||||
}).to.throwException();
|
||||
});
|
||||
it('should validate the type of items of an Array main', function() {
|
||||
it('should validate the type of items of an Array main', function () {
|
||||
var json = {
|
||||
name: 'foo',
|
||||
main: [{}]
|
||||
};
|
||||
expect(function() {
|
||||
expect(function () {
|
||||
bowerJson.validate(json);
|
||||
}).to.throwException();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.normalize', function() {
|
||||
it('should normalize the main property', function() {
|
||||
describe('.normalize', function () {
|
||||
it('should normalize the main property', function () {
|
||||
var json = { name: 'foo', main: 'foo.js' };
|
||||
|
||||
bowerJson.normalize(json);
|
||||
@@ -514,35 +444,34 @@ describe('.normalize', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('packages from bower registry', function() {
|
||||
describe('packages from bower registry', function () {
|
||||
|
||||
var packageList,
|
||||
packageListUrl = 'http://registry.bower.io/packages';
|
||||
packageListUrl = 'http://bower.herokuapp.com/packages';
|
||||
|
||||
this.timeout(60000);
|
||||
|
||||
it('can be downloaded from online source ' + packageListUrl, function(
|
||||
done
|
||||
) {
|
||||
request(
|
||||
{
|
||||
url: packageListUrl,
|
||||
json: true
|
||||
},
|
||||
function(error, response, body) {
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
it('can be downloaded from online source ' + packageListUrl, function(done) {
|
||||
request({
|
||||
url: packageListUrl,
|
||||
json: true
|
||||
}, function(error, response, body) {
|
||||
|
||||
expect(body).to.be.an('array');
|
||||
expect(body).to.not.be.empty();
|
||||
packageList = body;
|
||||
|
||||
done();
|
||||
if(error) {
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
|
||||
expect(body).to.be.an('array');
|
||||
expect(body).to.not.be.empty();
|
||||
packageList = body;
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate each listed package', function(done) {
|
||||
it('should validate each listed package', function (done) {
|
||||
|
||||
expect(packageList).to.be.an('array');
|
||||
|
||||
var invalidPackageCount = 0;
|
||||
@@ -550,23 +479,19 @@ describe('packages from bower registry', function() {
|
||||
packageList.forEach(function(package) {
|
||||
try {
|
||||
bowerJson.validate(package);
|
||||
} catch (e) {
|
||||
} catch(e) {
|
||||
invalidPackageCount++;
|
||||
console.error(
|
||||
'validation of "' + package.name + '" failed: ' + e.message
|
||||
);
|
||||
console.error('validation of "' + package.name + '" failed: ' + e.message);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if (invalidPackageCount) {
|
||||
throw new Error(
|
||||
invalidPackageCount +
|
||||
'/' +
|
||||
packageList.length +
|
||||
' package names do not validate'
|
||||
);
|
||||
if(invalidPackageCount) {
|
||||
throw new Error(invalidPackageCount + '/' + packageList.length + ' package names do not validate');
|
||||
}
|
||||
|
||||
done();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
62
packages/bower-logger/.jshintrc
Normal file
62
packages/bower-logger/.jshintrc
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"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,
|
||||
"camelcase": 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
|
||||
}
|
||||
6
packages/bower-logger/.travis.yml
Normal file
6
packages/bower-logger/.travis.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- 'iojs'
|
||||
- '0.12'
|
||||
- '0.10'
|
||||
@@ -1,4 +1,4 @@
|
||||
# bower-logger
|
||||
# bower-logger [](http://travis-ci.org/bower/logger)
|
||||
|
||||
The logger used in the various architecture components of Bower.
|
||||
|
||||
|
||||
@@ -10,18 +10,18 @@ function Logger() {
|
||||
|
||||
util.inherits(Logger, EventEmitter);
|
||||
|
||||
Logger.prototype.intercept = function(fn) {
|
||||
Logger.prototype.intercept = function (fn) {
|
||||
this._interceptors.push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
Logger.prototype.emit = function() {
|
||||
Logger.prototype.emit = function () {
|
||||
var ret;
|
||||
var args = slice.call(arguments);
|
||||
|
||||
// Run interceptors before
|
||||
if (args[0] === 'log') {
|
||||
this._interceptors.forEach(function(interceptor) {
|
||||
this._interceptors.forEach(function (interceptor) {
|
||||
interceptor.apply(this, args.slice(1));
|
||||
});
|
||||
}
|
||||
@@ -29,27 +29,27 @@ Logger.prototype.emit = function() {
|
||||
ret = EventEmitter.prototype.emit.apply(this, args);
|
||||
|
||||
// Pipe
|
||||
this._piped.forEach(function(emitter) {
|
||||
this._piped.forEach(function (emitter) {
|
||||
emitter.emit.apply(emitter, args);
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
Logger.prototype.pipe = function(emitter) {
|
||||
Logger.prototype.pipe = function (emitter) {
|
||||
this._piped.push(emitter);
|
||||
|
||||
return emitter;
|
||||
};
|
||||
|
||||
Logger.prototype.geminate = function() {
|
||||
Logger.prototype.geminate = function () {
|
||||
var logger = new Logger();
|
||||
|
||||
logger.pipe(this);
|
||||
return logger;
|
||||
};
|
||||
|
||||
Logger.prototype.log = function(level, id, message, data) {
|
||||
Logger.prototype.log = function (level, id, message, data) {
|
||||
var log = {
|
||||
level: level,
|
||||
id: id,
|
||||
@@ -63,7 +63,7 @@ Logger.prototype.log = function(level, id, message, data) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Logger.prototype.prompt = function(prompts, callback) {
|
||||
Logger.prototype.prompt = function (prompts, callback) {
|
||||
var fn;
|
||||
var one;
|
||||
var invalid;
|
||||
@@ -78,7 +78,7 @@ Logger.prototype.prompt = function(prompts, callback) {
|
||||
}
|
||||
|
||||
// Validate prompt types
|
||||
invalid = prompts.some(function(prompt) {
|
||||
invalid = prompts.some(function (prompt) {
|
||||
return validPrompts.indexOf(prompt.type) === -1;
|
||||
});
|
||||
|
||||
@@ -88,20 +88,20 @@ Logger.prototype.prompt = function(prompts, callback) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
fn = function(answers) {
|
||||
fn = function (answers) {
|
||||
// Run callback only once
|
||||
if (runned) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Trim answers automatically
|
||||
Object.keys(answers).forEach(function(key) {
|
||||
Object.keys(answers).forEach(function (key) {
|
||||
var value = answers[key];
|
||||
|
||||
if (typeof value === 'string') {
|
||||
answers[key] = value.trim();
|
||||
} else if (Array.isArray(value)) {
|
||||
answers[key] = value.map(function(item) {
|
||||
answers[key] = value.map(function (item) {
|
||||
if (typeof item === 'string') {
|
||||
return item.trim();
|
||||
}
|
||||
@@ -124,20 +124,25 @@ Logger.prototype.prompt = function(prompts, callback) {
|
||||
|
||||
// ------------------
|
||||
|
||||
Logger._validPrompts = ['input', 'confirm', 'password', 'checkbox'];
|
||||
Logger._validPrompts = [
|
||||
'input',
|
||||
'confirm',
|
||||
'password',
|
||||
'checkbox'
|
||||
];
|
||||
|
||||
Logger.LEVELS = {
|
||||
error: 5,
|
||||
conflict: 4,
|
||||
warn: 3,
|
||||
action: 2,
|
||||
info: 1,
|
||||
debug: 0
|
||||
'error': 5,
|
||||
'conflict': 4,
|
||||
'warn': 3,
|
||||
'action': 2,
|
||||
'info': 1,
|
||||
'debug': 0
|
||||
};
|
||||
|
||||
// Add helpful log methods
|
||||
Object.keys(Logger.LEVELS).forEach(function(level) {
|
||||
Logger.prototype[level] = function(id, message, data) {
|
||||
Object.keys(Logger.LEVELS).forEach(function (level) {
|
||||
Logger.prototype[level] = function (id, message, data) {
|
||||
this.log(level, id, message, data);
|
||||
};
|
||||
});
|
||||
|
||||
@@ -9,19 +9,19 @@
|
||||
"url": "https://github.com/bower/logger/blob/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"repository": "https://github.com/bower/bower/tree/master/packages/bower-logger",
|
||||
"repository": "bower/logger",
|
||||
"main": "lib/Logger",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expect.js": "~0.2.0",
|
||||
"mocha": "^3.5.3"
|
||||
"mocha": "~1.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha -R spec"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
"lib"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,55 +2,57 @@ var expect = require('expect.js');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Logger = require('../');
|
||||
|
||||
describe('Logger', function() {
|
||||
describe('Logger', function () {
|
||||
var logger;
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function () {
|
||||
logger = new Logger();
|
||||
});
|
||||
|
||||
describe('.constructor', function() {
|
||||
it('should provide an instance of Logger', function() {
|
||||
describe('.constructor', function () {
|
||||
it('should provide an instance of Logger', function () {
|
||||
expect(logger instanceof Logger).to.be(true);
|
||||
});
|
||||
|
||||
it('should provide an instance of EventEmitter', function() {
|
||||
it('should provide an instance of EventEmitter', function () {
|
||||
expect(logger instanceof EventEmitter).to.be(true);
|
||||
});
|
||||
|
||||
it('should have prototype methods', function() {
|
||||
var methods = ['intercept', 'pipe', 'geminate', 'log'];
|
||||
it('should have prototype methods', function () {
|
||||
var methods = [
|
||||
'intercept', 'pipe', 'geminate', 'log'
|
||||
];
|
||||
|
||||
methods.forEach(function(method) {
|
||||
methods.forEach(function (method) {
|
||||
expect(logger).to.have.property(method);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('events', function() {
|
||||
describe('events', function () {
|
||||
var logData = {
|
||||
foo: 'bar',
|
||||
baz: 'string'
|
||||
};
|
||||
|
||||
it('should pass through {}', function(next) {
|
||||
logger.on('log', function(log) {
|
||||
it('should pass through {}', function (next) {
|
||||
logger.on('log', function (log) {
|
||||
expect(log.data).to.eql({});
|
||||
next();
|
||||
});
|
||||
logger.info();
|
||||
});
|
||||
|
||||
it('should pass through logData', function(next) {
|
||||
logger.on('log', function(log) {
|
||||
it('should pass through logData', function (next) {
|
||||
logger.on('log', function (log) {
|
||||
expect(log.data).to.eql(logData);
|
||||
next();
|
||||
});
|
||||
logger.info('foo', 'message', logData);
|
||||
});
|
||||
|
||||
it('should emit error event', function(next) {
|
||||
logger.on('log', function(log) {
|
||||
it('should emit error event', function (next) {
|
||||
logger.on('log', function (log) {
|
||||
expect(log.level).to.eql('error');
|
||||
expect(log.id).to.eql('foo');
|
||||
expect(log.message).to.eql('error message');
|
||||
@@ -60,8 +62,8 @@ describe('Logger', function() {
|
||||
logger.error('foo', 'error message');
|
||||
});
|
||||
|
||||
it('should emit conflict event', function(next) {
|
||||
logger.on('log', function(log) {
|
||||
it('should emit conflict event', function (next) {
|
||||
logger.on('log', function (log) {
|
||||
expect(log.level).to.eql('conflict');
|
||||
expect(log.id).to.eql('foo');
|
||||
expect(log.message).to.eql('conflict message');
|
||||
@@ -71,8 +73,8 @@ describe('Logger', function() {
|
||||
logger.conflict('foo', 'conflict message');
|
||||
});
|
||||
|
||||
it('should emit warn event', function(next) {
|
||||
logger.on('log', function(log) {
|
||||
it('should emit warn event', function (next) {
|
||||
logger.on('log', function (log) {
|
||||
expect(log.level).to.eql('warn');
|
||||
expect(log.id).to.eql('foo');
|
||||
expect(log.message).to.eql('warn message');
|
||||
@@ -82,8 +84,8 @@ describe('Logger', function() {
|
||||
logger.warn('foo', 'warn message');
|
||||
});
|
||||
|
||||
it('should emit action event', function(next) {
|
||||
logger.on('log', function(log) {
|
||||
it('should emit action event', function (next) {
|
||||
logger.on('log', function (log) {
|
||||
expect(log.level).to.eql('action');
|
||||
expect(log.id).to.eql('foo');
|
||||
expect(log.message).to.eql('action message');
|
||||
@@ -93,8 +95,8 @@ describe('Logger', function() {
|
||||
logger.action('foo', 'action message');
|
||||
});
|
||||
|
||||
it('should emit info event', function(next) {
|
||||
logger.on('log', function(log) {
|
||||
it('should emit info event', function (next) {
|
||||
logger.on('log', function (log) {
|
||||
expect(log.level).to.eql('info');
|
||||
expect(log.id).to.eql('foo');
|
||||
expect(log.message).to.eql('info message');
|
||||
@@ -104,8 +106,8 @@ describe('Logger', function() {
|
||||
logger.info('foo', 'info message');
|
||||
});
|
||||
|
||||
it('should emit debug event', function(next) {
|
||||
logger.on('log', function(log) {
|
||||
it('should emit debug event', function (next) {
|
||||
logger.on('log', function (log) {
|
||||
expect(log.level).to.eql('debug');
|
||||
expect(log.id).to.eql('foo');
|
||||
expect(log.message).to.eql('debug message');
|
||||
@@ -116,14 +118,14 @@ describe('Logger', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.intercept', function() {
|
||||
it('should add the function and call it when a log occurs', function(next) {
|
||||
describe('.intercept', function () {
|
||||
it('should add the function and call it when a log occurs', function (next) {
|
||||
var called;
|
||||
var data = {
|
||||
some: 'thing'
|
||||
'some': 'thing'
|
||||
};
|
||||
|
||||
logger.intercept(function(log) {
|
||||
logger.intercept(function (log) {
|
||||
called = true;
|
||||
|
||||
expect(log).to.eql({
|
||||
@@ -141,13 +143,13 @@ describe('Logger', function() {
|
||||
next();
|
||||
});
|
||||
|
||||
it('should call the interceptors by order before emitting the event', function(next) {
|
||||
it('should call the interceptors by order before emitting the event', function (next) {
|
||||
var called = [];
|
||||
|
||||
logger.intercept(function() {
|
||||
logger.intercept(function () {
|
||||
called.push(1);
|
||||
});
|
||||
logger.intercept(function() {
|
||||
logger.intercept(function () {
|
||||
called.push(2);
|
||||
});
|
||||
|
||||
@@ -157,21 +159,21 @@ describe('Logger', function() {
|
||||
next();
|
||||
});
|
||||
|
||||
it('should call the interceptors along the chain', function(next) {
|
||||
it('should call the interceptors along the chain', function (next) {
|
||||
var called = [];
|
||||
var childLogger = logger.geminate();
|
||||
|
||||
childLogger.intercept(function() {
|
||||
childLogger.intercept(function () {
|
||||
called.push(1);
|
||||
});
|
||||
logger.intercept(function() {
|
||||
logger.intercept(function () {
|
||||
called.push(3);
|
||||
});
|
||||
|
||||
childLogger.on('log', function() {
|
||||
childLogger.on('log', function () {
|
||||
called.push(2);
|
||||
});
|
||||
logger.on('log', function() {
|
||||
logger.on('log', function () {
|
||||
called.push(4);
|
||||
});
|
||||
|
||||
@@ -182,22 +184,22 @@ describe('Logger', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.pipe', function() {
|
||||
it('should return the passed emitter', function() {
|
||||
describe('.pipe', function () {
|
||||
it('should return the passed emitter', function () {
|
||||
var otherEmitter = new EventEmitter();
|
||||
expect(logger.pipe(otherEmitter)).to.equal(otherEmitter);
|
||||
});
|
||||
|
||||
it('should pipe log events to another emitter', function(next) {
|
||||
it('should pipe log events to another emitter', function (next) {
|
||||
var otherEmitter = new EventEmitter();
|
||||
var data = {
|
||||
some: 'thing'
|
||||
'some': 'thing'
|
||||
};
|
||||
var piped;
|
||||
|
||||
logger.pipe(otherEmitter);
|
||||
|
||||
otherEmitter.on('log', function(log) {
|
||||
otherEmitter.on('log', function (log) {
|
||||
piped = true;
|
||||
expect(log).to.eql({
|
||||
level: 'warn',
|
||||
@@ -214,8 +216,8 @@ describe('Logger', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.geminate', function() {
|
||||
it('should return a new logger instance', function() {
|
||||
describe('.geminate', function () {
|
||||
it('should return a new logger instance', function () {
|
||||
var newLogger = logger.geminate();
|
||||
|
||||
expect(newLogger).to.be.an(Logger);
|
||||
@@ -223,14 +225,14 @@ describe('Logger', function() {
|
||||
expect(newLogger).to.not.be.equal(logger);
|
||||
});
|
||||
|
||||
it('should pipe the new logger events to the original logger', function(next) {
|
||||
it('should pipe the new logger events to the original logger', function (next) {
|
||||
var piped = [];
|
||||
var childLogger = logger.geminate();
|
||||
var data = {
|
||||
some: 'thing'
|
||||
'some': 'thing'
|
||||
};
|
||||
|
||||
childLogger.on('log', function(log) {
|
||||
childLogger.on('log', function (log) {
|
||||
piped.push(1);
|
||||
expect(log).to.eql({
|
||||
level: 'warn',
|
||||
@@ -241,7 +243,7 @@ describe('Logger', function() {
|
||||
expect(log.data).to.equal(data);
|
||||
});
|
||||
|
||||
logger.on('log', function(log) {
|
||||
logger.on('log', function (log) {
|
||||
piped.push(2);
|
||||
expect(log).to.eql({
|
||||
level: 'warn',
|
||||
@@ -258,150 +260,130 @@ describe('Logger', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('.prompt', function() {
|
||||
it('should only allow calling the callback once', function() {
|
||||
describe('.prompt', function () {
|
||||
it('should only allow calling the callback once', function () {
|
||||
var calls = 0;
|
||||
|
||||
logger
|
||||
.once('prompt', function(prompts, callback) {
|
||||
callback({ prompt: 'bar' });
|
||||
callback({ prompt: 'foo' });
|
||||
})
|
||||
.prompt(
|
||||
{
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
},
|
||||
function() {
|
||||
calls += 1;
|
||||
}
|
||||
);
|
||||
.once('prompt', function (prompts, callback) {
|
||||
callback({ prompt: 'bar' });
|
||||
callback({ prompt: 'foo' });
|
||||
})
|
||||
.prompt({
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
}, function () {
|
||||
calls += 1;
|
||||
});
|
||||
|
||||
expect(calls).to.equal(1);
|
||||
});
|
||||
|
||||
it('should accept a prompt', function(next) {
|
||||
it('should accept a prompt', function (next) {
|
||||
logger
|
||||
.once('prompt', function(prompts, callback) {
|
||||
callback({
|
||||
prompt: 'bar'
|
||||
});
|
||||
})
|
||||
.prompt(
|
||||
{
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
},
|
||||
function(err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer).to.equal('bar');
|
||||
next();
|
||||
}
|
||||
);
|
||||
.once('prompt', function (prompts, callback) {
|
||||
callback({
|
||||
prompt: 'bar'
|
||||
});
|
||||
})
|
||||
.prompt({
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
}, function (err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer).to.equal('bar');
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept several prompts', function(next) {
|
||||
it('should accept several prompts', function (next) {
|
||||
logger
|
||||
.once('prompt', function(prompts, callback) {
|
||||
callback({
|
||||
foo: 'bar',
|
||||
foz: 'baz'
|
||||
});
|
||||
})
|
||||
.prompt(
|
||||
[
|
||||
{
|
||||
name: 'foo',
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
},
|
||||
{
|
||||
name: 'foz',
|
||||
type: 'confirm',
|
||||
message: 'foz'
|
||||
}
|
||||
],
|
||||
function(err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer.foo).to.equal('bar');
|
||||
expect(answer.foz).to.equal('baz');
|
||||
|
||||
logger
|
||||
.once('prompt', function(prompts, callback) {
|
||||
callback({
|
||||
foo: 'bar'
|
||||
});
|
||||
})
|
||||
.prompt(
|
||||
[
|
||||
{
|
||||
name: 'foo',
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
}
|
||||
],
|
||||
function(err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer.foo).to.equal('bar');
|
||||
next();
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should error on invalid prompt type', function(next) {
|
||||
logger.prompt(
|
||||
.once('prompt', function (prompts, callback) {
|
||||
callback({
|
||||
foo: 'bar',
|
||||
foz: 'baz'
|
||||
});
|
||||
})
|
||||
.prompt([
|
||||
{
|
||||
type: 'xxx',
|
||||
name: 'foo',
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
},
|
||||
function(err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.be('ENOTSUP');
|
||||
next();
|
||||
{
|
||||
name: 'foz',
|
||||
type: 'confirm',
|
||||
message: 'foz'
|
||||
}
|
||||
);
|
||||
});
|
||||
], function (err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer.foo).to.equal('bar');
|
||||
expect(answer.foz).to.equal('baz');
|
||||
|
||||
it('should trim the answers', function(next) {
|
||||
logger
|
||||
.once('prompt', function(prompts, callback) {
|
||||
logger
|
||||
.once('prompt', function (prompts, callback) {
|
||||
callback({
|
||||
prompt: ' bar '
|
||||
foo: 'bar'
|
||||
});
|
||||
})
|
||||
.prompt(
|
||||
.prompt([
|
||||
{
|
||||
name: 'foo',
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
},
|
||||
function(err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer).to.equal('bar');
|
||||
next();
|
||||
}
|
||||
);
|
||||
], function (err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer.foo).to.equal('bar');
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should trim multiple response answers', function(next) {
|
||||
logger
|
||||
.once('prompt', function(prompts, callback) {
|
||||
callback({
|
||||
prompt: [' bar ', ' foo', 'baz ']
|
||||
});
|
||||
})
|
||||
.prompt(
|
||||
{
|
||||
type: 'checkbox',
|
||||
message: 'foo'
|
||||
},
|
||||
function(err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer).to.eql(['bar', 'foo', 'baz']);
|
||||
next();
|
||||
}
|
||||
);
|
||||
it('should error on invalid prompt type', function (next) {
|
||||
logger.prompt({
|
||||
type: 'xxx',
|
||||
message: 'foo'
|
||||
}, function (err) {
|
||||
expect(err).to.be.an(Error);
|
||||
expect(err.code).to.be('ENOTSUP');
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
it('should trim the answers', function (next) {
|
||||
logger
|
||||
.once('prompt', function (prompts, callback) {
|
||||
callback({
|
||||
prompt: ' bar '
|
||||
});
|
||||
})
|
||||
.prompt({
|
||||
type: 'input',
|
||||
message: 'foo'
|
||||
}, function (err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer).to.equal('bar');
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
it('should trim multiple response answers', function (next) {
|
||||
logger
|
||||
.once('prompt', function (prompts, callback) {
|
||||
callback({
|
||||
prompt: [' bar ', ' foo', 'baz ']
|
||||
});
|
||||
})
|
||||
.prompt({
|
||||
type: 'checkbox',
|
||||
message: 'foo'
|
||||
}, function (err, answer) {
|
||||
expect(err).to.not.be.ok();
|
||||
expect(answer).to.eql(['bar', 'foo', 'baz']);
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
61
packages/bower-registry-client/.jshintrc
Normal file
61
packages/bower-registry-client/.jshintrc
Normal file
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"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": "vars",
|
||||
"quotmark": "single",
|
||||
"strict": false,
|
||||
"trailing": true,
|
||||
"camelcase": true,
|
||||
|
||||
"asi": false,
|
||||
"boss": true,
|
||||
"debug": false,
|
||||
"eqnull": true,
|
||||
"esnext": false,
|
||||
"evil": false,
|
||||
"expr": true,
|
||||
"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
|
||||
}
|
||||
6
packages/bower-registry-client/.travis.yml
Normal file
6
packages/bower-registry-client/.travis.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- 'iojs'
|
||||
- '0.12'
|
||||
- '0.10'
|
||||
@@ -7,16 +7,12 @@ function RegistryClient(config, logger) {
|
||||
this._config = config;
|
||||
|
||||
if (!this._config.registry) {
|
||||
throw new Error(
|
||||
'You need to pass config as read by bower-config module. Registry field is missing.'
|
||||
);
|
||||
throw new Error("You need to pass config as read by bower-config module. Registry field is missing.");
|
||||
}
|
||||
|
||||
// Cache defaults to storage registry
|
||||
if (!Object.prototype.hasOwnProperty.call(this._config, 'cache')) {
|
||||
this._config.cache = this._config.storage
|
||||
? this._config.storage.registry
|
||||
: null;
|
||||
this._config.cache = this._config.storage ? this._config.storage.registry : null;
|
||||
}
|
||||
|
||||
// Init the cache
|
||||
@@ -30,23 +26,20 @@ RegistryClient.prototype.list = methods.list;
|
||||
RegistryClient.prototype.register = methods.register;
|
||||
RegistryClient.prototype.unregister = methods.unregister;
|
||||
|
||||
RegistryClient.prototype.clearCache = function(name, callback) {
|
||||
RegistryClient.prototype.clearCache = function (name, callback) {
|
||||
if (typeof name === 'function') {
|
||||
callback = name;
|
||||
name = null;
|
||||
}
|
||||
|
||||
async.parallel(
|
||||
[
|
||||
this.lookup.clearCache.bind(this, name),
|
||||
this.search.clearCache.bind(this, name),
|
||||
this.list.clearCache.bind(this)
|
||||
],
|
||||
callback
|
||||
);
|
||||
async.parallel([
|
||||
this.lookup.clearCache.bind(this, name),
|
||||
this.search.clearCache.bind(this, name),
|
||||
this.list.clearCache.bind(this)
|
||||
], callback);
|
||||
};
|
||||
|
||||
RegistryClient.prototype.resetCache = function(name) {
|
||||
RegistryClient.prototype.resetCache = function (name) {
|
||||
this.lookup.resetCache.call(this, name);
|
||||
this.search.resetCache.call(this, name);
|
||||
this.list.resetCache.call(this);
|
||||
@@ -54,13 +47,13 @@ RegistryClient.prototype.resetCache = function(name) {
|
||||
return this;
|
||||
};
|
||||
|
||||
RegistryClient.clearRuntimeCache = function() {
|
||||
RegistryClient.clearRuntimeCache = function () {
|
||||
Cache.clearRuntimeCache();
|
||||
};
|
||||
|
||||
// -----------------------------
|
||||
|
||||
RegistryClient.prototype._initCache = function() {
|
||||
RegistryClient.prototype._initCache = function () {
|
||||
var cache;
|
||||
var dir = this._config.cache;
|
||||
|
||||
|
||||
47
packages/bower-registry-client/Gruntfile.js
Normal file
47
packages/bower-registry-client/Gruntfile.js
Normal file
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
module.exports = function (grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
grunt.loadNpmTasks('grunt-simple-mocha');
|
||||
|
||||
grunt.initConfig({
|
||||
jshint: {
|
||||
files: [
|
||||
'Gruntfile.js',
|
||||
'lib/**/*.js',
|
||||
'test/**/*.js'
|
||||
],
|
||||
options: {
|
||||
jshintrc: '.jshintrc'
|
||||
}
|
||||
},
|
||||
simplemocha: {
|
||||
options: {
|
||||
reporter: 'spec',
|
||||
timeout: 20000
|
||||
},
|
||||
full: {
|
||||
src: ['test/runner.js']
|
||||
},
|
||||
short: {
|
||||
options: {
|
||||
reporter: 'dot'
|
||||
},
|
||||
src: ['test/runner.js']
|
||||
},
|
||||
build: {
|
||||
options: {
|
||||
reporter: 'tap'
|
||||
},
|
||||
src: ['test/runner.js']
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: ['<%= jshint.files %>'],
|
||||
tasks: ['jshint', 'simplemocha:short']
|
||||
}
|
||||
});
|
||||
|
||||
grunt.registerTask('test', ['simplemocha:full']);
|
||||
grunt.registerTask('default', ['jshint', 'test']);
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
# bower-registry-client
|
||||
# bower-registry-client [](https://travis-ci.org/bower/registry-client)
|
||||
|
||||
> Provides easy interaction with the Bower registry
|
||||
|
||||
|
||||
@@ -20,63 +20,59 @@ function list(callback) {
|
||||
}
|
||||
|
||||
// List packages in series in each registry
|
||||
async.doUntil(
|
||||
function(next) {
|
||||
var remote = url.parse(registry[index]);
|
||||
var listCache = that._listCache[remote.host];
|
||||
async.doUntil(function (next) {
|
||||
var remote = url.parse(registry[index]);
|
||||
var listCache = that._listCache[remote.host];
|
||||
|
||||
// If offline flag is passed, only query the cache
|
||||
if (that._config.offline) {
|
||||
return listCache.get('list', function(err, results) {
|
||||
if (err || !results) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// Add each result
|
||||
results.forEach(function(result) {
|
||||
addResult.call(that, data, result);
|
||||
});
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise make a request to always obtain fresh data
|
||||
doRequest.call(that, index, function(err, results) {
|
||||
// If offline flag is passed, only query the cache
|
||||
if (that._config.offline) {
|
||||
return listCache.get('list', function (err, results) {
|
||||
if (err || !results) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// Add each result
|
||||
results.forEach(function(result) {
|
||||
addResult(data, result);
|
||||
results.forEach(function (result) {
|
||||
addResult.call(that, data, result);
|
||||
});
|
||||
|
||||
// Store in cache for future offline usage
|
||||
listCache.set('list', results, getMaxAge(), next);
|
||||
next();
|
||||
});
|
||||
},
|
||||
function() {
|
||||
// Until there's still registries to test
|
||||
return ++index === total;
|
||||
},
|
||||
function(err) {
|
||||
// Clear runtime cache, keeping the persistent data
|
||||
// in files for future offline usage
|
||||
resetCache();
|
||||
}
|
||||
|
||||
// If some of the registry entries failed, error out
|
||||
if (err) {
|
||||
return callback(err);
|
||||
// Otherwise make a request to always obtain fresh data
|
||||
doRequest.call(that, index, function (err, results) {
|
||||
if (err || !results) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
// Add each result
|
||||
results.forEach(function (result) {
|
||||
addResult(data, result);
|
||||
});
|
||||
|
||||
// Store in cache for future offline usage
|
||||
listCache.set('list', results, getMaxAge(), next);
|
||||
});
|
||||
}, function () {
|
||||
// Until there's still registries to test
|
||||
return ++index === total;
|
||||
}, function (err) {
|
||||
// Clear runtime cache, keeping the persistent data
|
||||
// in files for future offline usage
|
||||
resetCache();
|
||||
|
||||
// If some of the registry entries failed, error out
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
);
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
}
|
||||
|
||||
function addResult(accumulated, result) {
|
||||
var exists = accumulated.some(function(current) {
|
||||
var exists = accumulated.some(function (current) {
|
||||
return current.name === result.name;
|
||||
});
|
||||
|
||||
@@ -97,69 +93,35 @@ function doRequest(index, callback) {
|
||||
headers['User-Agent'] = this._config.userAgent;
|
||||
}
|
||||
|
||||
req = replay(
|
||||
request.get(
|
||||
requestUrl,
|
||||
{
|
||||
ca: this._config.ca.search[index],
|
||||
headers: headers,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
json: true
|
||||
},
|
||||
function(err, response, body) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(
|
||||
createError(
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed: ' +
|
||||
err.message,
|
||||
err.code
|
||||
)
|
||||
);
|
||||
}
|
||||
req = replay(request.get(requestUrl, {
|
||||
ca: this._config.ca.search[index],
|
||||
headers: headers,
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
json: true
|
||||
}, function (err, response, body) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(createError('Request to ' + requestUrl + ' failed: ' + err.message, err.code));
|
||||
}
|
||||
|
||||
// Abort if there was an error (range different than 2xx)
|
||||
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
return callback(
|
||||
createError(
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed with ' +
|
||||
response.statusCode,
|
||||
'EINVRES'
|
||||
)
|
||||
);
|
||||
}
|
||||
// Abort if there was an error (range different than 2xx)
|
||||
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
return callback(createError('Request to ' + requestUrl + ' failed with ' + response.statusCode, 'EINVRES'));
|
||||
}
|
||||
|
||||
// Validate response body, since we are expecting a JSON object
|
||||
// If the server returns an invalid JSON, it's still a string
|
||||
if (typeof body !== 'object') {
|
||||
return callback(
|
||||
createError(
|
||||
'Response of request to ' +
|
||||
requestUrl +
|
||||
' is not a valid json',
|
||||
'EINVRES'
|
||||
)
|
||||
);
|
||||
}
|
||||
// Validate response body, since we are expecting a JSON object
|
||||
// If the server returns an invalid JSON, it's still a string
|
||||
if (typeof body !== 'object') {
|
||||
return callback(createError('Response of request to ' + requestUrl + ' is not a valid json', 'EINVRES'));
|
||||
}
|
||||
|
||||
callback(null, body);
|
||||
}
|
||||
)
|
||||
);
|
||||
callback(null, body);
|
||||
}));
|
||||
|
||||
if (this._logger) {
|
||||
req.on('replay', function(replay) {
|
||||
msg =
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed with ' +
|
||||
replay.error.code +
|
||||
', ';
|
||||
req.on('replay', function (replay) {
|
||||
msg = 'Request to ' + requestUrl + ' failed with ' + replay.error.code + ', ';
|
||||
msg += 'retrying in ' + (replay.delay / 1000).toFixed(1) + 's';
|
||||
that._logger.warn('retry', msg);
|
||||
});
|
||||
@@ -175,7 +137,7 @@ function initCache() {
|
||||
this._listCache = this._cache.list || {};
|
||||
|
||||
// Generate a cache instance for each registry endpoint
|
||||
this._config.registry.search.forEach(function(registry) {
|
||||
this._config.registry.search.forEach(function (registry) {
|
||||
var cacheDir;
|
||||
var host = url.parse(registry).host;
|
||||
|
||||
@@ -185,11 +147,7 @@ function initCache() {
|
||||
}
|
||||
|
||||
if (this._config.cache) {
|
||||
cacheDir = path.join(
|
||||
this._config.cache,
|
||||
encodeURIComponent(host),
|
||||
'list'
|
||||
);
|
||||
cacheDir = path.join(this._config.cache, encodeURIComponent(host), 'list');
|
||||
}
|
||||
|
||||
this._listCache[host] = new Cache(cacheDir, {
|
||||
@@ -206,13 +164,9 @@ function clearCache(callback) {
|
||||
|
||||
// There's only one key, which is 'list'..
|
||||
// But we clear everything anyway
|
||||
async.forEach(
|
||||
remotes,
|
||||
function(remote, next) {
|
||||
listCache[remote].clear(next);
|
||||
},
|
||||
callback
|
||||
);
|
||||
async.forEach(remotes, function (remote, next) {
|
||||
listCache[remote].clear(next);
|
||||
}, callback);
|
||||
}
|
||||
|
||||
function resetCache() {
|
||||
|
||||
@@ -20,37 +20,22 @@ function lookup(name, callback) {
|
||||
|
||||
// Lookup package in series in each registry
|
||||
// endpoint until we got the data
|
||||
async.doUntil(
|
||||
function(next) {
|
||||
var remote = url.parse(registry[index]);
|
||||
var lookupCache = that._lookupCache[remote.host];
|
||||
async.doUntil(function (next) {
|
||||
var remote = url.parse(registry[index]);
|
||||
var lookupCache = that._lookupCache[remote.host];
|
||||
|
||||
// If force flag is disabled we check the cache
|
||||
if (!that._config.force) {
|
||||
lookupCache.get(name, function(err, value) {
|
||||
data = value;
|
||||
// If force flag is disabled we check the cache
|
||||
if (!that._config.force) {
|
||||
lookupCache.get(name, function (err, value) {
|
||||
data = value;
|
||||
|
||||
// Don't proceed with making a request if we got an error,
|
||||
// a value from the cache or if the offline flag is enabled
|
||||
if (err || data || that._config.offline) {
|
||||
return next(err);
|
||||
}
|
||||
// Don't proceed with making a request if we got an error,
|
||||
// a value from the cache or if the offline flag is enabled
|
||||
if (err || data || that._config.offline) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
doRequest.call(that, name, index, function(err, entry) {
|
||||
if (err || !entry) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
data = entry;
|
||||
|
||||
// Store in cache
|
||||
lookupCache.set(name, entry, getMaxAge(entry), next);
|
||||
});
|
||||
});
|
||||
// Otherwise, we totally bypass the cache and
|
||||
// make only the request
|
||||
} else {
|
||||
doRequest.call(that, name, index, function(err, entry) {
|
||||
doRequest.call(that, name, index, function (err, entry) {
|
||||
if (err || !entry) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -60,30 +45,38 @@ function lookup(name, callback) {
|
||||
// Store in cache
|
||||
lookupCache.set(name, entry, getMaxAge(entry), next);
|
||||
});
|
||||
}
|
||||
},
|
||||
function() {
|
||||
// Until the data is unknown or there's still registries to test
|
||||
return !!data || ++index === total;
|
||||
},
|
||||
function(err) {
|
||||
// If some of the registry entries failed, error out
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
});
|
||||
// Otherwise, we totally bypass the cache and
|
||||
// make only the request
|
||||
} else {
|
||||
doRequest.call(that, name, index, function (err, entry) {
|
||||
if (err || !entry) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
data = entry;
|
||||
|
||||
// Store in cache
|
||||
lookupCache.set(name, entry, getMaxAge(entry), next);
|
||||
});
|
||||
}
|
||||
);
|
||||
}, function () {
|
||||
// Until the data is unknown or there's still registries to test
|
||||
return !!data || ++index === total;
|
||||
}, function (err) {
|
||||
// If some of the registry entries failed, error out
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
}
|
||||
|
||||
function doRequest(name, index, callback) {
|
||||
var req;
|
||||
var msg;
|
||||
var requestUrl =
|
||||
this._config.registry.search[index] +
|
||||
'/packages/' +
|
||||
encodeURIComponent(name);
|
||||
var requestUrl = this._config.registry.search[index] + '/packages/' + encodeURIComponent(name);
|
||||
var remote = url.parse(requestUrl);
|
||||
var headers = {};
|
||||
var that = this;
|
||||
@@ -92,81 +85,47 @@ function doRequest(name, index, callback) {
|
||||
headers['User-Agent'] = this._config.userAgent;
|
||||
}
|
||||
|
||||
req = replay(
|
||||
request.get(
|
||||
requestUrl,
|
||||
{
|
||||
headers: headers,
|
||||
ca: this._config.ca.search[index],
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
json: true
|
||||
},
|
||||
function(err, response, body) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(
|
||||
createError(
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed: ' +
|
||||
err.message,
|
||||
err.code
|
||||
)
|
||||
);
|
||||
}
|
||||
req = replay(request.get(requestUrl, {
|
||||
headers: headers,
|
||||
ca: this._config.ca.search[index],
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
json: true
|
||||
}, function (err, response, body) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(createError('Request to ' + requestUrl + ' failed: ' + err.message, err.code));
|
||||
}
|
||||
|
||||
// If not found, try next
|
||||
if (response.statusCode === 404) {
|
||||
return callback();
|
||||
}
|
||||
// If not found, try next
|
||||
if (response.statusCode === 404) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// Abort if there was an error (range different than 2xx)
|
||||
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
return callback(
|
||||
createError(
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed with ' +
|
||||
response.statusCode,
|
||||
'EINVRES'
|
||||
)
|
||||
);
|
||||
}
|
||||
// Abort if there was an error (range different than 2xx)
|
||||
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
return callback(createError('Request to ' + requestUrl + ' failed with ' + response.statusCode, 'EINVRES'));
|
||||
}
|
||||
|
||||
// Validate response body, since we are expecting a JSON object
|
||||
// If the server returns an invalid JSON, it's still a string
|
||||
if (typeof body !== 'object') {
|
||||
return callback(
|
||||
createError(
|
||||
'Response of request to ' +
|
||||
requestUrl +
|
||||
' is not a valid json',
|
||||
'EINVRES'
|
||||
)
|
||||
);
|
||||
}
|
||||
// Validate response body, since we are expecting a JSON object
|
||||
// If the server returns an invalid JSON, it's still a string
|
||||
if (typeof body !== 'object') {
|
||||
return callback(createError('Response of request to ' + requestUrl + ' is not a valid json', 'EINVRES'));
|
||||
}
|
||||
|
||||
var data;
|
||||
if (body.url) {
|
||||
data = {
|
||||
type: 'alias',
|
||||
url: body.url
|
||||
};
|
||||
}
|
||||
callback(null, data);
|
||||
}
|
||||
)
|
||||
);
|
||||
var data;
|
||||
if (body.url) {
|
||||
data = {
|
||||
type: 'alias',
|
||||
url: body.url
|
||||
};
|
||||
}
|
||||
callback(null, data);
|
||||
}));
|
||||
|
||||
if (this._logger) {
|
||||
req.on('replay', function(replay) {
|
||||
msg =
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed with ' +
|
||||
replay.error.code +
|
||||
', ';
|
||||
req.on('replay', function (replay) {
|
||||
msg = 'Request to ' + requestUrl + ' failed with ' + replay.error.code + ', ';
|
||||
msg += 'retrying in ' + (replay.delay / 1000).toFixed(1) + 's';
|
||||
that._logger.warn('retry', msg);
|
||||
});
|
||||
@@ -187,7 +146,7 @@ function initCache() {
|
||||
this._lookupCache = this._cache.lookup || {};
|
||||
|
||||
// Generate a cache instance for each registry endpoint
|
||||
this._config.registry.search.forEach(function(registry) {
|
||||
this._config.registry.search.forEach(function (registry) {
|
||||
var cacheDir;
|
||||
var host = url.parse(registry).host;
|
||||
|
||||
@@ -197,11 +156,7 @@ function initCache() {
|
||||
}
|
||||
|
||||
if (this._config.cache) {
|
||||
cacheDir = path.join(
|
||||
this._config.cache,
|
||||
encodeURIComponent(host),
|
||||
'lookup'
|
||||
);
|
||||
cacheDir = path.join(this._config.cache, encodeURIComponent(host), 'lookup');
|
||||
}
|
||||
|
||||
this._lookupCache[host] = new Cache(cacheDir, {
|
||||
@@ -222,21 +177,13 @@ function clearCache(name, callback) {
|
||||
}
|
||||
|
||||
if (name) {
|
||||
async.forEach(
|
||||
remotes,
|
||||
function(remote, next) {
|
||||
lookupCache[remote].del(name, next);
|
||||
},
|
||||
callback
|
||||
);
|
||||
async.forEach(remotes, function (remote, next) {
|
||||
lookupCache[remote].del(name, next);
|
||||
}, callback);
|
||||
} else {
|
||||
async.forEach(
|
||||
remotes,
|
||||
function(remote, next) {
|
||||
lookupCache[remote].clear(next);
|
||||
},
|
||||
callback
|
||||
);
|
||||
async.forEach(remotes, function (remote, next) {
|
||||
lookupCache[remote].clear(next);
|
||||
}, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,61 +16,43 @@ function register(name, url, callback) {
|
||||
requestUrl += '?access_token=' + config.accessToken;
|
||||
}
|
||||
|
||||
request.post(
|
||||
{
|
||||
url: requestUrl,
|
||||
headers: headers,
|
||||
ca: config.ca.register,
|
||||
strictSSL: config.strictSsl,
|
||||
timeout: config.timeout,
|
||||
json: true,
|
||||
form: {
|
||||
name: name,
|
||||
url: url
|
||||
}
|
||||
},
|
||||
function(err, response) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(
|
||||
createError(
|
||||
'Request to ' + requestUrl + ' failed: ' + err.message,
|
||||
err.code
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Duplicate
|
||||
if (response.statusCode === 403) {
|
||||
return callback(createError('Duplicate package', 'EDUPLICATE'));
|
||||
}
|
||||
|
||||
// Invalid format
|
||||
if (response.statusCode === 400) {
|
||||
return callback(
|
||||
createError('Invalid URL format', 'EINVFORMAT')
|
||||
);
|
||||
}
|
||||
|
||||
// Everything other than 201 is unknown
|
||||
if (response.statusCode !== 201) {
|
||||
return callback(
|
||||
createError(
|
||||
'Unknown error: ' +
|
||||
response.statusCode +
|
||||
' - ' +
|
||||
response.body,
|
||||
'EUNKNOWN'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
name: name,
|
||||
url: url
|
||||
});
|
||||
request.post({
|
||||
url: requestUrl,
|
||||
headers: headers,
|
||||
ca: config.ca.register,
|
||||
strictSSL: config.strictSsl,
|
||||
timeout: config.timeout,
|
||||
json: true,
|
||||
form: {
|
||||
name: name,
|
||||
url: url
|
||||
}
|
||||
);
|
||||
}, function (err, response) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(createError('Request to ' + requestUrl + ' failed: ' + err.message, err.code));
|
||||
}
|
||||
|
||||
// Duplicate
|
||||
if (response.statusCode === 403) {
|
||||
return callback(createError('Duplicate package', 'EDUPLICATE'));
|
||||
}
|
||||
|
||||
// Invalid format
|
||||
if (response.statusCode === 400) {
|
||||
return callback(createError('Invalid URL format', 'EINVFORMAT'));
|
||||
}
|
||||
|
||||
// Everything other than 201 is unknown
|
||||
if (response.statusCode !== 201) {
|
||||
return callback(createError('Unknown error: ' + response.statusCode + ' - ' + response.body, 'EUNKNOWN'));
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
name: name,
|
||||
url: url
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = register;
|
||||
|
||||
@@ -28,63 +28,59 @@ function search(name, callback) {
|
||||
|
||||
// Search package in series in each registry,
|
||||
// merging results together
|
||||
async.doUntil(
|
||||
function(next) {
|
||||
var remote = url.parse(registry[index]);
|
||||
var searchCache = that._searchCache[remote.host];
|
||||
async.doUntil(function (next) {
|
||||
var remote = url.parse(registry[index]);
|
||||
var searchCache = that._searchCache[remote.host];
|
||||
|
||||
// If offline flag is passed, only query the cache
|
||||
if (that._config.offline) {
|
||||
return searchCache.get(name, function(err, results) {
|
||||
if (err || !results || !results.length) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// Add each result
|
||||
results.forEach(function(result) {
|
||||
addResult.call(that, data, result);
|
||||
});
|
||||
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise make a request to always obtain fresh data
|
||||
doRequest.call(that, name, index, function(err, results) {
|
||||
// If offline flag is passed, only query the cache
|
||||
if (that._config.offline) {
|
||||
return searchCache.get(name, function (err, results) {
|
||||
if (err || !results || !results.length) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
// Add each result
|
||||
results.forEach(function(result) {
|
||||
results.forEach(function (result) {
|
||||
addResult.call(that, data, result);
|
||||
});
|
||||
|
||||
// Store in cache for future offline usage
|
||||
searchCache.set(name, results, getMaxAge(), next);
|
||||
next();
|
||||
});
|
||||
},
|
||||
function() {
|
||||
// Until the data is unknown or there's still registries to test
|
||||
return ++index === total;
|
||||
},
|
||||
function(err) {
|
||||
// Clear runtime cache, keeping the persistent data
|
||||
// in files for future offline usage
|
||||
resetCache();
|
||||
}
|
||||
|
||||
// If some of the registry entries failed, error out
|
||||
if (err) {
|
||||
return callback(err);
|
||||
// Otherwise make a request to always obtain fresh data
|
||||
doRequest.call(that, name, index, function (err, results) {
|
||||
if (err || !results || !results.length) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
callback(null, data);
|
||||
// Add each result
|
||||
results.forEach(function (result) {
|
||||
addResult.call(that, data, result);
|
||||
});
|
||||
|
||||
// Store in cache for future offline usage
|
||||
searchCache.set(name, results, getMaxAge(), next);
|
||||
});
|
||||
}, function () {
|
||||
// Until the data is unknown or there's still registries to test
|
||||
return ++index === total;
|
||||
}, function (err) {
|
||||
// Clear runtime cache, keeping the persistent data
|
||||
// in files for future offline usage
|
||||
resetCache();
|
||||
|
||||
// If some of the registry entries failed, error out
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
);
|
||||
|
||||
callback(null, data);
|
||||
});
|
||||
}
|
||||
|
||||
function addResult(accumulated, result) {
|
||||
var exists = accumulated.some(function(current) {
|
||||
var exists = accumulated.some(function (current) {
|
||||
return current.name === result.name;
|
||||
});
|
||||
|
||||
@@ -96,10 +92,7 @@ function addResult(accumulated, result) {
|
||||
function doRequest(name, index, callback) {
|
||||
var req;
|
||||
var msg;
|
||||
var requestUrl =
|
||||
this._config.registry.search[index] +
|
||||
'/packages/search/' +
|
||||
encodeURIComponent(name);
|
||||
var requestUrl = this._config.registry.search[index] + '/packages/search/' + encodeURIComponent(name);
|
||||
var remote = url.parse(requestUrl);
|
||||
var headers = {};
|
||||
var that = this;
|
||||
@@ -108,69 +101,35 @@ function doRequest(name, index, callback) {
|
||||
headers['User-Agent'] = this._config.userAgent;
|
||||
}
|
||||
|
||||
req = replay(
|
||||
request.get(
|
||||
requestUrl,
|
||||
{
|
||||
headers: headers,
|
||||
ca: this._config.ca.search[index],
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
json: true
|
||||
},
|
||||
function(err, response, body) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(
|
||||
createError(
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed: ' +
|
||||
err.message,
|
||||
err.code
|
||||
)
|
||||
);
|
||||
}
|
||||
req = replay(request.get(requestUrl, {
|
||||
headers: headers,
|
||||
ca: this._config.ca.search[index],
|
||||
strictSSL: this._config.strictSsl,
|
||||
timeout: this._config.timeout,
|
||||
json: true
|
||||
}, function (err, response, body) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(createError('Request to ' + requestUrl + ' failed: ' + err.message, err.code));
|
||||
}
|
||||
|
||||
// Abort if there was an error (range different than 2xx)
|
||||
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
return callback(
|
||||
createError(
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed with ' +
|
||||
response.statusCode,
|
||||
'EINVRES'
|
||||
)
|
||||
);
|
||||
}
|
||||
// Abort if there was an error (range different than 2xx)
|
||||
if (response.statusCode < 200 || response.statusCode > 299) {
|
||||
return callback(createError('Request to ' + requestUrl + ' failed with ' + response.statusCode, 'EINVRES'));
|
||||
}
|
||||
|
||||
// Validate response body, since we are expecting a JSON object
|
||||
// If the server returns an invalid JSON, it's still a string
|
||||
if (typeof body !== 'object') {
|
||||
return callback(
|
||||
createError(
|
||||
'Response of request to ' +
|
||||
requestUrl +
|
||||
' is not a valid json',
|
||||
'EINVRES'
|
||||
)
|
||||
);
|
||||
}
|
||||
// Validate response body, since we are expecting a JSON object
|
||||
// If the server returns an invalid JSON, it's still a string
|
||||
if (typeof body !== 'object') {
|
||||
return callback(createError('Response of request to ' + requestUrl + ' is not a valid json', 'EINVRES'));
|
||||
}
|
||||
|
||||
callback(null, body);
|
||||
}
|
||||
)
|
||||
);
|
||||
callback(null, body);
|
||||
}));
|
||||
|
||||
if (this._logger) {
|
||||
req.on('replay', function(replay) {
|
||||
msg =
|
||||
'Request to ' +
|
||||
requestUrl +
|
||||
' failed with ' +
|
||||
replay.error.code +
|
||||
', ';
|
||||
req.on('replay', function (replay) {
|
||||
msg = 'Request to ' + requestUrl + ' failed with ' + replay.error.code + ', ';
|
||||
msg += 'retrying in ' + (replay.delay / 1000).toFixed(1) + 's';
|
||||
that._logger.warn('retry', msg);
|
||||
});
|
||||
@@ -186,7 +145,7 @@ function initCache() {
|
||||
this._searchCache = this._cache.search || {};
|
||||
|
||||
// Generate a cache instance for each registry endpoint
|
||||
this._config.registry.search.forEach(function(registry) {
|
||||
this._config.registry.search.forEach(function (registry) {
|
||||
var cacheDir;
|
||||
var host = url.parse(registry).host;
|
||||
|
||||
@@ -196,11 +155,7 @@ function initCache() {
|
||||
}
|
||||
|
||||
if (this._config.cache) {
|
||||
cacheDir = path.join(
|
||||
this._config.cache,
|
||||
encodeURIComponent(host),
|
||||
'search'
|
||||
);
|
||||
cacheDir = path.join(this._config.cache, encodeURIComponent(host), 'search');
|
||||
}
|
||||
|
||||
this._searchCache[host] = new Cache(cacheDir, {
|
||||
@@ -225,13 +180,9 @@ function clearCache(name, callback) {
|
||||
// One possible solution would be to read every entry from the cache and
|
||||
// delete if the package is contained in the search results
|
||||
// But this is too expensive
|
||||
async.forEach(
|
||||
remotes,
|
||||
function(remote, next) {
|
||||
searchCache[remote].clear(next);
|
||||
},
|
||||
callback
|
||||
);
|
||||
async.forEach(remotes, function (remote, next) {
|
||||
searchCache[remote].clear(next);
|
||||
}, callback);
|
||||
}
|
||||
|
||||
function resetCache() {
|
||||
|
||||
@@ -16,40 +16,32 @@ function unregister(name, callback) {
|
||||
requestUrl += '?access_token=' + config.accessToken;
|
||||
}
|
||||
|
||||
request.del(
|
||||
{
|
||||
url: requestUrl,
|
||||
headers: headers,
|
||||
ca: config.ca.register,
|
||||
strictSSL: config.strictSsl,
|
||||
timeout: config.timeout
|
||||
},
|
||||
function(err, response) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(
|
||||
createError(
|
||||
'Request to ' + requestUrl + ' failed: ' + err.message,
|
||||
err.code
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Forbidden
|
||||
if (response.statusCode === 403) {
|
||||
return callback(createError(response.body, 'EFORBIDDEN'));
|
||||
}
|
||||
|
||||
// Everything other than 204 is unknown
|
||||
if (response.statusCode !== 204) {
|
||||
return callback(createError(response.body, 'EUNKNOWN'));
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
name: name
|
||||
});
|
||||
request.del({
|
||||
url: requestUrl,
|
||||
headers: headers,
|
||||
ca: config.ca.register,
|
||||
strictSSL: config.strictSsl,
|
||||
timeout: config.timeout
|
||||
}, function (err, response) {
|
||||
// If there was an internal error (e.g. timeout)
|
||||
if (err) {
|
||||
return callback(createError('Request to ' + requestUrl + ' failed: ' + err.message, err.code));
|
||||
}
|
||||
);
|
||||
|
||||
// Forbidden
|
||||
if (response.statusCode === 403) {
|
||||
return callback(createError(response.body, 'EFORBIDDEN'));
|
||||
}
|
||||
|
||||
// Everything other than 204 is unknown
|
||||
if (response.statusCode !== 204) {
|
||||
return callback(createError(response.body, 'EUNKNOWN'));
|
||||
}
|
||||
|
||||
callback(null, {
|
||||
name: name
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = unregister;
|
||||
|
||||
@@ -22,7 +22,7 @@ function Cache(dir, options) {
|
||||
}
|
||||
}
|
||||
|
||||
Cache.prototype.get = function(key, callback) {
|
||||
Cache.prototype.get = function (key, callback) {
|
||||
var file;
|
||||
var json = this._cache.get(key);
|
||||
|
||||
@@ -43,38 +43,35 @@ Cache.prototype.get = function(key, callback) {
|
||||
}
|
||||
|
||||
file = this._getFile(key);
|
||||
fs.readFile(
|
||||
file,
|
||||
function(err, contents) {
|
||||
var json;
|
||||
fs.readFile(file, function (err, contents) {
|
||||
var json;
|
||||
|
||||
// Check if there was an error reading
|
||||
// Note that if the file does not exist then
|
||||
// we don't have its value
|
||||
if (err) {
|
||||
return callback(err.code === 'ENOENT' ? null : err);
|
||||
}
|
||||
// Check if there was an error reading
|
||||
// Note that if the file does not exist then
|
||||
// we don't have its value
|
||||
if (err) {
|
||||
return callback(err.code === 'ENOENT' ? null : err);
|
||||
}
|
||||
|
||||
// If there was an error reading the file as json
|
||||
// simply assume it doesn't exist
|
||||
try {
|
||||
json = JSON.parse(contents.toString());
|
||||
} catch (e) {
|
||||
return this.del(key, callback); // If so, delete it
|
||||
}
|
||||
// If there was an error reading the file as json
|
||||
// simply assume it doesn't exist
|
||||
try {
|
||||
json = JSON.parse(contents.toString());
|
||||
} catch (e) {
|
||||
return this.del(key, callback); // If so, delete it
|
||||
}
|
||||
|
||||
// Check if it has expired
|
||||
if (this._hasExpired(json)) {
|
||||
return this.del(key, callback);
|
||||
}
|
||||
// Check if it has expired
|
||||
if (this._hasExpired(json)) {
|
||||
return this.del(key, callback);
|
||||
}
|
||||
|
||||
this._cache.set(key, json);
|
||||
callback(null, json.value);
|
||||
}.bind(this)
|
||||
);
|
||||
this._cache.set(key, json);
|
||||
callback(null, json.value);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
Cache.prototype.set = function(key, value, maxAge, callback) {
|
||||
Cache.prototype.set = function (key, value, maxAge, callback) {
|
||||
var file;
|
||||
var entry;
|
||||
var str;
|
||||
@@ -105,7 +102,7 @@ Cache.prototype.set = function(key, value, maxAge, callback) {
|
||||
fs.writeFile(file, str, callback);
|
||||
};
|
||||
|
||||
Cache.prototype.del = function(key, callback) {
|
||||
Cache.prototype.del = function (key, callback) {
|
||||
// Delete from memory
|
||||
this._cache.del(key);
|
||||
|
||||
@@ -114,7 +111,7 @@ Cache.prototype.del = function(key, callback) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
fs.unlink(this._getFile(key), function(err) {
|
||||
fs.unlink(this._getFile(key), function (err) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -123,7 +120,7 @@ Cache.prototype.del = function(key, callback) {
|
||||
});
|
||||
};
|
||||
|
||||
Cache.prototype.clear = function(callback) {
|
||||
Cache.prototype.clear = function (callback) {
|
||||
var dir = this._dir;
|
||||
|
||||
// Clear in memory cache
|
||||
@@ -134,39 +131,35 @@ Cache.prototype.clear = function(callback) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
fs.readdir(dir, function(err, files) {
|
||||
fs.readdir(dir, function (err, files) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Delete every file in parallel
|
||||
async.forEach(
|
||||
files,
|
||||
function(file, next) {
|
||||
fs.unlink(path.join(dir, file), function(err) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
return next(err);
|
||||
}
|
||||
async.forEach(files, function (file, next) {
|
||||
fs.unlink(path.join(dir, file), function (err) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
},
|
||||
callback
|
||||
);
|
||||
next();
|
||||
});
|
||||
}, callback);
|
||||
});
|
||||
};
|
||||
|
||||
Cache.prototype.reset = function() {
|
||||
Cache.prototype.reset = function () {
|
||||
this._cache.reset();
|
||||
};
|
||||
|
||||
Cache.clearRuntimeCache = function() {
|
||||
Cache.clearRuntimeCache = function () {
|
||||
// Note that _cache refers to the static _cache variable
|
||||
// that holds other caches per dir!
|
||||
// Do not confuse it with the instance cache
|
||||
|
||||
// Clear cache of each directory
|
||||
this._cache.forEach(function(cache) {
|
||||
this._cache.forEach(function (cache) {
|
||||
cache.reset();
|
||||
});
|
||||
|
||||
@@ -176,7 +169,7 @@ Cache.clearRuntimeCache = function() {
|
||||
|
||||
//-------------------------------
|
||||
|
||||
Cache.prototype._hasExpired = function(json) {
|
||||
Cache.prototype._hasExpired = function (json) {
|
||||
var expires = json.expires;
|
||||
|
||||
if (!expires || this._options.useStale) {
|
||||
@@ -187,19 +180,16 @@ Cache.prototype._hasExpired = function(json) {
|
||||
return Date.now() > expires;
|
||||
};
|
||||
|
||||
Cache.prototype._getFile = function(key) {
|
||||
Cache.prototype._getFile = function (key) {
|
||||
// Append a truncated md5 to the end of the file to solve case issues
|
||||
// on case insensitive file systems
|
||||
// See: https://github.com/bower/bower/issues/859
|
||||
return path.join(
|
||||
this._dir,
|
||||
encodeURIComponent(key) + '_' + md5(key).substr(0, 5)
|
||||
);
|
||||
return path.join(this._dir, encodeURIComponent(key) + '_' + md5(key).substr(0, 5));
|
||||
};
|
||||
|
||||
Cache._cache = new LRU({
|
||||
max: 5,
|
||||
maxAge: 60 * 30 * 1000 // 30 minutes
|
||||
maxAge: 60 * 30 * 1000 // 30 minutes
|
||||
});
|
||||
|
||||
module.exports = Cache;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
var crypto = require('crypto');
|
||||
|
||||
function md5(contents) {
|
||||
return crypto
|
||||
.createHash('md5')
|
||||
.update(contents)
|
||||
.digest('hex');
|
||||
return crypto.createHash('md5').update(contents).digest('hex');
|
||||
}
|
||||
|
||||
module.exports = md5;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"description": "Provides easy interaction with the Bower registry",
|
||||
"author": "Twitter",
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/bower/bower/tree/master/packages/bower-registry-client",
|
||||
"repository": "bower/registry-client",
|
||||
"main": "Client",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -21,11 +21,16 @@
|
||||
"devDependencies": {
|
||||
"bower-config": "^1.1.2",
|
||||
"expect.js": "~0.2.0",
|
||||
"mocha": "^3.5.3",
|
||||
"nock": "9.2.3"
|
||||
"grunt": "~0.4.1",
|
||||
"grunt-cli": "^0.1.13",
|
||||
"grunt-contrib-jshint": "~0.6.0",
|
||||
"grunt-contrib-watch": "~0.5.0",
|
||||
"grunt-simple-mocha": "~0.4.0",
|
||||
"mocha": "~1.12.0",
|
||||
"nock": "~0.22.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
"test": "grunt test"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,21 @@
|
||||
var index = require('../../lib/index');
|
||||
var expect = require('expect.js');
|
||||
|
||||
describe('index module', function() {
|
||||
describe('requiring the index module', function() {
|
||||
it('should expose a lookup method', function() {
|
||||
describe('index module', function () {
|
||||
describe('requiring the index module', function () {
|
||||
it('should expose a lookup method', function () {
|
||||
expect(index.lookup).to.be.ok;
|
||||
});
|
||||
|
||||
it('should expose a list method', function() {
|
||||
it('should expose a list method', function () {
|
||||
expect(index.list).to.be.ok;
|
||||
});
|
||||
|
||||
it('should expose a register method', function() {
|
||||
it('should expose a register method', function () {
|
||||
expect(index.register).to.be.ok;
|
||||
});
|
||||
|
||||
it('should expose a search method', function() {
|
||||
it('should expose a search method', function () {
|
||||
expect(index.search).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
var list = require('../../lib/list');
|
||||
var expect = require('expect.js');
|
||||
|
||||
describe('list module', function() {
|
||||
describe('requiring the list module', function() {
|
||||
it('should expose a list method', function() {
|
||||
describe('list module', function () {
|
||||
describe('requiring the list module', function () {
|
||||
it('should expose a list method', function () {
|
||||
expect(typeof list === 'function').to.be.ok;
|
||||
});
|
||||
|
||||
it('should expose a initCache method', function() {
|
||||
it('should expose a initCache method', function () {
|
||||
expect(list.initCache).to.be.ok;
|
||||
expect(typeof list.initCache === 'function').to.be.ok;
|
||||
});
|
||||
|
||||
it('should expose a clearCache method', function() {
|
||||
it('should expose a clearCache method', function () {
|
||||
expect(list.clearCache).to.be.ok;
|
||||
expect(typeof list.clearCache === 'function').to.be.ok;
|
||||
});
|
||||
|
||||
it('should expose a resetCache method', function() {
|
||||
it('should expose a resetCache method', function () {
|
||||
expect(list.resetCache).to.be.ok;
|
||||
expect(typeof list.resetCache === 'function').to.be.ok;
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user